Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide --verbose option that does not print sensitive info #684

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import (
func NewGenerateCommand() *cobra.Command {
const StdIn = "-"
var configPath, secretName string
var verboseOutput bool
var verboseSafe bool
var verboseSensitive bool
var disableCache bool

var command = &cobra.Command{
Expand Down Expand Up @@ -63,8 +64,12 @@ func NewGenerateCommand() *cobra.Command {
}

v := viper.New()
viper.Set("verboseOutput", verboseOutput)
viper.Set("verbose", verboseSafe || verboseSensitive)
viper.Set("verboseRedact", verboseSafe && !verboseSensitive)
viper.Set("disableCache", disableCache)
if verboseSensitive {
utils.VerboseToStdErr("Running with --verbose-sensitive-output. Sensitive information will be printed to standard error!")
}
cmdConfig, err := config.New(v, &config.Options{
SecretName: secretName,
ConfigPath: configPath,
Expand Down Expand Up @@ -117,7 +122,8 @@ 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(&verboseSafe, "verbose", false, "enable verbose mode for detailed info to help with debugging. Omits sensitive data (credentials), logged to stderr")
command.Flags().BoolVar(&verboseSensitive, "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
}
58 changes: 58 additions & 0 deletions cmd/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"bytes"
"fmt"
"github.com/stretchr/testify/assert"
"io"
"os"
"strings"
Expand Down Expand Up @@ -308,3 +309,60 @@ func TestMain(t *testing.T) {
os.Unsetenv("VAULT_SKIP_VERIFY")
os.Unsetenv("AVP_PATH_VALIDATION")
}

func TestVerboseness(t *testing.T) {
cluster, roleid, secretid = helpers.CreateTestAppRoleVault(t)
os.Setenv("AVP_TYPE", "vault")
os.Setenv("VAULT_ADDR", cluster.Cores[0].Client.Address())
os.Setenv("AVP_AUTH_TYPE", "approle")
os.Setenv("AVP_SECRET_ID", "broken_but_secret")
os.Setenv("AVP_ROLE_ID", "broken_but_secret")
os.Setenv("VAULT_SKIP_VERIFY", "true")

t.Run("Quiet", func(t *testing.T) {
cmd := NewGenerateCommand()
cmd.SetArgs([]string{"../fixtures/input/nonempty/secret_path.yaml"})
cmd.SetOut(bytes.NewBufferString(""))
cmd.SetErr(bytes.NewBufferString(""))
logOut := helpers.CaptureOutput(func() {
cmd.Execute()
})

assert.Equal(t, "", logOut)
})

t.Run("Safe verbose", func(t *testing.T) {
cmd := NewGenerateCommand()
cmd.SetArgs([]string{"../fixtures/input/nonempty/secret_path.yaml", "--verbose"})
cmd.SetOut(bytes.NewBufferString(""))
cmd.SetErr(bytes.NewBufferString(""))
logOut := helpers.CaptureOutput(func() {
cmd.Execute()
})

assert.Contains(t, logOut, "Hashicorp Vault authenticating with role ID ***REDACTED(17 characters)*** and secret ID ***REDACTED(17 characters)*** at path auth/approle")
assert.NotContains(t, logOut, "broken_but_secret")
})

t.Run("Sensitive verbose", func(t *testing.T) {
cmd := NewGenerateCommand()
cmd.SetArgs([]string{"../fixtures/input/nonempty/secret_path.yaml", "--verbose-sensitive-output"})
cmd.SetOut(bytes.NewBufferString(""))
cmd.SetErr(bytes.NewBufferString(""))
logOut := helpers.CaptureOutput(func() {
cmd.Execute()
})

assert.Contains(t, logOut, "Running with --verbose-sensitive-output. Sensitive information will be printed to standard error!")
assert.Contains(t, logOut, "Hashicorp Vault authenticating with role ID broken_but_secret and secret ID broken_but_secret at path auth/approle")
assert.NotContains(t, logOut, "***REDACTED")
})

os.Unsetenv("AVP_TYPE")
os.Unsetenv("VAULT_ADDR")
os.Unsetenv("AVP_AUTH_TYPE")
os.Unsetenv("AVP_SECRET_ID")
os.Unsetenv("AVP_ROLE_ID")
os.Unsetenv("VAULT_SKIP_VERIFY")
os.Unsetenv("AVP_PATH_VALIDATION")
}
11 changes: 7 additions & 4 deletions pkg/auth/vault/approle.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func NewAppRoleAuth(roleID, secretID, mountPath string) *AppRoleAuth {
func (a *AppRoleAuth) Authenticate(vaultClient *api.Client) error {
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)
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", utils.SanitizeUnsafe(err))
} else {
return nil
}
Expand All @@ -45,18 +45,21 @@ func (a *AppRoleAuth) Authenticate(vaultClient *api.Client) error {
"secret_id": a.SecretID,
}

utils.VerboseToStdErr("Hashicorp Vault authenticating with role ID %s and secret ID %s at path %s", a.RoleID, a.SecretID, a.MountPath)
utils.VerboseToStdErr(
"Hashicorp Vault authenticating with role ID %s and secret ID %s at path %s",
utils.SanitizeUnsafe(a.RoleID), utils.SanitizeUnsafe(a.SecretID), a.MountPath,
)
data, err := vaultClient.Logical().Write(fmt.Sprintf("%s/login", a.MountPath), payload)
if err != nil {
return err
}

utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data)
utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", utils.SanitizeUnsafe(data))

// If we cannot write the Vault token, we'll just have to login next time. Nothing showstopping.
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)
utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", utils.SanitizeUnsafe(err))
}

return nil
Expand Down
8 changes: 4 additions & 4 deletions pkg/auth/vault/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func NewGithubAuth(token, mountPath string) *GithubAuth {
func (g *GithubAuth) Authenticate(vaultClient *api.Client) error {
err := utils.LoginWithCachedToken(vaultClient, "github")
if err != nil {
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", err)
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", utils.SanitizeUnsafe(err))
} else {
return nil
}
Expand All @@ -43,18 +43,18 @@ func (g *GithubAuth) Authenticate(vaultClient *api.Client) error {
"token": g.AccessToken,
}

utils.VerboseToStdErr("Hashicorp Vault authenticating with Github token %s", g.AccessToken)
utils.VerboseToStdErr("Hashicorp Vault authenticating with Github token %s", utils.SanitizeUnsafe(g.AccessToken))
data, err := vaultClient.Logical().Write(fmt.Sprintf("%s/login", g.MountPath), payload)
if err != nil {
return err
}

utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data)
utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", utils.SanitizeUnsafe(data))

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

return nil
Expand Down
11 changes: 7 additions & 4 deletions pkg/auth/vault/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func NewK8sAuth(role, mountPath, tokenPath string) *K8sAuth {
func (k *K8sAuth) Authenticate(vaultClient *api.Client) error {
err := utils.LoginWithCachedToken(vaultClient, "kubernetes")
if err != nil {
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", err)
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", utils.SanitizeUnsafe(err))
} else {
return nil
}
Expand All @@ -61,18 +61,21 @@ func (k *K8sAuth) Authenticate(vaultClient *api.Client) error {
kubeAuthPath = k.MountPath
}

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, utils.SanitizeUnsafe(token),
)
data, err := vaultClient.Logical().Write(fmt.Sprintf("%s/login", kubeAuthPath), payload)
if err != nil {
return err
}

utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data)
utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", utils.SanitizeUnsafe(data))

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

return nil
Expand Down
11 changes: 7 additions & 4 deletions pkg/auth/vault/userpass.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func NewUserPassAuth(username, password, mountPath string) *UserPassAuth {
func (a *UserPassAuth) Authenticate(vaultClient *api.Client) error {
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)
utils.VerboseToStdErr("Hashicorp Vault cannot retrieve cached token: %v. Generating a new one", utils.SanitizeUnsafe(err))
} else {
return nil
}
Expand All @@ -44,17 +44,20 @@ func (a *UserPassAuth) Authenticate(vaultClient *api.Client) error {
"password": a.Password,
}

utils.VerboseToStdErr("Hashicorp Vault authenticating with username %s and password %s", a.Username, a.Password)
utils.VerboseToStdErr(
"Hashicorp Vault authenticating with username %s and password %s",
utils.SanitizeUnsafe(a.Username), utils.SanitizeUnsafe(a.Password),
)
data, err := vaultClient.Logical().Write(fmt.Sprintf("%s/login/%s", a.MountPath, a.Username), payload)
if err != nil {
return err
}

utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", data)
utils.VerboseToStdErr("Hashicorp Vault authentication response: %v", utils.SanitizeUnsafe(data))

// If we cannot write the Vault token, we'll just have to login next time. Nothing showstopping.
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)
utils.VerboseToStdErr("Hashicorp Vault cannot cache token for future runs: %v", utils.SanitizeUnsafe(err))
}

return nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/backends/awssecretsmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (a *AWSSecretsManager) GetSecrets(path string, version string, annotations
return nil, err
}

utils.VerboseToStdErr("AWS Secrets Manager get secret response %v", result)
utils.VerboseToStdErr("AWS Secrets Manager get secret response %v", utils.SanitizeUnsafe(result))

var dat map[string]interface{}

Expand Down
8 changes: 4 additions & 4 deletions pkg/backends/azurekeyvault.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,16 @@ func (a *AzureKeyVault) GetSecrets(kvpath string, version string, _ map[string]s
if err != nil {
return nil, err
}
utils.VerboseToStdErr("Azure Key Vault get secret response %v", secret)
utils.VerboseToStdErr("Azure Key Vault get secret response %v", utils.SanitizeUnsafe(secret))
data[name] = *secret.Value
} else {
verboseOptionalVersion("Azure Key Vault getting secret %s from vault %s", version, name, kvpath)
secret, err := client.GetSecret(ctx, name, version, nil)
if err != nil || !*secretVersion.Attributes.Enabled {
utils.VerboseToStdErr("Azure Key Vault get versioned secret not found %s", err)
utils.VerboseToStdErr("Azure Key Vault get versioned secret not found %s", utils.SanitizeUnsafe(err))
continue
}
utils.VerboseToStdErr("Azure Key Vault get versioned secret response %v", secret)
utils.VerboseToStdErr("Azure Key Vault get versioned secret response %v", utils.SanitizeUnsafe(secret))
data[name] = *secret.Value
}
}
Expand Down Expand Up @@ -110,7 +110,7 @@ func (a *AzureKeyVault) GetIndividualSecret(kvpath, secret, version string, anno
return nil, err
}

