Skip to content

Commit d2907ea

Browse files
author
sbene
committed
feat: Enable caching tokens for multiple vault namespaces
Signed-off-by: sbene <[email protected]>
1 parent b046a7d commit d2907ea

12 files changed

+202
-38
lines changed

Diff for: cmd/generate_test.go

+84
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,90 @@ func TestMain(t *testing.T) {
300300
}
301301
})
302302

303+
t.Run("will create cache per namespace when provided", func(t *testing.T) {
304+
305+
// Purging token cache before launching this test
306+
err := utils.PurgeTokenCache()
307+
if err != nil {
308+
t.Fatalf("fail to purge tocken cache: %s", err.Error())
309+
}
310+
311+
args := []string{"../fixtures/input/nonempty"}
312+
cmd := NewGenerateCommand()
313+
314+
b := bytes.NewBufferString("")
315+
e := bytes.NewBufferString("")
316+
cmd.SetArgs(args)
317+
cmd.SetOut(b)
318+
cmd.SetErr(e)
319+
cmd.Execute()
320+
out, err := io.ReadAll(b) // Read buffer to bytes
321+
if err != nil {
322+
t.Fatal(err)
323+
}
324+
stderr, err := io.ReadAll(e) // Read buffer to bytes
325+
if err != nil {
326+
t.Fatal(err)
327+
}
328+
329+
buf, err := os.ReadFile("../fixtures/output/all.yaml")
330+
if err != nil {
331+
t.Fatal(err)
332+
}
333+
334+
// We first check that the command was successful to make sure it reached the token caching part
335+
expected := string(buf)
336+
if string(out) != expected {
337+
t.Fatalf("expected %s\n\nbut got\n\n%s\nerr: %s", expected, string(out), string(stderr))
338+
}
339+
340+
// Default namespace cache is expected
341+
_, err = utils.ReadExistingToken(fmt.Sprintf("approle_default_%s", roleid))
342+
if err != nil {
343+
t.Fatalf("expected cached vault token but got: %s", err)
344+
}
345+
346+
// Setting the vault namespace environment variable
347+
os.Setenv("VAULT_NAMESPACE", `test-namespace`)
348+
349+
nsArgs := []string{"../fixtures/input/nonempty"}
350+
nsCmd := NewGenerateCommand()
351+
352+
nsB := bytes.NewBufferString("")
353+
nsE := bytes.NewBufferString("")
354+
nsCmd.SetArgs(nsArgs)
355+
nsCmd.SetOut(nsB)
356+
nsCmd.SetErr(nsE)
357+
nsCmd.Execute()
358+
nsOut, nsErr := io.ReadAll(nsB) // Read buffer to bytes
359+
if nsErr != nil {
360+
t.Fatal(nsErr)
361+
}
362+
nsStderr, nsErr := io.ReadAll(nsE) // Read buffer to bytes
363+
if nsErr != nil {
364+
t.Fatal(nsErr)
365+
}
366+
367+
nsBuf, nsErr := os.ReadFile("../fixtures/output/all.yaml")
368+
if nsErr != nil {
369+
t.Fatal(nsErr)
370+
}
371+
372+
// We first check that the command was successful to make sure it reached the token caching part
373+
nsExpected := string(nsBuf)
374+
if string(nsOut) != nsExpected {
375+
t.Fatalf("expected %s\n\nbut got\n\n%s\nerr: %s", nsExpected, string(nsOut), string(nsStderr))
376+
}
377+
378+
// Namespaced cache is expected
379+
_, nsErr = utils.ReadExistingToken(fmt.Sprintf("approle_test-namespace_%s", roleid))
380+
if nsErr != nil {
381+
t.Fatalf("expected cached vault token but got: %s", nsErr)
382+
}
383+
384+
os.Unsetenv("VAULT_NAMESPACE")
385+
})
386+
303387
os.Unsetenv("AVP_TYPE")
304388
os.Unsetenv("VAULT_ADDR")
305389
os.Unsetenv("AVP_AUTH_TYPE")

Diff for: pkg/auth/vault/approle.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ type AppRoleAuth struct {
1515
RoleID string
1616
SecretID string
1717
MountPath string
18+
Namespace string
1819
}
1920

