Skip to content

Commit

Permalink
feat: Enable multiple token caching and add option to disable caching…
Browse files Browse the repository at this point in the history
… completely

Signed-off-by: sbene <[email protected]>
  • Loading branch information
sbene authored and werne2j committed Sep 29, 2024
1 parent 3e41df8 commit 631bbc3
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 67 deletions.
3 changes: 3 additions & 0 deletions cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func NewGenerateCommand() *cobra.Command {
const StdIn = "-"
var configPath, secretName string
var verboseOutput bool
var disableCache bool

var command = &cobra.Command{
Use: "generate <path>",
Expand Down Expand Up @@ -63,6 +64,7 @@ func NewGenerateCommand() *cobra.Command {

v := viper.New()
viper.Set("verboseOutput", verboseOutput)
viper.Set("disableCache", disableCache)
cmdConfig, err := config.New(v, &config.Options{
SecretName: secretName,
ConfigPath: configPath,
Expand Down Expand Up @@ -116,5 +118,6 @@ func NewGenerateCommand() *cobra.Command {
command.Flags().StringVarP(&configPath, "config-path", "c", "", "path to a file containing Vault configuration (YAML, JSON, envfile) to use")
command.Flags().StringVarP(&secretName, "secret-name", "s", "", "name of a Kubernetes Secret in the argocd namespace containing Vault configuration data in the argocd namespace of your ArgoCD host (Only available when used in ArgoCD). The namespace can be overridden by using the format <namespace>:<name>")
command.Flags().BoolVar(&verboseOutput, "verbose-sensitive-output", false, "enable verbose mode for detailed info to help with debugging. Includes sensitive data (credentials), logged to stderr")
command.Flags().BoolVar(&disableCache, "disable-token-cache", false, "disable the automatic token cache feature that store tokens locally")
return command
}
50 changes: 50 additions & 0 deletions cmd/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package cmd

import (
"bytes"
"fmt"
"io"
"os"
"strings"
"testing"

"github.com/argoproj-labs/argocd-vault-plugin/pkg/helpers"
"github.com/argoproj-labs/argocd-vault-plugin/pkg/utils"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/vault"
)
Expand Down Expand Up @@ -250,6 +252,54 @@ func TestMain(t *testing.T) {
}
})

t.Run("will not create cache if disabled", func(t *testing.T) {

// Purging token cache before launching this test
err := utils.PurgeTokenCache()
if err != nil {
t.Fatalf("fail to purge tocken cache: %s", err.Error())
}

// Starting the generate command with the --disable-token-cache flag
args := []string{
"../fixtures/input/nonempty",
"--disable-token-cache",
}
cmd := NewGenerateCommand()

b := bytes.NewBufferString("")
e := bytes.NewBufferString("")
cmd.SetArgs(args)
cmd.SetOut(b)
cmd.SetErr(e)
cmd.Execute()
out, err := io.ReadAll(b) // Read buffer to bytes
if err != nil {
t.Fatal(err)
}
stderr, err := io.ReadAll(e) // Read buffer to bytes
if err != nil {
t.Fatal(err)
}

buf, err := os.ReadFile("../fixtures/output/all.yaml")
if err != nil {
t.Fatal(err)
}

// We first check that the command was successful to make sure it reached the token caching part
expected := string(buf)
if string(out) != expected {
t.Fatalf("expected %s\n\nbut got\n\n%s\nerr: %s", expected, string(out), string(stderr))
}

// No cache is expected
_, err = utils.ReadExistingToken(fmt.Sprintf("approle_%s", roleid))
if err == nil {
t.Fatalf("expected no cache but found one")
}
})

os.Unsetenv("AVP_TYPE")
os.Unsetenv("VAULT_ADDR")
os.Unsetenv("AVP_AUTH_TYPE")
Expand Down
4 changes: 2 additions & 2 deletions pkg/auth/vault/approle.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func NewAppRoleAuth(roleID, secretID, mountPath string) *AppRoleAuth {

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

// If we cannot write the Vault token, we'll just have to login next time. Nothing showstopping.
err = utils.SetToken(vaultClient, data.Auth.ClientToken)
err = utils.SetToken(vaultClient, fmt.Sprintf("approle_%s", a.RoleID), data.Auth.ClientToken)
if err != nil {
utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err)
}
Expand Down
26 changes: 24 additions & 2 deletions pkg/auth/vault/approle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package vault_test

import (
"bytes"
"fmt"
"testing"

"github.com/argoproj-labs/argocd-vault-plugin/pkg/auth/vault"
Expand All @@ -20,7 +21,7 @@ func TestAppRoleLogin(t *testing.T) {
t.Fatalf("expected no errors but got: %s", err)
}

cachedToken, err := utils.ReadExistingToken()
cachedToken, err := utils.ReadExistingToken(fmt.Sprintf("approle_%s", roleID))
if err != nil {
t.Fatalf("expected cached vault token but got: %s", err)
}
Expand All @@ -30,12 +31,33 @@ func TestAppRoleLogin(t *testing.T) {
t.Fatalf("expected no errors but got: %s", err)
}

newCachedToken, err := utils.ReadExistingToken()
newCachedToken, err := utils.ReadExistingToken(fmt.Sprintf("approle_%s", roleID))
if err != nil {
t.Fatalf("expected cached vault token but got: %s", err)
}

if bytes.Compare(cachedToken, newCachedToken) != 0 {
t.Fatalf("expected same token %s but got %s", cachedToken, newCachedToken)
}

// We create a new connection with a different approle and create a different cache
secondCluster, secondRoleID, secondSecretID := helpers.CreateTestAppRoleVault(t)
defer secondCluster.Cleanup()

secondAppRole := vault.NewAppRoleAuth(secondRoleID, secondSecretID, "")

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

secondCachedToken, err := utils.ReadExistingToken(fmt.Sprintf("approle_%s", secondRoleID))
if err != nil {
t.Fatalf("expected cached vault token but got: %s", err)
}

// Both cache should be different
if bytes.Compare(cachedToken, secondCachedToken) == 0 {
t.Fatalf("expected different tokens but got %s", secondCachedToken)
}
}
4 changes: 2 additions & 2 deletions pkg/auth/vault/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func NewGithubAuth(token, mountPath string) *GithubAuth {

// Authenticate authenticates with Vault and returns a token
func (g *GithubAuth) Authenticate(vaultClient *api.Client) error {
err := utils.LoginWithCachedToken(vaultClient)
err := utils.LoginWithCachedToken(vaultClient, "github")
if err != nil {
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", err)
} else {
Expand All @@ -52,7 +52,7 @@ func (g *GithubAuth) Authenticate(vaultClient *api.Client) error {
utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data)

// If we cannot write the Vault token, we'll just have to login next time. Nothing showstopping.
err = utils.SetToken(vaultClient, data.Auth.ClientToken)
err = utils.SetToken(vaultClient, "github", data.Auth.ClientToken)
if err != nil {
utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/auth/vault/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestGithubLogin(t *testing.T) {
t.Fatalf("expected no errors but got: %s", err)
}

cachedToken, err := utils.ReadExistingToken()
cachedToken, err := utils.ReadExistingToken("github")
if err != nil {
t.Fatalf("expected cached vault token but got: %s", err)
}
Expand All @@ -31,7 +31,7 @@ func TestGithubLogin(t *testing.T) {
t.Fatalf("expected no errors but got: %s", err)
}

newCachedToken, err := utils.ReadExistingToken()
newCachedToken, err := utils.ReadExistingToken("github")
if err != nil {
t.Fatalf("expected cached vault token but got: %s", err)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/auth/vault/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func NewK8sAuth(role, mountPath, tokenPath string) *K8sAuth {

// Authenticate authenticates with Vault via K8s and returns a token
func (k *K8sAuth) Authenticate(vaultClient *api.Client) error {
err := utils.LoginWithCachedToken(vaultClient)
err := utils.LoginWithCachedToken(vaultClient, "kubernetes")
if err != nil {
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", err)
} else {
Expand Down Expand Up @@ -70,7 +70,7 @@ func (k *K8sAuth) Authenticate(vaultClient *api.Client) error {
utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data)

// If we cannot write the Vault token, we'll just have to login next time. Nothing showstopping.
err = utils.SetToken(vaultClient, data.Auth.ClientToken)
err = utils.SetToken(vaultClient, "kubernetes", data.Auth.ClientToken)
if err != nil {
utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/auth/vault/kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestKubernetesAuth(t *testing.T) {
t.Fatalf("expected no errors but got: %s", err)
}

cachedToken, err := utils.ReadExistingToken()
cachedToken, err := utils.ReadExistingToken("kubernetes")
if err != nil {
t.Fatalf("expected cached vault token but got: %s", err)
}
Expand All @@ -63,7 +63,7 @@ func TestKubernetesAuth(t *testing.T) {
t.Fatalf("expected no errors but got: %s", err)
}

newCachedToken, err := utils.ReadExistingToken()
newCachedToken, err := utils.ReadExistingToken("kubernetes")
if err != nil {
t.Fatalf("expected cached vault token but got: %s", err)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/auth/vault/userpass.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func NewUserPassAuth(username, password, mountPath string) *UserPassAuth {

// Authenticate authenticates with Vault using userpass and returns a token
func (a *UserPassAuth) Authenticate(vaultClient *api.Client) error {
err := utils.LoginWithCachedToken(vaultClient)
err := utils.LoginWithCachedToken(vaultClient, fmt.Sprintf("userpass_%s", a.Username))
if err != nil {
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", err)
} else {
Expand All @@ -53,7 +53,7 @@ func (a *UserPassAuth) Authenticate(vaultClient *api.Client) error {
utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data)

// If we cannot write the Vault token, we'll just have to login next time. Nothing showstopping.
if err = utils.SetToken(vaultClient, data.Auth.ClientToken); err != nil {
if err = utils.SetToken(vaultClient, fmt.Sprintf("userpass_%s", a.Username), data.Auth.ClientToken); err != nil {
utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err)
}

Expand Down
26 changes: 24 additions & 2 deletions pkg/auth/vault/userpass_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package vault_test

import (
"bytes"
"fmt"
"testing"

"github.com/argoproj-labs/argocd-vault-plugin/pkg/auth/vault"
Expand All @@ -19,7 +20,7 @@ func TestUserPassLogin(t *testing.T) {
t.Fatalf("expected no errors but got: %s", err)
}

cachedToken, err := utils.ReadExistingToken()
cachedToken, err := utils.ReadExistingToken(fmt.Sprintf("userpass_%s", username))
if err != nil {
t.Fatalf("expected cached vault token but got: %s", err)
}
Expand All @@ -29,12 +30,33 @@ func TestUserPassLogin(t *testing.T) {
t.Fatalf("expected no errors but got: %s", err)
}

newCachedToken, err := utils.ReadExistingToken()
newCachedToken, err := utils.ReadExistingToken(fmt.Sprintf("userpass_%s", username))
if err != nil {
t.Fatalf("expected cached vault token but got: %s", err)
}

if bytes.Compare(cachedToken, newCachedToken) != 0 {
t.Fatalf("expected same token %s but got %s", cachedToken, newCachedToken)
}

// We create a new connection with a different approle and create a different cache
secondCluster, secondUsername, secondPassword := helpers.CreateTestUserPassVault(t)
defer secondCluster.Cleanup()

secondUserpass := vault.NewUserPassAuth(secondUsername, secondPassword, "")

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

secondCachedToken, err := utils.ReadExistingToken(fmt.Sprintf("userpass_%s", secondUsername))
if err != nil {
t.Fatalf("expected cached vault token but got: %s", err)
}

// Both cache should be different
if bytes.Compare(cachedToken, secondCachedToken) == 0 {
t.Fatalf("expected different tokens but got %s", secondCachedToken)
}
}
Loading

0 comments on commit 631bbc3

Please sign in to comment.