utils.VerboseToStdErr("Azure Key Vault get individual secret response %v", data)
utils.VerboseToStdErr("Azure Key Vault get individual secret response %v", utils.SanitizeUnsafe(data))

return *data.Value, nil
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/backends/delineasecretsmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (a *DelineaSecretServer) GetSecrets(path string, version string, annotation
return nil, fmt.Errorf("could not decode secret json %s", secret_json)
}

utils.VerboseToStdErr("Delinea Secret Server decoding json %s", secret)
utils.VerboseToStdErr("Delinea Secret Server decoding json %s", utils.SanitizeUnsafe(secret))

secret_map := make(map[string]interface{})

Expand All @@ -69,7 +69,7 @@ func (a *DelineaSecretServer) GetSecrets(path string, version string, annotation
secret_map[secret.Fields[index].Slug] = secret.Fields[index].ItemValue
}

utils.VerboseToStdErr("Delinea Secret Server constructed map %s", secret_map)
utils.VerboseToStdErr("Delinea Secret Server constructed map %s", utils.SanitizeUnsafe(secret_map))
return secret_map, nil

}
Expand Down
2 changes: 1 addition & 1 deletion pkg/backends/gcpsecretmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (a *GCPSecretManager) GetSecrets(path string, version string, annotations m
return nil, fmt.Errorf("Could not find secret: %v", err)
}