2021
// NewAppRoleAuth initalizes a new AppRolAuth with role id and secret id
21-
func NewAppRoleAuth(roleID, secretID, mountPath string) *AppRoleAuth {
22+
func NewAppRoleAuth(roleID, secretID, mountPath string, namespace string) *AppRoleAuth {
2223
appRoleAuth := &AppRoleAuth{
2324
RoleID: roleID,
2425
SecretID: secretID,
2526
MountPath: approleMountPath,
27+
Namespace: namespace,
2628
}
2729
if mountPath != "" {
2830
appRoleAuth.MountPath = mountPath
@@ -33,7 +35,7 @@ func NewAppRoleAuth(roleID, secretID, mountPath string) *AppRoleAuth {
3335

3436
// Authenticate authenticates with Vault using App Role and returns a token
3537
func (a *AppRoleAuth) Authenticate(vaultClient *api.Client) error {
36-
err := utils.LoginWithCachedToken(vaultClient, fmt.Sprintf("approle_%s", a.RoleID))
38+
err := utils.LoginWithCachedToken(vaultClient, fmt.Sprintf("approle_%s_%s", a.Namespace, a.RoleID))
3739
if err != nil {
3840
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", err)
3941
} else {
@@ -54,7 +56,7 @@ func (a *AppRoleAuth) Authenticate(vaultClient *api.Client) error {
5456
utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data)
5557

5658
// If we cannot write the Vault token, we'll just have to login next time. Nothing showstopping.
57-
err = utils.SetToken(vaultClient, fmt.Sprintf("approle_%s", a.RoleID), data.Auth.ClientToken)
59+
err = utils.SetToken(vaultClient, fmt.Sprintf("approle_%s_%s", a.Namespace, a.RoleID), data.Auth.ClientToken)
5860
if err != nil {
5961
utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err)
6062
}

Diff for: pkg/auth/vault/approle_test.go

+21-5
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ func TestAppRoleLogin(t *testing.T) {
1414
cluster, roleID, secretID := helpers.CreateTestAppRoleVault(t)
1515
defer cluster.Cleanup()
1616

17-
appRole := vault.NewAppRoleAuth(roleID, secretID, "")
17+
appRole := vault.NewAppRoleAuth(roleID, secretID, "", "default")
1818

1919
err := appRole.Authenticate(cluster.Cores[0].Client)
2020
if err != nil {
2121
t.Fatalf("expected no errors but got: %s", err)
2222
}
2323

24-
cachedToken, err := utils.ReadExistingToken(fmt.Sprintf("approle_%s", roleID))
24+
cachedToken, err := utils.ReadExistingToken(fmt.Sprintf("approle_default_%s", roleID))
2525
if err != nil {
2626
t.Fatalf("expected cached vault token but got: %s", err)
2727
}
@@ -31,7 +31,7 @@ func TestAppRoleLogin(t *testing.T) {
3131
t.Fatalf("expected no errors but got: %s", err)
3232
}
3333

34-
newCachedToken, err := utils.ReadExistingToken(fmt.Sprintf("approle_%s", roleID))
34+
newCachedToken, err := utils.ReadExistingToken(fmt.Sprintf("approle_default_%s", roleID))
3535
if err != nil {
3636
t.Fatalf("expected cached vault token but got: %s", err)
3737
}
@@ -44,14 +44,14 @@ func TestAppRoleLogin(t *testing.T) {
4444
secondCluster, secondRoleID, secondSecretID := helpers.CreateTestAppRoleVault(t)
4545
defer secondCluster.Cleanup()
4646

47-
secondAppRole := vault.NewAppRoleAuth(secondRoleID, secondSecretID, "")
47+
secondAppRole := vault.NewAppRoleAuth(secondRoleID, secondSecretID, "", "default")
4848

4949
err = secondAppRole.Authenticate(secondCluster.Cores[0].Client)
5050
if err != nil {
5151
t.Fatalf("expected no errors but got: %s", err)
5252
}
5353

54-
secondCachedToken, err := utils.ReadExistingToken(fmt.Sprintf("approle_%s", secondRoleID))
54+
secondCachedToken, err := utils.ReadExistingToken(fmt.Sprintf("approle_default_%s", secondRoleID))
5555
if err != nil {
5656
t.Fatalf("expected cached vault token but got: %s", err)
5757
}
@@ -60,4 +60,20 @@ func TestAppRoleLogin(t *testing.T) {
6060
if bytes.Compare(cachedToken, secondCachedToken) == 0 {
6161
t.Fatalf("expected different tokens but got %s", secondCachedToken)
6262
}
63+
64+
// We create a new connection to a specific namespace and create a different cache
65+
namespaceCluster, namespaceRoleID, namespaceSecretID := helpers.CreateTestAppRoleVault(t)
66+
defer namespaceCluster.Cleanup()
67+
68+
namespaceAppRole := vault.NewAppRoleAuth(namespaceRoleID, namespaceSecretID, "", "my-other-namespace")
69+
70+
err = namespaceAppRole.Authenticate(namespaceCluster.Cores[0].Client)
71+
if err != nil {
72+
t.Fatalf("expected no errors but got: %s", err)
73+
}
74+
75+
_, err = utils.ReadExistingToken(fmt.Sprintf("approle_my-other-namespace_%s", namespaceRoleID))
76+
if err != nil {
77+
t.Fatalf("expected cached vault token but got: %s", err)
78+
}
6379
}

Diff for: pkg/auth/vault/github.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ const (
1515
type GithubAuth struct {
1616
AccessToken string
1717
MountPath string
18+
Namespace string
1819
}
1920

2021
// NewGithubAuth initializes a new GithubAuth with token
21-
func NewGithubAuth(token, mountPath string) *GithubAuth {
22+
func NewGithubAuth(token, mountPath string, namespace string) *GithubAuth {
2223
githubAuth := &GithubAuth{
2324
AccessToken: token,
2425
MountPath: githubMountPath,
26+
Namespace: namespace,
2527
}
2628
if mountPath != "" {
2729
githubAuth.MountPath = mountPath
@@ -32,7 +34,7 @@ func NewGithubAuth(token, mountPath string) *GithubAuth {
3234

3335
// Authenticate authenticates with Vault and returns a token
3436
func (g *GithubAuth) Authenticate(vaultClient *api.Client) error {
35-
err := utils.LoginWithCachedToken(vaultClient, "github")
37+
err := utils.LoginWithCachedToken(vaultClient, fmt.Sprintf("github_%s", g.Namespace))
3638
if err != nil {
3739
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", err)
3840
} else {
@@ -52,7 +54,7 @@ func (g *GithubAuth) Authenticate(vaultClient *api.Client) error {
5254
utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data)
5355

5456
// If we cannot write the Vault token, we'll just have to login next time. Nothing showstopping.
55-
err = utils.SetToken(vaultClient, "github", data.Auth.ClientToken)
57+
err = utils.SetToken(vaultClient, fmt.Sprintf("github_%s", g.Namespace), data.Auth.ClientToken)
5658
if err != nil {
5759
utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err)
5860
}

Diff for: pkg/auth/vault/github_test.go

+19-3
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ func TestGithubLogin(t *testing.T) {
1414
cluster := helpers.CreateTestAuthVault(t)
1515
defer cluster.Cleanup()
1616

17-
github := vault.NewGithubAuth("123", "")
17+
github := vault.NewGithubAuth("123", "", "default")
1818

1919
err := github.Authenticate(cluster.Cores[0].Client)
2020
if err != nil {
2121
t.Fatalf("expected no errors but got: %s", err)
2222
}
2323

24-
cachedToken, err := utils.ReadExistingToken("github")
24+
cachedToken, err := utils.ReadExistingToken("github_default")
2525
if err != nil {
2626
t.Fatalf("expected cached vault token but got: %s", err)
2727
}
@@ -31,12 +31,28 @@ func TestGithubLogin(t *testing.T) {
3131
t.Fatalf("expected no errors but got: %s", err)
3232
}
3333

34-
newCachedToken, err := utils.ReadExistingToken("github")
34+
newCachedToken, err := utils.ReadExistingToken("github_default")
3535
if err != nil {
3636
t.Fatalf("expected cached vault token but got: %s", err)
3737
}
3838

3939
if bytes.Compare(cachedToken, newCachedToken) != 0 {
4040
t.Fatalf("expected same token %s but got %s", cachedToken, newCachedToken)
4141
}
42+
43+
// We create a new connection to a specific namespace and create a different cache
44+
namespaceCluster := helpers.CreateTestAuthVault(t)
45+
defer namespaceCluster.Cleanup()
46+
47+
namespaceGithub := vault.NewGithubAuth("123", "", "my-other-namespace")
48+
49+
err = namespaceGithub.Authenticate(namespaceCluster.Cores[0].Client)
50+
if err != nil {
51+
t.Fatalf("expected no errors but got: %s", err)
52+
}
53+
54+
_, err = utils.ReadExistingToken("github_my-other-namespace")
55+
if err != nil {
56+
t.Fatalf("expected cached vault token but got: %s", err)
57+
}
4258
}

Diff for: pkg/auth/vault/kubernetes.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,30 @@ const (
1717

1818
// K8sAuth TODO
1919
type K8sAuth struct {
20-
// Optional, will use default path of auth/kubernetes if left blank
21-
MountPath string
22-
20+
Role string
2321
// Optional, will use default service account if left blank
2422
TokenPath string
25-
26-
Role string
23+
// Optional, will use default path of auth/kubernetes if left blank
24+
MountPath string
25+
// Optional, will use "default" if not provided
26+
Namespace string
2727
}
2828

2929
// NewK8sAuth initializes and returns a K8sAuth Struct
30-
func NewK8sAuth(role, mountPath, tokenPath string) *K8sAuth {
30+
func NewK8sAuth(role, mountPath, tokenPath string, namespace string) *K8sAuth {
3131
k8sAuth := &K8sAuth{
3232
Role: role,
3333
MountPath: mountPath,
3434
TokenPath: tokenPath,
35+
Namespace: namespace,
3536
}
3637

3738
return k8sAuth
3839
}
3940

4041
// Authenticate authenticates with Vault via K8s and returns a token
4142
func (k *K8sAuth) Authenticate(vaultClient *api.Client) error {
42-
err := utils.LoginWithCachedToken(vaultClient, "kubernetes")
43+
err := utils.LoginWithCachedToken(vaultClient, fmt.Sprintf("kubernetes_%s", k.Namespace))
4344
if err != nil {
4445
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", err)
4546
} else {
@@ -70,7 +71,7 @@ func (k *K8sAuth) Authenticate(vaultClient *api.Client) error {
7071
utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data)
7172

7273
// If we cannot write the Vault token, we'll just have to login next time. Nothing showstopping.
73-
err = utils.SetToken(vaultClient, "kubernetes", data.Auth.ClientToken)
74+
err = utils.SetToken(vaultClient, fmt.Sprintf("kubernetes_%s", k.Namespace), data.Auth.ClientToken)
7475
if err != nil {
7576
utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err)
7677
}

Diff for: pkg/auth/vault/kubernetes_test.go

+19-3
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ func TestKubernetesAuth(t *testing.T) {
4646
t.Fatalf("error writing token: %s", err)
4747
}
4848

49-
k8s := vault.NewK8sAuth("role", "", string(filepath.Join(saPath, "token")))
49+
k8s := vault.NewK8sAuth("role", "", string(filepath.Join(saPath, "token")), "default")
5050

5151
err = k8s.Authenticate(cluster.Cores[0].Client)
5252
if err != nil {
5353
t.Fatalf("expected no errors but got: %s", err)
5454
}
5555

56-
cachedToken, err := utils.ReadExistingToken("kubernetes")
56+
cachedToken, err := utils.ReadExistingToken("kubernetes_default")
5757
if err != nil {
5858
t.Fatalf("expected cached vault token but got: %s", err)
5959
}
@@ -63,7 +63,7 @@ func TestKubernetesAuth(t *testing.T) {
6363
t.Fatalf("expected no errors but got: %s", err)
6464
}
6565

66-
newCachedToken, err := utils.ReadExistingToken("kubernetes")
66+
newCachedToken, err := utils.ReadExistingToken("kubernetes_default")
6767
if err != nil {
6868
t.Fatalf("expected cached vault token but got: %s", err)
6969
}
@@ -72,6 +72,22 @@ func TestKubernetesAuth(t *testing.T) {
7272
t.Fatalf("expected same token %s but got %s", cachedToken, newCachedToken)
7373
}
7474

75+
// We create a new connection to a specific namespace and create a different cache
76+
namespaceCluster := helpers.CreateTestAuthVault(t)
77+
defer namespaceCluster.Cleanup()
78+
79+
namespaceK8s := vault.NewK8sAuth("role", "", string(filepath.Join(saPath, "token")), "my-other-namespace")
80+
81+
err = namespaceK8s.Authenticate(namespaceCluster.Cores[0].Client)
82+
if err != nil {
83+
t.Fatalf("expected no errors but got: %s", err)
84+
}
85+
86+
_, err = utils.ReadExistingToken("kubernetes_my-other-namespace")
87+
if err != nil {
88+
t.Fatalf("expected cached vault token but got: %s", err)
89+
}
90+
7591
err = removeK8sToken()
7692
if err != nil {
7793
fmt.Println(err)

0 commit comments

Comments
 (0)