From 6fddf111ce08b1d5fa063fb0e6c893a669185ad4 Mon Sep 17 00:00:00 2001 From: Josh Kayani Date: Tue, 31 May 2022 23:46:23 -0400 Subject: [PATCH 1/4] feat: Verbose logging with flag --- cmd/generate.go | 8 +++++++ pkg/auth/vault/approle.go | 12 +++++++++- pkg/auth/vault/github.go | 12 +++++++++- pkg/auth/vault/kubernetes.go | 11 ++++++++- pkg/backends/awssecretsmanager.go | 8 +++++++ pkg/backends/azurekeyvault.go | 27 ++++++++++++++++++++++ pkg/backends/gcpsecretmanager.go | 8 +++++++ pkg/backends/ibmsecretsmanager.go | 26 +++++++++++++++++++++ pkg/backends/localsecretmanager.go | 8 +++++++ pkg/backends/onepasswordconnect.go | 8 +++++++ pkg/backends/vault.go | 11 +++++++++ pkg/backends/yandexcloudlockbox.go | 8 +++++++ pkg/config/config.go | 37 ++++++++++++++++++++++++++---- pkg/config/config_test.go | 14 ++++++----- pkg/kube/client.go | 6 +++++ pkg/kube/template.go | 6 +++++ pkg/kube/util.go | 30 +++++++++++++++++++++++- pkg/utils/util.go | 5 ++++ 18 files changed, 230 insertions(+), 15 deletions(-) diff --git a/cmd/generate.go b/cmd/generate.go index 9a2dae62..3ee239e8 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -10,6 +10,7 @@ import ( "github.com/argoproj-labs/argocd-vault-plugin/pkg/config" "github.com/argoproj-labs/argocd-vault-plugin/pkg/kube" "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -18,6 +19,7 @@ import ( func NewGenerateCommand() *cobra.Command { const StdIn = "-" var configPath, secretName string + var verboseOutput bool var command = &cobra.Command{ Use: "generate ", @@ -59,6 +61,7 @@ func NewGenerateCommand() *cobra.Command { } v := viper.New() + viper.Set("verboseOutput", verboseOutput) cmdConfig, err := config.New(v, &config.Options{ SecretName: secretName, ConfigPath: configPath, @@ -90,6 +93,10 @@ func NewGenerateCommand() *cobra.Command { if err != nil { return err } + } else { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("skipping %s.%s because %s annotation is present", manifest.GetNamespace(), manifest.GetName(), types.AVPIgnoreAnnotation) + } } output, err := template.ToYAML() @@ -106,5 +113,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 :") + 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") return command } diff --git a/pkg/auth/vault/approle.go b/pkg/auth/vault/approle.go index 7d8878b7..36c8d51b 100644 --- a/pkg/auth/vault/approle.go +++ b/pkg/auth/vault/approle.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/hashicorp/vault/api" + "github.com/spf13/viper" ) const ( @@ -37,15 +38,24 @@ func (a *AppRoleAuth) Authenticate(vaultClient *api.Client) error { "role_id": a.RoleID, "secret_id": a.SecretID, } + + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Hashicorp Vault authenticating with role ID %s and secret ID %s", a.RoleID, a.SecretID) + } data, err := vaultClient.Logical().Write(fmt.Sprintf("%s/login", a.MountPath), payload) if err != nil { return err } + if viper.GetBool("verboseOutput") { + 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) if err != nil { - print(err) + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err) + } } return nil diff --git a/pkg/auth/vault/github.go b/pkg/auth/vault/github.go index 337f8c36..71f9e8ea 100644 --- a/pkg/auth/vault/github.go +++ b/pkg/auth/vault/github.go @@ -5,6 +5,7 @@ import ( "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/hashicorp/vault/api" + "github.com/spf13/viper" ) const ( @@ -36,14 +37,23 @@ func (g *GithubAuth) Authenticate(vaultClient *api.Client) error { "token": g.AccessToken, } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Hashicorp Vault authenticating with Github token %s", g.AccessToken) + } data, err := vaultClient.Logical().Write(fmt.Sprintf("%s/login", g.MountPath), payload) if err != nil { return err } + if viper.GetBool("verboseOutput") { + 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) if err != nil { - print(err) + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err) + } } return nil diff --git a/pkg/auth/vault/kubernetes.go b/pkg/auth/vault/kubernetes.go index f686aed1..714fcad4 100644 --- a/pkg/auth/vault/kubernetes.go +++ b/pkg/auth/vault/kubernetes.go @@ -8,6 +8,7 @@ import ( "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/hashicorp/vault/api" + "github.com/spf13/viper" ) const ( @@ -54,15 +55,23 @@ func (k *K8sAuth) Authenticate(vaultClient *api.Client) error { kubeAuthPath = k.MountPath } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Hashicorp Vault authenticating with Vault role %s using Kubernetes service account token %s read from %s", k.Role, serviceAccountFile, token) + } data, err := vaultClient.Logical().Write(fmt.Sprintf("%s/login", kubeAuthPath), payload) if err != nil { return err } + if viper.GetBool("verboseOutput") { + 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) if err != nil { - print(err) + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err) + } } return nil diff --git a/pkg/backends/awssecretsmanager.go b/pkg/backends/awssecretsmanager.go index c3c0b53f..7d576769 100644 --- a/pkg/backends/awssecretsmanager.go +++ b/pkg/backends/awssecretsmanager.go @@ -4,9 +4,11 @@ import ( "encoding/json" "fmt" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" + "github.com/spf13/viper" ) // AWSSecretsManager is a struct for working with a AWS Secrets Manager backend @@ -36,10 +38,16 @@ func (a *AWSSecretsManager) GetSecrets(path string, version string, annotations input.SetVersionId(version) } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("AWS Secrets Manager getting secret %s at version %s", path, version) + } result, err := a.Client.GetSecretValue(input) if err != nil { return nil, err } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("AWS Secrets Manager get secret response %v", result) + } var dat map[string]interface{} diff --git a/pkg/backends/azurekeyvault.go b/pkg/backends/azurekeyvault.go index 4f4f7d29..6c3a842e 100644 --- a/pkg/backends/azurekeyvault.go +++ b/pkg/backends/azurekeyvault.go @@ -4,6 +4,8 @@ import ( "context" "fmt" "github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" + "github.com/spf13/viper" "path" "strings" "time" @@ -36,18 +38,31 @@ func (a *AzureKeyVault) GetSecrets(kvpath string, version string, _ map[string]s data := make(map[string]interface{}) + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Azure Key Vault listing secrets in vault %v", kvpath) + } secretList, err := a.Client.GetSecretsComplete(ctx, kvpath, nil) if err != nil { return nil, err } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Azure Key Vault list secrets response %v", secretList) + } // Gather all secrets in Key Vault + for ; secretList.NotDone(); secretList.NextWithContext(ctx) { secret := path.Base(*secretList.Value().ID) if version == "" { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Azure Key Vault getting secret %s from vault %s", secret, kvpath) + } secretResp, err := a.Client.GetSecret(ctx, kvpath, secret, "") if err != nil { return nil, err } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Azure Key Vault get unversioned secret response %v", secretResp) + } data[secret] = *secretResp.Value continue } @@ -62,10 +77,16 @@ func (a *AzureKeyVault) GetSecrets(kvpath string, version string, _ map[string]s } // Secret version matched given version if strings.Contains(*secretVersion.ID, version) { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Azure Key Vault getting secret %s from vault %s at version %s", secret, kvpath, version) + } secretResp, err := a.Client.GetSecret(ctx, kvpath, secret, version) if err != nil { return nil, err } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Azure Key Vault get versioned secret response %v", secretResp) + } data[secret] = *secretResp.Value } } @@ -80,12 +101,18 @@ func (a *AzureKeyVault) GetSecrets(kvpath string, version string, _ map[string]s func (a *AzureKeyVault) GetIndividualSecret(kvpath, secret, version string, annotations map[string]string) (interface{}, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Azure Key Vault getting secret %s from vault %s at version %s", secret, kvpath, version) + } kvpath = fmt.Sprintf("https://%s.vault.azure.net", kvpath) data, err := a.Client.GetSecret(ctx, kvpath, secret, version) if err != nil { return nil, err } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Azure Key Vault get versioned secret response %v", data) + } return *data.Value, nil } diff --git a/pkg/backends/gcpsecretmanager.go b/pkg/backends/gcpsecretmanager.go index 9276f1d0..40ed2ba0 100644 --- a/pkg/backends/gcpsecretmanager.go +++ b/pkg/backends/gcpsecretmanager.go @@ -6,7 +6,9 @@ import ( "regexp" "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/googleapis/gax-go/v2" + "github.com/spf13/viper" secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1" ) @@ -50,10 +52,16 @@ func (a *GCPSecretManager) GetSecrets(path string, version string, annotations m Name: fmt.Sprintf("%s/versions/%s", path, version), } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("GCP Secret Manager accessing secret at path %s at version %v", path, version) + } result, err := a.Client.AccessSecretVersion(a.Context, req) if err != nil { return nil, fmt.Errorf("Could not find secret: %v", err) } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("GCP Secret Manager access secret version response %v", result) + } data := make(map[string]interface{}) diff --git a/pkg/backends/ibmsecretsmanager.go b/pkg/backends/ibmsecretsmanager.go index 96c951a4..6a49902a 100644 --- a/pkg/backends/ibmsecretsmanager.go +++ b/pkg/backends/ibmsecretsmanager.go @@ -8,6 +8,8 @@ import ( "github.com/IBM/go-sdk-core/v5/core" ibmsm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv1" "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" + "github.com/spf13/viper" ) var IBMPath, _ = regexp.Compile(`ibmcloud/(?P.+)/secrets/groups/(?P.+)`) @@ -120,6 +122,10 @@ func (i *IBMSecretsManager) getSecretVersionedOrNot(secret *ibmsm.SecretResource return nil, fmt.Errorf("Could not retrieve secret %s after %d retries, statuscode %d", *secret.ID, types.IBMMaxRetries, httpResponse.GetStatusCode()) } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("IBM Cloud Secrets Manager get versioned secret %s HTTP response: %v", *secret.ID, httpResponse) + } + result = (secretVersion.Resources[0].(*ibmsm.SecretVersion)).SecretData.(map[string]interface{}) } else { secretRes, httpResponse, err := i.Client.GetSecret(&ibmsm.GetSecretOptions{ @@ -133,6 +139,10 @@ func (i *IBMSecretsManager) getSecretVersionedOrNot(secret *ibmsm.SecretResource return nil, fmt.Errorf("Could not retrieve secret %s after %d retries, statuscode %d", *secret.ID, types.IBMMaxRetries, httpResponse.GetStatusCode()) } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("IBM Cloud Secrets Manager get unversioned secret %s HTTP response: %v", *secret.ID, httpResponse) + } + // APIKey secrets don't come from `SecretData` if *secret.SecretType == types.IBMIAMCredentialsType { result = map[string]interface{}{ @@ -171,9 +181,15 @@ func (i *IBMSecretsManager) getSecret(secret *ibmsm.SecretResource, version stri // Bypass the cache when explicit version is requested if cacheResult != nil && version == "" { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("IBM Cloud Secrets Manager get secret: cache hit for %s of type %s from group %s", secretName, secretType, groupId) + } result["payload"] = cacheResult } else { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("IBM Cloud Secrets Manager get secret: getting secret %s of type %s from group %s", secretName, secretType, groupId) + } secretData, err := i.getSecretVersionedOrNot(secret, version) var payload interface{} if err != nil { @@ -211,11 +227,17 @@ func (i *IBMSecretsManager) listSecretsInGroup(groupId, secretType string) (map[ ckey := cacheKey{groupId, secretType} cachedData := i.listAllSecretsCache[ckey] if cachedData != nil { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("IBM Cloud Secrets Manager list secrets in group: cache hit group %s", groupId) + } return cachedData, nil } var offset int64 = 0 for { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("IBM Cloud Secrets Manager listing secrets of from group %s starting at offset %d", groupId, offset) + } res, details, err := i.Client.ListAllSecrets(&ibmsm.ListAllSecretsOptions{ Groups: []string{groupId}, Offset: &offset, @@ -227,6 +249,10 @@ func (i *IBMSecretsManager) listSecretsInGroup(groupId, secretType string) (map[ return nil, fmt.Errorf("Could not list secrets for secret group %s: %d\n%s", groupId, details.GetStatusCode(), details.String()) } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("IBM Cloud Secrets Manager list secrets in group HTTP response: %v", details) + } + for _, secret := range res.Resources { name := *(secret.(*ibmsm.SecretResource).Name) ttype := *(secret.(*ibmsm.SecretResource).SecretType) diff --git a/pkg/backends/localsecretmanager.go b/pkg/backends/localsecretmanager.go index 68bc4335..be48eeef 100644 --- a/pkg/backends/localsecretmanager.go +++ b/pkg/backends/localsecretmanager.go @@ -3,6 +3,8 @@ package backends import ( "fmt" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" + "github.com/spf13/viper" "k8s.io/apimachinery/pkg/util/yaml" ) @@ -29,7 +31,13 @@ func (a *LocalSecretManager) Login() error { // GetSecrets gets secrets using decrypt function and returns the formatted data func (a *LocalSecretManager) GetSecrets(path string, version string, annotations map[string]string) (map[string]interface{}, error) { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Local secret manager getting secret %s at version %s", path, version) + } cleartext, err := a.Decrypt(path, "yaml") + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Local secret manager get secret response: %v", cleartext) + } var dat map[string]interface{} diff --git a/pkg/backends/onepasswordconnect.go b/pkg/backends/onepasswordconnect.go index 378cebc4..15d22092 100644 --- a/pkg/backends/onepasswordconnect.go +++ b/pkg/backends/onepasswordconnect.go @@ -4,6 +4,8 @@ import ( "strings" "github.com/1Password/connect-sdk-go/connect" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" + "github.com/spf13/viper" ) // OnePassword is a struct for working with a 1Password Connect backend @@ -30,10 +32,16 @@ func (a *OnePasswordConnect) GetSecrets(path string, version string, annotations vaultUUID := splits[1] itemUUID := splits[3] + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("OnePassword Connect getting item %s from vault %s", itemUUID, vaultUUID) + } result, err := a.Client.GetItem(itemUUID, vaultUUID) if err != nil { return nil, err } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("OnePassword Connect get secret response: %v", result) + } data := make(map[string]interface{}) diff --git a/pkg/backends/vault.go b/pkg/backends/vault.go index edcdd9c8..1e7a71c9 100644 --- a/pkg/backends/vault.go +++ b/pkg/backends/vault.go @@ -5,7 +5,9 @@ import ( "fmt" "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/hashicorp/vault/api" + "github.com/spf13/viper" ) // Vault is a struct for working with a Vault backend @@ -47,16 +49,25 @@ func (v *Vault) GetSecrets(path string, version string, annotations map[string]s // Vault KV-V1 doesn't support versioning so we only honor `version` if KV-V2 is used if version != "" && kvVersion == "2" { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Hashicorp Vault getting kv pairs from KV-V2 path %s at version %s", path, version) + } secret, err = v.VaultClient.Logical().ReadWithData(path, map[string][]string{ "version": {version}, }) } else { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Hashicorp Vault getting kv pairs from KV-V1 path %s", path) + } secret, err = v.VaultClient.Logical().Read(path) } if err != nil { return nil, err } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Hashicorp Vault get kv pairs response: %v", secret) + } if secret == nil { // Do not mention `version` in error message when it's not honored (KV-V1) diff --git a/pkg/backends/yandexcloudlockbox.go b/pkg/backends/yandexcloudlockbox.go index 14a3de2a..bd0de43c 100644 --- a/pkg/backends/yandexcloudlockbox.go +++ b/pkg/backends/yandexcloudlockbox.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" + "github.com/spf13/viper" "github.com/yandex-cloud/go-genproto/yandex/cloud/lockbox/v1" ) @@ -34,10 +36,16 @@ func (ycl *YandexCloudLockbox) GetSecrets(secretID string, version string, _ map req.SetVersionId(version) } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Yandex Cloud Lockbox getting secret %s at version %s", secretID, version) + } resp, err := ycl.client.Get(context.Background(), req) if err != nil { return nil, err } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("Yandex Cloud Lockbox get secret response %v", resp) + } result := make(map[string]interface{}, len(resp.GetEntries())) for _, v := range resp.GetEntries() { diff --git a/pkg/config/config.go b/pkg/config/config.go index 15bf5b13..52364688 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,7 +3,6 @@ package config import ( "bytes" "context" - "errors" "fmt" "log" "os" @@ -21,6 +20,7 @@ import ( "github.com/argoproj-labs/argocd-vault-plugin/pkg/backends" "github.com/argoproj-labs/argocd-vault-plugin/pkg/kube" "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" awssm "github.com/aws/aws-sdk-go/service/secretsmanager" @@ -63,8 +63,18 @@ func New(v *viper.Viper, co *Options) (*Config, error) { } // Instantiate Env + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("reading configuration from environment, overriding any previous settings") + } v.AutomaticEnv() + if viper.GetBool("verboseOutput") { + log.Print("AVP configured with the following settings:\n") + for k, viperValue := range v.AllSettings() { + utils.VerboseToStdErr("%s: %s\n", k, viperValue) + } + } + authType := v.GetString(types.EnvAvpAuthType) var auth types.AuthType @@ -117,7 +127,7 @@ func New(v *viper.Viper, co *Options) (*Config, error) { return nil, fmt.Errorf("%s for token authentication cannot be empty", api.EnvVaultToken) } default: - return nil, errors.New("Must provide a supported Authentication Type") + return nil, fmt.Errorf("Must provide a supported Authentication Type, received %s", authType) } backend = backends.NewVaultBackend(auth, apiClient, v.GetString(types.EnvAvpKvVersion)) } @@ -129,6 +139,10 @@ func New(v *viper.Viper, co *Options) (*Config, error) { if !v.IsSet(types.EnvVaultAddress) { return nil, fmt.Errorf("%s or %s required for IBM Secrets Manager", types.EnvAvpIBMInstanceURL, types.EnvVaultAddress) } + + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("falling back to %s in place of %s", types.EnvVaultAddress, types.EnvAvpIBMInstanceURL) + } url = v.GetString(types.EnvVaultAddress) } @@ -140,14 +154,19 @@ func New(v *viper.Viper, co *Options) (*Config, error) { return nil, err } + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("IBM Cloud Secrets Manager enabling %d API call retries with %d seconds between tries", types.IBMMaxRetries, types.IBMRetryIntervalSeconds) + } client.EnableRetries(types.IBMMaxRetries, time.Duration(types.IBMRetryIntervalSeconds)*time.Second) backend = backends.NewIBMSecretsManagerBackend(client) } case types.AWSSecretsManagerbackend: { - if !v.IsSet(types.EnvAWSRegion) { // issue warning when using default region - log.Printf("Warning: %s env var not set, using AWS region %s.\n", types.EnvAWSRegion, types.AwsDefaultRegion) + if !v.IsSet(types.EnvAWSRegion) { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("warning: %s env var not set, using AWS region %s", types.EnvAWSRegion, types.AwsDefaultRegion) + } v.Set(types.EnvAWSRegion, types.AwsDefaultRegion) } @@ -224,7 +243,7 @@ func New(v *viper.Viper, co *Options) (*Config, error) { backend = backends.NewOnePasswordConnectBackend(client) } default: - return nil, errors.New("Must provide a supported Vault Type") + return nil, fmt.Errorf("Must provide a supported Vault Type, received %s", v.GetString(types.EnvAvpType)) } return &Config{ @@ -235,6 +254,10 @@ func New(v *viper.Viper, co *Options) (*Config, error) { func readConfigOrSecret(secretName, configPath string, v *viper.Viper) error { // If a secret name is passed, pull config from Kubernetes if secretName != "" { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("reading configuration from secret %s", secretName) + } + localClient, err := kube.NewClient() if err != nil { return err @@ -249,6 +272,10 @@ func readConfigOrSecret(secretName, configPath string, v *viper.Viper) error { // If a config file path is passed, read in that file and overwrite all other if configPath != "" { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("reading configuration from config file %s, overriding any previous settings", configPath) + } + v.SetConfigFile(configPath) err := v.ReadInConfig() if err != nil { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index ecb4b79c..d0acabb6 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "log" "os" + "strings" "testing" "github.com/argoproj-labs/argocd-vault-plugin/pkg/config" @@ -189,7 +190,7 @@ fDGt+yaf3RaZbVwHSVLzxiXGsu1WQJde3uJeNh5c6z+5 func TestNewConfigNoType(t *testing.T) { viper := viper.New() _, err := config.New(viper, &config.Options{}) - expectedError := "Must provide a supported Vault Type" + expectedError := "Must provide a supported Vault Type, received " if err.Error() != expectedError { t.Errorf("expected error %s to be thrown, got %s", expectedError, err) @@ -200,7 +201,7 @@ func TestNewConfigNoAuthType(t *testing.T) { os.Setenv("AVP_TYPE", "vault") viper := viper.New() _, err := config.New(viper, &config.Options{}) - expectedError := "Must provide a supported Authentication Type" + expectedError := "Must provide a supported Authentication Type, received " if err.Error() != expectedError { t.Errorf("expected error %s to be thrown, got %s", expectedError, err) @@ -234,7 +235,7 @@ func TestNewConfigAwsRegionWarning(t *testing.T) { "AWS_SECRET_ACCESS_KEY": "key", }, "*backends.AWSSecretsManager", - "Warning: AWS_REGION env var not set, using AWS region us-east-2.\n", + "warning: AWS_REGION env var not set, using AWS region us-east-2\n", }, { // no warning is issued map[string]interface{}{ @@ -252,10 +253,11 @@ func TestNewConfigAwsRegionWarning(t *testing.T) { for k, v := range tc.environment { os.Setenv(k, v.(string)) } - viper := viper.New() + viper.Set("verboseOutput", true) + v := viper.New() output := captureOutput(func() { - config, err := config.New(viper, &config.Options{}) + config, err := config.New(v, &config.Options{}) if err != nil { t.Error(err) t.FailNow() @@ -266,7 +268,7 @@ func TestNewConfigAwsRegionWarning(t *testing.T) { } }) - if output != tc.expectedLog { + if !strings.Contains(output, tc.expectedLog) { t.Errorf("Unexpected warning issued. Expected: %s, actual: %s", tc.expectedLog, output) } diff --git a/pkg/kube/client.go b/pkg/kube/client.go index bfbb50d2..30943714 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" + "github.com/spf13/viper" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -35,6 +37,10 @@ func NewClient() (*Client, error) { // and returns a YAML []byte containing its data, decoded from base64 func (c *Client) ReadSecret(name string) ([]byte, error) { secretNamespace, secretName := secretNamespaceName(name) + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("parsed secret name as %s from namespace %s", secretName, secretNamespace) + } + s, err := c.client.CoreV1().Secrets(secretNamespace).Get(context.TODO(), secretName, metav1.GetOptions{}) if err != nil { return nil, err diff --git a/pkg/kube/template.go b/pkg/kube/template.go index e3714450..d31f1803 100644 --- a/pkg/kube/template.go +++ b/pkg/kube/template.go @@ -5,6 +5,8 @@ import ( "strings" "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" + "github.com/spf13/viper" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" yaml "sigs.k8s.io/yaml" ) @@ -37,6 +39,10 @@ func NewTemplate(template unstructured.Unstructured, backend types.Backend) (*Te if err != nil { return nil, err } + + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("calling GetSecrets to get all secrets from backend because %s is set to %s", types.AVPPathAnnotation, path) + } } return &Template{ diff --git a/pkg/kube/util.go b/pkg/kube/util.go index b581cda0..c0a49163 100644 --- a/pkg/kube/util.go +++ b/pkg/kube/util.go @@ -11,6 +11,8 @@ import ( "strings" "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" + "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" + "github.com/spf13/viper" k8yaml "k8s.io/apimachinery/pkg/util/yaml" ) @@ -70,8 +72,8 @@ func replaceInner( } } } else if valueType == reflect.String { - // Base case, replace templated strings + // Base case, replace templated strings removeKey := false replacement, err := replacerFunc(key, value.(string), *r) if len(err) != 0 { @@ -92,6 +94,9 @@ func replaceInner( } if removeKey { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("removing key %s due to %s being set on the containing manifest", key, types.AVPRemoveMissingAnnotation) + } delete(obj, key) } else { obj[key] = replacement @@ -119,6 +124,10 @@ func genericReplacement(key, value string, resource Resource) (_ interface{}, er pipelineFields := strings.Split(placeholder, "|") placeholder = strings.Trim(pipelineFields[0], " ") + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("found placeholder %s with modifiers %s", placeholder, pipelineFields[1:]) + } + var secretValue interface{} var secretErr error // Check to see if should call out to get individual secret (inline-path in placeholder) @@ -129,6 +138,9 @@ func genericReplacement(key, value string, resource Resource) (_ interface{}, er key := indivSecretMatches[indivPlaceholderSyntax.SubexpIndex("key")] version := indivSecretMatches[indivPlaceholderSyntax.SubexpIndex("version")] + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("calling GetIndividualSecret for secret %s from path %s at version %s", key, path, version) + } secretValue, secretErr = resource.Backend.GetIndividualSecret(path, strings.TrimSpace(key), version, resource.Annotations) if secretErr != nil { err = append(err, secretErr) @@ -143,6 +155,11 @@ func genericReplacement(key, value string, resource Resource) (_ interface{}, er for _, stmt := range pipelineFields[1:] { fields := strings.Fields(stmt) functionName := strings.Trim(fields[0], " ") + + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("processing modifier %s with args %q", functionName, fields) + } + if _, ok := modifiers[functionName]; !ok { e := fmt.Errorf("invalid modifier: %s for placeholder %s in string %s: %s", functionName, placeholder, key, value) err = append(err, e) @@ -182,6 +199,9 @@ func genericReplacement(key, value string, resource Resource) (_ interface{}, er // In the case where the value is a non-string, we insert it directly here. // Useful for cases like `replicas: ` if nonStringReplacement != nil { + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("value found in secret manager is non-string: %v, injecting directly into template") + } return nonStringReplacement, err } @@ -195,6 +215,9 @@ func configReplacement(key, value string, resource Resource) (interface{}, []err } // configMap data values must be strings + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("key %s comes from ConfigMap manifest, stringifying value %s to fit", key, value) + } return stringify(res), err } @@ -202,6 +225,10 @@ func secretReplacement(key, value string, resource Resource) (interface{}, []err decoded, err := base64.StdEncoding.DecodeString(value) if err == nil && genericPlaceholder.Match(decoded) { res, err := genericReplacement(key, string(decoded), resource) + + if viper.GetBool("verboseOutput") { + utils.VerboseToStdErr("key %s comes from Secret manifest, base64 encoding value %s to fit", key, value) + } return base64.StdEncoding.EncodeToString([]byte(stringify(res))), err } @@ -249,5 +276,6 @@ func secretNamespaceName(input string) (string, string) { secretNamespace = types.ArgoCDNamespace secretName = nameFields[0] } + return secretNamespace, secretName } diff --git a/pkg/utils/util.go b/pkg/utils/util.go index f64af008..1d00352e 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "log" "net/http" "os" "path/filepath" @@ -106,3 +107,7 @@ func DefaultHttpClient() *http.Client { return httpClient } + +func VerboseToStdErr(format string, message ...interface{}) { + log.Printf(fmt.Sprintf("%s\n", format), message...) +} From ed1a62e5fbaf25e9c5b7b53b925c76eacdf1c29b Mon Sep 17 00:00:00 2001 From: Josh Kayani Date: Thu, 2 Jun 2022 00:39:38 -0400 Subject: [PATCH 2/4] docs: Include verbose output flag --- docs/cmd/generate.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/cmd/generate.md b/docs/cmd/generate.md index ae61a1bb..ade76457 100644 --- a/docs/cmd/generate.md +++ b/docs/cmd/generate.md @@ -6,9 +6,10 @@ argocd-vault-plugin generate PATH [flags] ### Options ``` - -c, --config-path string path to a file containing Vault configuration (YAML, JSON, envfile) to use - -h, --help help for generate - -s, --secret-name string name of a Kubernetes Secret containing Vault configuration data in the argocd namespace of your ArgoCD host (Only available when used in ArgoCD) + -c, --config-path string path to a file containing Vault configuration (YAML, JSON, envfile) to use + -h, --help help for generate + -s, --secret-name string 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 : + --verbose-sensitive-output enable verbose mode for detailed info to help with debugging. Includes sensitive data (credentials), logged to stderr ``` ### SEE ALSO From 28e6fb1eb0071b40f224c847abc1d3852e632b3f Mon Sep 17 00:00:00 2001 From: Josh Kayani Date: Thu, 2 Jun 2022 00:42:17 -0400 Subject: [PATCH 3/4] chore: Update ISSUE_TEMPLATE to mention verbose output --- .github/ISSUE_TEMPLATE/bug_report.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 891c6177..df02625e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -20,8 +20,10 @@ Steps to reproduce the behavior: **Expected behavior** A clear and concise description of what you expected to happen. -**Screenshots** +**Screenshots/Verbose output** If applicable, add screenshots to help explain your problem. +If you've tried running `argocd-vault-plugin generate` with `--verbose-sensitive-output` to help debug, please include that output here after redacting any secrets. + **Additional context** Add any other context about the problem here. From 16624e9611f03ae81a782f472e0301b46854e082 Mon Sep 17 00:00:00 2001 From: Josh Kayani Date: Wed, 8 Jun 2022 22:37:53 -0400 Subject: [PATCH 4/4] refactor: Move conditional to VerboseToStdErr() --- cmd/generate.go | 4 +--- pkg/auth/vault/approle.go | 14 ++++------- pkg/auth/vault/github.go | 14 ++++------- pkg/auth/vault/kubernetes.go | 14 ++++------- pkg/backends/awssecretsmanager.go | 10 +++----- pkg/backends/azurekeyvault.go | 38 ++++++++++-------------------- pkg/backends/gcpsecretmanager.go | 10 +++----- pkg/backends/ibmsecretsmanager.go | 30 ++++++----------------- pkg/backends/localsecretmanager.go | 11 +++------ pkg/backends/onepasswordconnect.go | 10 +++----- pkg/backends/vault.go | 14 ++++------- pkg/backends/yandexcloudlockbox.go | 10 +++----- pkg/config/config.go | 34 ++++++++------------------ pkg/kube/client.go | 6 ++--- pkg/kube/template.go | 5 +--- pkg/kube/util.go | 30 +++++++---------------- pkg/utils/util.go | 5 +++- 17 files changed, 77 insertions(+), 182 deletions(-) diff --git a/cmd/generate.go b/cmd/generate.go index 3ee239e8..0d4a3407 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -94,9 +94,7 @@ func NewGenerateCommand() *cobra.Command { return err } } else { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("skipping %s.%s because %s annotation is present", manifest.GetNamespace(), manifest.GetName(), types.AVPIgnoreAnnotation) - } + utils.VerboseToStdErr("skipping %s.%s because %s annotation is present", manifest.GetNamespace(), manifest.GetName(), types.AVPIgnoreAnnotation) } output, err := template.ToYAML() diff --git a/pkg/auth/vault/approle.go b/pkg/auth/vault/approle.go index 36c8d51b..ec577fca 100644 --- a/pkg/auth/vault/approle.go +++ b/pkg/auth/vault/approle.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/hashicorp/vault/api" - "github.com/spf13/viper" ) const ( @@ -39,23 +38,18 @@ func (a *AppRoleAuth) Authenticate(vaultClient *api.Client) error { "secret_id": a.SecretID, } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault authenticating with role ID %s and secret ID %s", a.RoleID, a.SecretID) - } + utils.VerboseToStdErr("Hashicorp Vault authenticating with role ID %s and secret ID %s", a.RoleID, a.SecretID) data, err := vaultClient.Logical().Write(fmt.Sprintf("%s/login", a.MountPath), payload) if err != nil { return err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data) - } + + 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) if err != nil { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err) - } + utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err) } return nil diff --git a/pkg/auth/vault/github.go b/pkg/auth/vault/github.go index 71f9e8ea..19d5f371 100644 --- a/pkg/auth/vault/github.go +++ b/pkg/auth/vault/github.go @@ -5,7 +5,6 @@ import ( "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/hashicorp/vault/api" - "github.com/spf13/viper" ) const ( @@ -37,23 +36,18 @@ func (g *GithubAuth) Authenticate(vaultClient *api.Client) error { "token": g.AccessToken, } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault authenticating with Github token %s", g.AccessToken) - } + utils.VerboseToStdErr("Hashicorp Vault authenticating with Github token %s", g.AccessToken) data, err := vaultClient.Logical().Write(fmt.Sprintf("%s/login", g.MountPath), payload) if err != nil { return err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data) - } + + 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) if err != nil { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err) - } + utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err) } return nil diff --git a/pkg/auth/vault/kubernetes.go b/pkg/auth/vault/kubernetes.go index 714fcad4..225c4b24 100644 --- a/pkg/auth/vault/kubernetes.go +++ b/pkg/auth/vault/kubernetes.go @@ -8,7 +8,6 @@ import ( "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/hashicorp/vault/api" - "github.com/spf13/viper" ) const ( @@ -55,23 +54,18 @@ func (k *K8sAuth) Authenticate(vaultClient *api.Client) error { kubeAuthPath = k.MountPath } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault authenticating with Vault role %s using Kubernetes service account token %s read from %s", k.Role, serviceAccountFile, token) - } + utils.VerboseToStdErr("Hashicorp Vault authenticating with Vault role %s using Kubernetes service account token %s read from %s", k.Role, serviceAccountFile, token) data, err := vaultClient.Logical().Write(fmt.Sprintf("%s/login", kubeAuthPath), payload) if err != nil { return err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data) - } + + 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) if err != nil { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err) - } + utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", err) } return nil diff --git a/pkg/backends/awssecretsmanager.go b/pkg/backends/awssecretsmanager.go index 7d576769..421424c8 100644 --- a/pkg/backends/awssecretsmanager.go +++ b/pkg/backends/awssecretsmanager.go @@ -8,7 +8,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" - "github.com/spf13/viper" ) // AWSSecretsManager is a struct for working with a AWS Secrets Manager backend @@ -38,16 +37,13 @@ func (a *AWSSecretsManager) GetSecrets(path string, version string, annotations input.SetVersionId(version) } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("AWS Secrets Manager getting secret %s at version %s", path, version) - } + utils.VerboseToStdErr("AWS Secrets Manager getting secret %s at version %s", path, version) result, err := a.Client.GetSecretValue(input) if err != nil { return nil, err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("AWS Secrets Manager get secret response %v", result) - } + + utils.VerboseToStdErr("AWS Secrets Manager get secret response %v", result) var dat map[string]interface{} diff --git a/pkg/backends/azurekeyvault.go b/pkg/backends/azurekeyvault.go index 6c3a842e..be88305a 100644 --- a/pkg/backends/azurekeyvault.go +++ b/pkg/backends/azurekeyvault.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" - "github.com/spf13/viper" "path" "strings" "time" @@ -38,31 +37,25 @@ func (a *AzureKeyVault) GetSecrets(kvpath string, version string, _ map[string]s data := make(map[string]interface{}) - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Azure Key Vault listing secrets in vault %v", kvpath) - } + utils.VerboseToStdErr("Azure Key Vault listing secrets in vault %v", kvpath) secretList, err := a.Client.GetSecretsComplete(ctx, kvpath, nil) if err != nil { return nil, err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Azure Key Vault list secrets response %v", secretList) - } + + utils.VerboseToStdErr("Azure Key Vault list secrets response %v", secretList) // Gather all secrets in Key Vault for ; secretList.NotDone(); secretList.NextWithContext(ctx) { secret := path.Base(*secretList.Value().ID) if version == "" { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Azure Key Vault getting secret %s from vault %s", secret, kvpath) - } + utils.VerboseToStdErr("Azure Key Vault getting secret %s from vault %s", secret, kvpath) secretResp, err := a.Client.GetSecret(ctx, kvpath, secret, "") if err != nil { return nil, err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Azure Key Vault get unversioned secret response %v", secretResp) - } + + utils.VerboseToStdErr("Azure Key Vault get unversioned secret response %v", secretResp) data[secret] = *secretResp.Value continue } @@ -77,16 +70,13 @@ func (a *AzureKeyVault) GetSecrets(kvpath string, version string, _ map[string]s } // Secret version matched given version if strings.Contains(*secretVersion.ID, version) { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Azure Key Vault getting secret %s from vault %s at version %s", secret, kvpath, version) - } + utils.VerboseToStdErr("Azure Key Vault getting secret %s from vault %s at version %s", secret, kvpath, version) secretResp, err := a.Client.GetSecret(ctx, kvpath, secret, version) if err != nil { return nil, err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Azure Key Vault get versioned secret response %v", secretResp) - } + + utils.VerboseToStdErr("Azure Key Vault get versioned secret response %v", secretResp) data[secret] = *secretResp.Value } } @@ -101,18 +91,16 @@ func (a *AzureKeyVault) GetSecrets(kvpath string, version string, _ map[string]s func (a *AzureKeyVault) GetIndividualSecret(kvpath, secret, version string, annotations map[string]string) (interface{}, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Azure Key Vault getting secret %s from vault %s at version %s", secret, kvpath, version) - } + + utils.VerboseToStdErr("Azure Key Vault getting secret %s from vault %s at version %s", secret, kvpath, version) kvpath = fmt.Sprintf("https://%s.vault.azure.net", kvpath) data, err := a.Client.GetSecret(ctx, kvpath, secret, version) if err != nil { return nil, err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Azure Key Vault get versioned secret response %v", data) - } + + utils.VerboseToStdErr("Azure Key Vault get versioned secret response %v", data) return *data.Value, nil } diff --git a/pkg/backends/gcpsecretmanager.go b/pkg/backends/gcpsecretmanager.go index 40ed2ba0..e18e97a7 100644 --- a/pkg/backends/gcpsecretmanager.go +++ b/pkg/backends/gcpsecretmanager.go @@ -8,7 +8,6 @@ import ( "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/googleapis/gax-go/v2" - "github.com/spf13/viper" secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1" ) @@ -52,16 +51,13 @@ func (a *GCPSecretManager) GetSecrets(path string, version string, annotations m Name: fmt.Sprintf("%s/versions/%s", path, version), } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("GCP Secret Manager accessing secret at path %s at version %v", path, version) - } + utils.VerboseToStdErr("GCP Secret Manager accessing secret at path %s at version %v", path, version) result, err := a.Client.AccessSecretVersion(a.Context, req) if err != nil { return nil, fmt.Errorf("Could not find secret: %v", err) } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("GCP Secret Manager access secret version response %v", result) - } + + utils.VerboseToStdErr("GCP Secret Manager access secret version response %v", result) data := make(map[string]interface{}) diff --git a/pkg/backends/ibmsecretsmanager.go b/pkg/backends/ibmsecretsmanager.go index 6a49902a..44af1af0 100644 --- a/pkg/backends/ibmsecretsmanager.go +++ b/pkg/backends/ibmsecretsmanager.go @@ -9,7 +9,6 @@ import ( ibmsm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv1" "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" - "github.com/spf13/viper" ) var IBMPath, _ = regexp.Compile(`ibmcloud/(?P.+)/secrets/groups/(?P.+)`) @@ -122,9 +121,7 @@ func (i *IBMSecretsManager) getSecretVersionedOrNot(secret *ibmsm.SecretResource return nil, fmt.Errorf("Could not retrieve secret %s after %d retries, statuscode %d", *secret.ID, types.IBMMaxRetries, httpResponse.GetStatusCode()) } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("IBM Cloud Secrets Manager get versioned secret %s HTTP response: %v", *secret.ID, httpResponse) - } + utils.VerboseToStdErr("IBM Cloud Secrets Manager get versioned secret %s HTTP response: %v", *secret.ID, httpResponse) result = (secretVersion.Resources[0].(*ibmsm.SecretVersion)).SecretData.(map[string]interface{}) } else { @@ -139,9 +136,7 @@ func (i *IBMSecretsManager) getSecretVersionedOrNot(secret *ibmsm.SecretResource return nil, fmt.Errorf("Could not retrieve secret %s after %d retries, statuscode %d", *secret.ID, types.IBMMaxRetries, httpResponse.GetStatusCode()) } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("IBM Cloud Secrets Manager get unversioned secret %s HTTP response: %v", *secret.ID, httpResponse) - } + utils.VerboseToStdErr("IBM Cloud Secrets Manager get unversioned secret %s HTTP response: %v", *secret.ID, httpResponse) // APIKey secrets don't come from `SecretData` if *secret.SecretType == types.IBMIAMCredentialsType { @@ -181,15 +176,10 @@ func (i *IBMSecretsManager) getSecret(secret *ibmsm.SecretResource, version stri // Bypass the cache when explicit version is requested if cacheResult != nil && version == "" { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("IBM Cloud Secrets Manager get secret: cache hit for %s of type %s from group %s", secretName, secretType, groupId) - } + utils.VerboseToStdErr("IBM Cloud Secrets Manager get secret: cache hit for %s of type %s from group %s", secretName, secretType, groupId) result["payload"] = cacheResult } else { - - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("IBM Cloud Secrets Manager get secret: getting secret %s of type %s from group %s", secretName, secretType, groupId) - } + utils.VerboseToStdErr("IBM Cloud Secrets Manager get secret: getting secret %s of type %s from group %s", secretName, secretType, groupId) secretData, err := i.getSecretVersionedOrNot(secret, version) var payload interface{} if err != nil { @@ -227,17 +217,13 @@ func (i *IBMSecretsManager) listSecretsInGroup(groupId, secretType string) (map[ ckey := cacheKey{groupId, secretType} cachedData := i.listAllSecretsCache[ckey] if cachedData != nil { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("IBM Cloud Secrets Manager list secrets in group: cache hit group %s", groupId) - } + utils.VerboseToStdErr("IBM Cloud Secrets Manager list secrets in group: cache hit group %s", groupId) return cachedData, nil } var offset int64 = 0 for { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("IBM Cloud Secrets Manager listing secrets of from group %s starting at offset %d", groupId, offset) - } + utils.VerboseToStdErr("IBM Cloud Secrets Manager listing secrets of from group %s starting at offset %d", groupId, offset) res, details, err := i.Client.ListAllSecrets(&ibmsm.ListAllSecretsOptions{ Groups: []string{groupId}, Offset: &offset, @@ -249,9 +235,7 @@ func (i *IBMSecretsManager) listSecretsInGroup(groupId, secretType string) (map[ return nil, fmt.Errorf("Could not list secrets for secret group %s: %d\n%s", groupId, details.GetStatusCode(), details.String()) } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("IBM Cloud Secrets Manager list secrets in group HTTP response: %v", details) - } + utils.VerboseToStdErr("IBM Cloud Secrets Manager list secrets in group HTTP response: %v", details) for _, secret := range res.Resources { name := *(secret.(*ibmsm.SecretResource).Name) diff --git a/pkg/backends/localsecretmanager.go b/pkg/backends/localsecretmanager.go index be48eeef..5fa15e42 100644 --- a/pkg/backends/localsecretmanager.go +++ b/pkg/backends/localsecretmanager.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" - "github.com/spf13/viper" "k8s.io/apimachinery/pkg/util/yaml" ) @@ -30,14 +29,10 @@ func (a *LocalSecretManager) Login() error { // GetSecrets gets secrets using decrypt function and returns the formatted data func (a *LocalSecretManager) GetSecrets(path string, version string, annotations map[string]string) (map[string]interface{}, error) { - - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Local secret manager getting secret %s at version %s", path, version) - } + utils.VerboseToStdErr("Local secret manager getting secret %s at version %s", path, version) cleartext, err := a.Decrypt(path, "yaml") - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Local secret manager get secret response: %v", cleartext) - } + + utils.VerboseToStdErr("Local secret manager get secret response: %v", cleartext) var dat map[string]interface{} diff --git a/pkg/backends/onepasswordconnect.go b/pkg/backends/onepasswordconnect.go index 15d22092..5b74bdfe 100644 --- a/pkg/backends/onepasswordconnect.go +++ b/pkg/backends/onepasswordconnect.go @@ -5,7 +5,6 @@ import ( "github.com/1Password/connect-sdk-go/connect" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" - "github.com/spf13/viper" ) // OnePassword is a struct for working with a 1Password Connect backend @@ -32,16 +31,13 @@ func (a *OnePasswordConnect) GetSecrets(path string, version string, annotations vaultUUID := splits[1] itemUUID := splits[3] - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("OnePassword Connect getting item %s from vault %s", itemUUID, vaultUUID) - } + utils.VerboseToStdErr("OnePassword Connect getting item %s from vault %s", itemUUID, vaultUUID) result, err := a.Client.GetItem(itemUUID, vaultUUID) if err != nil { return nil, err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("OnePassword Connect get secret response: %v", result) - } + + utils.VerboseToStdErr("OnePassword Connect get secret response: %v", result) data := make(map[string]interface{}) diff --git a/pkg/backends/vault.go b/pkg/backends/vault.go index 1e7a71c9..81d14cb2 100644 --- a/pkg/backends/vault.go +++ b/pkg/backends/vault.go @@ -7,7 +7,6 @@ import ( "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" "github.com/hashicorp/vault/api" - "github.com/spf13/viper" ) // Vault is a struct for working with a Vault backend @@ -49,25 +48,20 @@ func (v *Vault) GetSecrets(path string, version string, annotations map[string]s // Vault KV-V1 doesn't support versioning so we only honor `version` if KV-V2 is used if version != "" && kvVersion == "2" { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault getting kv pairs from KV-V2 path %s at version %s", path, version) - } + utils.VerboseToStdErr("Hashicorp Vault getting kv pairs from KV-V2 path %s at version %s", path, version) secret, err = v.VaultClient.Logical().ReadWithData(path, map[string][]string{ "version": {version}, }) } else { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault getting kv pairs from KV-V1 path %s", path) - } + utils.VerboseToStdErr("Hashicorp Vault getting kv pairs from KV-V1 path %s", path) secret, err = v.VaultClient.Logical().Read(path) } if err != nil { return nil, err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Hashicorp Vault get kv pairs response: %v", secret) - } + + utils.VerboseToStdErr("Hashicorp Vault get kv pairs response: %v", secret) if secret == nil { // Do not mention `version` in error message when it's not honored (KV-V1) diff --git a/pkg/backends/yandexcloudlockbox.go b/pkg/backends/yandexcloudlockbox.go index bd0de43c..7e07091d 100644 --- a/pkg/backends/yandexcloudlockbox.go +++ b/pkg/backends/yandexcloudlockbox.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" - "github.com/spf13/viper" "github.com/yandex-cloud/go-genproto/yandex/cloud/lockbox/v1" ) @@ -36,16 +35,13 @@ func (ycl *YandexCloudLockbox) GetSecrets(secretID string, version string, _ map req.SetVersionId(version) } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Yandex Cloud Lockbox getting secret %s at version %s", secretID, version) - } + utils.VerboseToStdErr("Yandex Cloud Lockbox getting secret %s at version %s", secretID, version) resp, err := ycl.client.Get(context.Background(), req) if err != nil { return nil, err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("Yandex Cloud Lockbox get secret response %v", resp) - } + + utils.VerboseToStdErr("Yandex Cloud Lockbox get secret response %v", resp) result := make(map[string]interface{}, len(resp.GetEntries())) for _, v := range resp.GetEntries() { diff --git a/pkg/config/config.go b/pkg/config/config.go index 52364688..16ac3f59 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "log" "os" "strconv" "strings" @@ -63,16 +62,12 @@ func New(v *viper.Viper, co *Options) (*Config, error) { } // Instantiate Env - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("reading configuration from environment, overriding any previous settings") - } + utils.VerboseToStdErr("reading configuration from environment, overriding any previous settings") v.AutomaticEnv() - if viper.GetBool("verboseOutput") { - log.Print("AVP configured with the following settings:\n") - for k, viperValue := range v.AllSettings() { - utils.VerboseToStdErr("%s: %s\n", k, viperValue) - } + utils.VerboseToStdErr("AVP configured with the following settings:\n") + for k, viperValue := range v.AllSettings() { + utils.VerboseToStdErr("%s: %s\n", k, viperValue) } authType := v.GetString(types.EnvAvpAuthType) @@ -140,9 +135,7 @@ func New(v *viper.Viper, co *Options) (*Config, error) { return nil, fmt.Errorf("%s or %s required for IBM Secrets Manager", types.EnvAvpIBMInstanceURL, types.EnvVaultAddress) } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("falling back to %s in place of %s", types.EnvVaultAddress, types.EnvAvpIBMInstanceURL) - } + utils.VerboseToStdErr("falling back to %s in place of %s", types.EnvVaultAddress, types.EnvAvpIBMInstanceURL) url = v.GetString(types.EnvVaultAddress) } @@ -154,9 +147,7 @@ func New(v *viper.Viper, co *Options) (*Config, error) { return nil, err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("IBM Cloud Secrets Manager enabling %d API call retries with %d seconds between tries", types.IBMMaxRetries, types.IBMRetryIntervalSeconds) - } + utils.VerboseToStdErr("IBM Cloud Secrets Manager enabling %d API call retries with %d seconds between tries", types.IBMMaxRetries, types.IBMRetryIntervalSeconds) client.EnableRetries(types.IBMMaxRetries, time.Duration(types.IBMRetryIntervalSeconds)*time.Second) backend = backends.NewIBMSecretsManagerBackend(client) @@ -164,9 +155,7 @@ func New(v *viper.Viper, co *Options) (*Config, error) { case types.AWSSecretsManagerbackend: { if !v.IsSet(types.EnvAWSRegion) { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("warning: %s env var not set, using AWS region %s", types.EnvAWSRegion, types.AwsDefaultRegion) - } + utils.VerboseToStdErr("warning: %s env var not set, using AWS region %s", types.EnvAWSRegion, types.AwsDefaultRegion) v.Set(types.EnvAWSRegion, types.AwsDefaultRegion) } @@ -254,9 +243,7 @@ func New(v *viper.Viper, co *Options) (*Config, error) { func readConfigOrSecret(secretName, configPath string, v *viper.Viper) error { // If a secret name is passed, pull config from Kubernetes if secretName != "" { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("reading configuration from secret %s", secretName) - } + utils.VerboseToStdErr("reading configuration from secret %s", secretName) localClient, err := kube.NewClient() if err != nil { @@ -272,9 +259,7 @@ func readConfigOrSecret(secretName, configPath string, v *viper.Viper) error { // If a config file path is passed, read in that file and overwrite all other if configPath != "" { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("reading configuration from config file %s, overriding any previous settings", configPath) - } + utils.VerboseToStdErr("reading configuration from config file %s, overriding any previous settings", configPath) v.SetConfigFile(configPath) err := v.ReadInConfig() @@ -294,6 +279,7 @@ func readConfigOrSecret(secretName, configPath string, v *viper.Viper) error { value = viperValue.(string) } os.Setenv(strings.ToUpper(k), value) + utils.VerboseToStdErr("Setting %s to %s for backend SDK", strings.ToUpper(k), value) } } } diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 30943714..f8b755c0 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" - "github.com/spf13/viper" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -37,9 +36,8 @@ func NewClient() (*Client, error) { // and returns a YAML []byte containing its data, decoded from base64 func (c *Client) ReadSecret(name string) ([]byte, error) { secretNamespace, secretName := secretNamespaceName(name) - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("parsed secret name as %s from namespace %s", secretName, secretNamespace) - } + + utils.VerboseToStdErr("parsed secret name as %s from namespace %s", secretName, secretNamespace) s, err := c.client.CoreV1().Secrets(secretNamespace).Get(context.TODO(), secretName, metav1.GetOptions{}) if err != nil { diff --git a/pkg/kube/template.go b/pkg/kube/template.go index d31f1803..ddd6a1b7 100644 --- a/pkg/kube/template.go +++ b/pkg/kube/template.go @@ -6,7 +6,6 @@ import ( "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" - "github.com/spf13/viper" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" yaml "sigs.k8s.io/yaml" ) @@ -40,9 +39,7 @@ func NewTemplate(template unstructured.Unstructured, backend types.Backend) (*Te return nil, err } - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("calling GetSecrets to get all secrets from backend because %s is set to %s", types.AVPPathAnnotation, path) - } + utils.VerboseToStdErr("calling GetSecrets to get all secrets from backend because %s is set to %s", types.AVPPathAnnotation, path) } return &Template{ diff --git a/pkg/kube/util.go b/pkg/kube/util.go index c0a49163..6905b6f2 100644 --- a/pkg/kube/util.go +++ b/pkg/kube/util.go @@ -12,7 +12,6 @@ import ( "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" - "github.com/spf13/viper" k8yaml "k8s.io/apimachinery/pkg/util/yaml" ) @@ -94,9 +93,7 @@ func replaceInner( } if removeKey { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("removing key %s due to %s being set on the containing manifest", key, types.AVPRemoveMissingAnnotation) - } + utils.VerboseToStdErr("removing key %s due to %s being set on the containing manifest", key, types.AVPRemoveMissingAnnotation) delete(obj, key) } else { obj[key] = replacement @@ -124,9 +121,7 @@ func genericReplacement(key, value string, resource Resource) (_ interface{}, er pipelineFields := strings.Split(placeholder, "|") placeholder = strings.Trim(pipelineFields[0], " ") - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("found placeholder %s with modifiers %s", placeholder, pipelineFields[1:]) - } + utils.VerboseToStdErr("found placeholder %s with modifiers %s", placeholder, pipelineFields[1:]) var secretValue interface{} var secretErr error @@ -138,9 +133,7 @@ func genericReplacement(key, value string, resource Resource) (_ interface{}, er key := indivSecretMatches[indivPlaceholderSyntax.SubexpIndex("key")] version := indivSecretMatches[indivPlaceholderSyntax.SubexpIndex("version")] - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("calling GetIndividualSecret for secret %s from path %s at version %s", key, path, version) - } + utils.VerboseToStdErr("calling GetIndividualSecret for secret %s from path %s at version %s", key, path, version) secretValue, secretErr = resource.Backend.GetIndividualSecret(path, strings.TrimSpace(key), version, resource.Annotations) if secretErr != nil { err = append(err, secretErr) @@ -156,9 +149,7 @@ func genericReplacement(key, value string, resource Resource) (_ interface{}, er fields := strings.Fields(stmt) functionName := strings.Trim(fields[0], " ") - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("processing modifier %s with args %q", functionName, fields) - } + utils.VerboseToStdErr("processing modifier %s with args %q", functionName, fields) if _, ok := modifiers[functionName]; !ok { e := fmt.Errorf("invalid modifier: %s for placeholder %s in string %s: %s", functionName, placeholder, key, value) @@ -199,9 +190,7 @@ func genericReplacement(key, value string, resource Resource) (_ interface{}, er // In the case where the value is a non-string, we insert it directly here. // Useful for cases like `replicas: ` if nonStringReplacement != nil { - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("value found in secret manager is non-string: %v, injecting directly into template") - } + utils.VerboseToStdErr("value found in secret manager is non-string: %v, injecting directly into template") return nonStringReplacement, err } @@ -215,9 +204,8 @@ func configReplacement(key, value string, resource Resource) (interface{}, []err } // configMap data values must be strings - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("key %s comes from ConfigMap manifest, stringifying value %s to fit", key, value) - } + + utils.VerboseToStdErr("key %s comes from ConfigMap manifest, stringifying value %s to fit", key, value) return stringify(res), err } @@ -226,9 +214,7 @@ func secretReplacement(key, value string, resource Resource) (interface{}, []err if err == nil && genericPlaceholder.Match(decoded) { res, err := genericReplacement(key, string(decoded), resource) - if viper.GetBool("verboseOutput") { - utils.VerboseToStdErr("key %s comes from Secret manifest, base64 encoding value %s to fit", key, value) - } + utils.VerboseToStdErr("key %s comes from Secret manifest, base64 encoding value %s to fit", key, value) return base64.StdEncoding.EncodeToString([]byte(stringify(res))), err } diff --git a/pkg/utils/util.go b/pkg/utils/util.go index 1d00352e..b6ab3985 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -12,6 +12,7 @@ import ( "time" "github.com/hashicorp/vault/api" + "github.com/spf13/viper" ) // CheckExistingToken takes a VaultType interface and logs in, while writting the config file @@ -109,5 +110,7 @@ func DefaultHttpClient() *http.Client { } func VerboseToStdErr(format string, message ...interface{}) { - log.Printf(fmt.Sprintf("%s\n", format), message...) + if viper.GetBool("verboseOutput") { + log.Printf(fmt.Sprintf("%s\n", format), message...) + } }