utils.VerboseToStdErr("GCP Secret Manager access secret version response %v", result)
utils.VerboseToStdErr("GCP Secret Manager access secret version response %v", utils.SanitizeUnsafe(result))

data := make(map[string]interface{})

Expand Down
8 changes: 4 additions & 4 deletions pkg/backends/ibmsecretsmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ func (i *IBMSecretsManager) getSecretVersionedOrNot(id, stype, version string) (
return nil, fmt.Errorf("Could not retrieve secret %s after %d retries, statuscode %d", id, types.IBMMaxRetries, httpResponse.GetStatusCode())
}

utils.VerboseToStdErr("IBM Cloud Secrets Manager get versioned secret %s HTTP response: %v", id, httpResponse)
utils.VerboseToStdErr("IBM Cloud Secrets Manager get versioned secret %s HTTP response: %v", id, utils.SanitizeUnsafe(httpResponse))

result, err = NewIBMVersionedSecretData(secretVersion).GetSecret()
if err != nil {
Expand All @@ -402,7 +402,7 @@ func (i *IBMSecretsManager) getSecretVersionedOrNot(id, stype, version string) (
return nil, fmt.Errorf("Could not retrieve secret %s after %d retries, statuscode %d", id, types.IBMMaxRetries, httpResponse.GetStatusCode())
}

utils.VerboseToStdErr("IBM Cloud Secrets Manager get unversioned secret %s HTTP response: %v", id, httpResponse)
utils.VerboseToStdErr("IBM Cloud Secrets Manager get unversioned secret %s HTTP response: %v", id, utils.SanitizeUnsafe(httpResponse))

result, err = NewIBMSecretData(secretRes).GetSecret()
if err != nil {
Expand Down Expand Up @@ -500,15 +500,15 @@ 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())
}

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", utils.SanitizeUnsafe(details))

for _, secret := range res.Secrets {
var name, ttype string
v := NewIBMSecretMetadata(secret)

data, err := v.GetMetadata()
if err != nil {
utils.VerboseToStdErr("Skipping a secret in group %s: %s", groupId, err)
utils.VerboseToStdErr("Skipping a secret in group %s: %s", groupId, utils.SanitizeUnsafe(err))
}

name = data["name"]
Expand Down
2 changes: 1 addition & 1 deletion pkg/backends/keepersecretsmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func (a *KeeperSecretsManager) GetSecrets(path string, version string, annotatio
}
}

utils.VerboseToStdErr("Keeper Secrets Manager constructed map %s", secretMap)
utils.VerboseToStdErr("Keeper Secrets Manager constructed map %s", utils.SanitizeUnsafe(secretMap))

return secretMap, nil

Expand Down
4 changes: 2 additions & 2 deletions pkg/backends/kubernetessecret.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ func (k *KubernetesSecret) GetSecrets(path string, version string, annotations m
out[k] = string(v)
}

utils.VerboseToStdErr("K8s Secret get secret response: %v", out)
utils.VerboseToStdErr("K8s Secret get secret response: %v", utils.SanitizeUnsafe(out))
return out, nil
}

// GetIndividualSecret will get the specific secret (placeholder) from the Kubernetes Secret backend
// Kubernetes Secrets can only be wholly read,
// So, we use GetSecrets and extract the specific placeholder we want
func (k *KubernetesSecret) GetIndividualSecret(path, secret, version string, annotations map[string]string) (interface{}, error) {
utils.VerboseToStdErr("K8s Secret getting secret %s and key %s", path, secret)
utils.VerboseToStdErr("K8s Secret getting secret %s and key %s", path, utils.SanitizeUnsafe(secret))
data, err := k.GetSecrets(path, version, annotations)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion pkg/backends/localsecretmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (a *LocalSecretManager) GetSecrets(path string, version string, annotations
utils.VerboseToStdErr("Local secret manager getting secret %s at version %s", path, version)
cleartext, err := a.Decrypt(path, "yaml")

utils.VerboseToStdErr("Local secret manager get secret response: %v", cleartext)
utils.VerboseToStdErr("Local secret manager get secret response: %v", utils.SanitizeUnsafe(cleartext))

var dat map[string]interface{}

Expand Down
2 changes: 1 addition & 1 deletion pkg/backends/onepasswordconnect.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (a *OnePasswordConnect) GetSecrets(path string, version string, annotations
return nil, err
}

utils.VerboseToStdErr("OnePassword Connect get secret response: %v", result)
utils.VerboseToStdErr("OnePassword Connect get secret response: %v", utils.SanitizeUnsafe(result))

data := make(map[string]interface{})

Expand Down
2 changes: 1 addition & 1 deletion pkg/backends/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (v *Vault) GetSecrets(path string, version string, annotations map[string]s
return nil, err
}

utils.VerboseToStdErr("Hashicorp Vault get kv pairs response: %v", secret)
utils.VerboseToStdErr("Hashicorp Vault get kv pairs response: %v", utils.SanitizeUnsafe(secret))

if secret == nil {
// Do not mention `version` in error message when it's not honored (KV-V1)
Expand Down
2 changes: 1 addition & 1 deletion pkg/backends/yandexcloudlockbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (ycl *YandexCloudLockbox) GetSecrets(secretID string, version string, _ map
return nil, err
}

utils.VerboseToStdErr("Yandex Cloud Lockbox get secret response %v", resp)
utils.VerboseToStdErr("Yandex Cloud Lockbox get secret response %v", utils.SanitizeUnsafe(resp))

result := make(map[string]interface{}, len(resp.GetEntries()))
for _, v := range resp.GetEntries() {
Expand Down
Loading