diff --git a/deploy/dev/config/k8s/secrets-provider-init-container.sh.yml b/deploy/dev/config/k8s/secrets-provider-init-container.sh.yml index f442e852..408ab2c5 100755 --- a/deploy/dev/config/k8s/secrets-provider-init-container.sh.yml +++ b/deploy/dev/config/k8s/secrets-provider-init-container.sh.yml @@ -1,6 +1,8 @@ #!/bin/bash set -euo pipefail +CONJUR_AUTHN_LOGIN=${CONJUR_AUTHN_LOGIN:-"host/conjur/authn-k8s/${AUTHENTICATOR_ID}/apps/${APP_NAMESPACE_NAME}/*/*"} + cat << EOL --- apiVersion: apps/v1 @@ -111,7 +113,7 @@ spec: value: "debug" - name: CONJUR_AUTHN_LOGIN - value: "host/conjur/authn-k8s/${AUTHENTICATOR_ID}/apps/${APP_NAMESPACE_NAME}/*/*" + value: ${CONJUR_AUTHN_LOGIN} imagePullSecrets: - name: dockerpullsecret diff --git a/e2e/k8s_test.go b/e2e/k8s_test.go index 8d523099..e01bc03d 100644 --- a/e2e/k8s_test.go +++ b/e2e/k8s_test.go @@ -4,12 +4,14 @@ package e2e import ( + "os" + "fmt" "bytes" "context" - "encoding/base64" - "fmt" - "math/rand" + "os/exec" "testing" + "math/rand" + "encoding/base64" "github.com/stretchr/testify/assert" "sigs.k8s.io/e2e-framework/pkg/envconf" @@ -18,6 +20,7 @@ import ( func TestSecretsProvidedK8s(t *testing.T) { f := features.New("secrets provided (K8s secrets mode)"). + // Replaces TEST_ID_1.1_providing_ssh_keys_successfully Assess("ssh key set correctly in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { var stdout, stderr bytes.Buffer command := []string{"printenv", "|", "grep", "SSH_KEY"} @@ -28,6 +31,7 @@ func TestSecretsProvidedK8s(t *testing.T) { return ctx }). + // Replaces TEST_ID_1.2_providing_json_object_secret_successfully Assess("json object secret set correctly in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { var stdout, stderr bytes.Buffer command := []string{"printenv", "|", "grep", "JSON_OBJECT_SECRET"} @@ -38,6 +42,7 @@ func TestSecretsProvidedK8s(t *testing.T) { return ctx }). + // Replaces TEST_ID_1.3_providing_variables_with_spaces_successfully Assess("variables with spaces secret set correctly in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { var stdout, stderr bytes.Buffer command := []string{"printenv", "|", "grep", "VARIABLE_WITH_SPACES_SECRET"} @@ -48,6 +53,7 @@ func TestSecretsProvidedK8s(t *testing.T) { return ctx }). + // Replaces TEST_ID_1.4_providing_variables_with_pluses_successfully Assess("variables with pluses secret set correctly in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { var stdout, stderr bytes.Buffer command := []string{"printenv", "|", "grep", "VARIABLE_WITH_PLUSES_SECRET"} @@ -58,6 +64,7 @@ func TestSecretsProvidedK8s(t *testing.T) { return ctx }). + // Replaces TEST_ID_1.5_providing_variables_with_german_umlaut_successfully Assess("umlaut secret set correctly in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { var stdout, stderr bytes.Buffer @@ -69,6 +76,7 @@ func TestSecretsProvidedK8s(t *testing.T) { return ctx }). + // Replaces TEST_ID_1.6_providing_variables_with_base64_decoding_successfully Assess("variables with base64 decoding secret set correctly in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { var stdout, stderr bytes.Buffer command := []string{"printenv", "|", "grep", "VARIABLE_WITH_BASE64_SECRET"} @@ -77,6 +85,17 @@ func TestSecretsProvidedK8s(t *testing.T) { assert.Contains(t, stdout.String(), "secret-value") + return ctx + }). + // Replaces TEST_ID_16_non_conjur_keys_stay_intact_in_k8s_secret + Assess("non conjur keys stay intact secret set correctly in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + var stdout, stderr bytes.Buffer + command := []string{"printenv", "|", "grep", "NON_CONJUR_SECRET"} + + RunCommandInSecretsProviderPod(cfg.Client(), command, &stdout, &stderr) + + assert.Contains(t, stdout.String(), "some-value") + return ctx }) @@ -108,6 +127,7 @@ func TestLargeDecodedVariableSecretProvidedK8s(t *testing.T) { return context.WithValue(ctx, "expected", string(str)) }). + // Replaces TEST_ID_1.7_providing_large_decoded_variable_successfully Assess("large decoded variable secret set correctly in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { // check environment variable for expected value (the secret before encoding) var stdout, stderr bytes.Buffer @@ -132,3 +152,238 @@ func TestLargeDecodedVariableSecretProvidedK8s(t *testing.T) { testenv.Test(t, f.Feature()) } + +func TestMultiplePodsChangingPwdInbetweenSecretProvidedK8s(t *testing.T) { + f := features.New("multiple pods changing pwd inbetween"). + // Replaces TEST_ID_2_multiple_pods_changing_pwd_inbetween + Assess("secret set correctly in pod1", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + var stdout, stderr bytes.Buffer + command := []string{"printenv", "|", "grep", "TEST_SECRET"} + + RunCommandInSecretsProviderPod(cfg.Client(), command, &stdout, &stderr) + + assert.Contains(t, stdout.String(), "supersecret") + + return ctx + }). + Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + platform := os.Getenv("PLATFORM") + + if platform == "kubernetes" { + err := SetConjurSecret(cfg.Client(), "secrets/test_secret", "secret2") + if err != nil { + fmt.Errorf("error setting conjur secret: %s", err) + } + } else if platform == "openshift" { + fmt.Println("else statement") + err := SetConjurSecret(cfg.Client(), "TEST_SECRET", "secret2") + if err != nil { + fmt.Errorf("error setting conjur secret: %s", err) + } + } + + err := SetConjurSecret(cfg.Client(), "secrets/test_secret", "secret2") + if err != nil { + fmt.Errorf("error setting conjur secret: %s", err) + } + + err = ReloadWithTemplate(cfg.Client(), K8sTemplate) + if err != nil { + fmt.Errorf("error reloading secrets provider pod: %s", err) + } + + return ctx + }). + Assess("secret set correctly in pod2", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + var stdout, stderr bytes.Buffer + command := []string{"printenv", "|", "grep", "TEST_SECRET"} + + RunCommandInSecretsProviderPod(cfg.Client(), command, &stdout, &stderr) + + assert.Contains(t, stdout.String(), "secret2") + + return ctx + }). + Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + err := SetConjurSecret(cfg.Client(), "secrets/test_secret", "secret3") + if err != nil { + fmt.Errorf("error setting conjur secret: %s", err) + } + + err = ReloadWithTemplate(cfg.Client(), K8sTemplate) + if err != nil { + fmt.Errorf("error reloading secrets provider pod: %s", err) + } + + return ctx + }). + Assess("secret set correctly in pod3", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + var stdout, stderr bytes.Buffer + command := []string{"printenv", "|", "grep", "TEST_SECRET"} + + RunCommandInSecretsProviderPod(cfg.Client(), command, &stdout, &stderr) + + assert.Contains(t, stdout.String(), "secret3") + + return ctx + }). + Teardown(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + // reset the secret value in Conjur + err := SetConjurSecret(cfg.Client(), "secrets/test_secret", "supersecret") + if err != nil { + fmt.Errorf("error setting conjur secret: %s", err) + } + return ctx + }) + + testenv.Test(t, f.Feature()) +} + +func TestNoPermissionSecretProvidedK8s(t *testing.T) { + f := features.New("no permission to view conjur secrets"). + Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + // set login configuration in local environment + authenticatorID := os.Getenv("AUTHENTICATOR_ID") + appNamespaceName := os.Getenv("APP_NAMESPACE_NAME") + + loginURI := fmt.Sprintf("host/conjur/authn-k8s/%s/apps/%s/service_account/%s-sa", authenticatorID, appNamespaceName, appNamespaceName) + + os.Setenv("CONJUR_AUTHN_LOGIN", loginURI) + + // In this case the pod will fail to start due to the expected error in Secrets Provider. + // Currently ReloadWithTemplate has a built-in 1 minute timeout for the pod to be 'Ready' + // We may be able to shorten or omit this timeout to speed up the test. + err := ReloadWithTemplate(cfg.Client(), K8sTemplate) + if err != nil { + fmt.Errorf("error reloading secrets provider: %s", err) + } + return ctx + }). + // Replaces TEST_ID_12_no_conjur_secrets_permission + Assess("no permission to view conjur secrets in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + spPod, err := FetchPodWithLabelSelector(cfg.Client(), SecretsProviderNamespace(), SecretsProviderLabelSelector) + + getPodLogsCommand := exec.Command("kubectl", "logs", spPod.Name, "-c", "cyberark-secrets-provider-for-k8s") + podLogs, err := getPodLogsCommand.CombinedOutput() + if err != nil { + fmt.Errorf("error getting pod logs output: %s", err) + } + + assert.Contains(t, string(podLogs), "CSPFK034E") // for reference, a successful conjur secrets update: CSPFK009I + + return ctx + }). + Teardown(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + os.Unsetenv("CONJUR_AUTHN_LOGIN") + return ctx + }) + + testenv.Test(t, f.Feature()) +} + +func TestHostNotInAppsSecretProvidedK8s(t *testing.T) { + f := features.New("host not in apps secret provided"). + Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + // set login configuration in local environment + appNamespaceName := os.Getenv("APP_NAMESPACE_NAME") + + loginURI := fmt.Sprintf("host/some-apps/%s/*/*", appNamespaceName) + + os.Setenv("CONJUR_AUTHN_LOGIN", loginURI) + + // reload template with new login configuration + err := ReloadWithTemplate(cfg.Client(), K8sTemplate) + if err != nil { + fmt.Errorf("error reloading secrets provider: %s", err) + } + return ctx + }). + // Replaces TEST_ID_13_host_not_in_apps + Assess("host not in apps secret set correctly in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + // expect pod has conjur secret + var stdout, stderr bytes.Buffer + command := []string{"printenv", "|", "grep", "TEST_SECRET"} + + RunCommandInSecretsProviderPod(cfg.Client(), command, &stdout, &stderr) + + assert.Contains(t, stdout.String(), "supersecret") + + return ctx + }). + Teardown(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + os.Unsetenv("CONJUR_AUTHN_LOGIN") + return ctx + }) + + testenv.Test(t, f.Feature()) +} + +func TestHostInRootPolicySecretProvidedK8s(t *testing.T) { + f := features.New("host in root policy secret provided"). + Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + // set login configuration in local environment + loginURI := fmt.Sprintf("host/%s/*/*", os.Getenv("APP_NAMESPACE_NAME")) + + os.Setenv("CONJUR_AUTHN_LOGIN", loginURI) + + // reload template with new login configuration + err := ReloadWithTemplate(cfg.Client(), K8sTemplate) + if err != nil { + fmt.Errorf("error reloading secrets provider: %s", err) + } + return ctx + }). + // Replaces TEST_ID_14_host_in_root_policy + Assess("host in root policy secret set correctly in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + // expect pod has conjur secret + var stdout, stderr bytes.Buffer + command := []string{"printenv", "|", "grep", "TEST_SECRET"} + + RunCommandInSecretsProviderPod(cfg.Client(), command, &stdout, &stderr) + + assert.Contains(t, stdout.String(), "supersecret") + + return ctx + }). + Teardown(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + os.Unsetenv("CONJUR_AUTHN_LOGIN") + return ctx + }) + + testenv.Test(t, f.Feature()) +} + +func TestHostWithApplicationIdentityInAnnotationsSecretProvidedK8s(t *testing.T) { + f := features.New("host with application identity in annotations secret provided"). + Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + // set login configuration in local environment + loginURI := "host/some-apps/annotations-app" + + os.Setenv("CONJUR_AUTHN_LOGIN", loginURI) + + // reload template with new login configuration + err := ReloadWithTemplate(cfg.Client(), K8sTemplate) + if err != nil { + fmt.Errorf("error reloading secrets provider: %s", err) + } + return ctx + }). + // Replaces TEST_ID_15_host_with_application_identity_in_annotations + Assess("host with application identity in annotations secret set correctly in pod", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + // expect pod has conjur secret + var stdout, stderr bytes.Buffer + command := []string{"printenv", "|", "grep", "TEST_SECRET"} + + RunCommandInSecretsProviderPod(cfg.Client(), command, &stdout, &stderr) + + assert.Contains(t, stdout.String(), "supersecret") + + return ctx + }). + Teardown(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + os.Unsetenv("CONJUR_AUTHN_LOGIN") + return ctx + }) + + testenv.Test(t, f.Feature()) +} diff --git a/go.mod b/go.mod index 1ff609e7..f42cb978 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/vladimirvivien/gexe v0.2.0 // indirect github.com/zalando/go-keyring v0.2.3-0.20230503081219-17db2e5354bd // indirect go.opentelemetry.io/otel/metric v1.16.0 // indirect sigs.k8s.io/controller-runtime v0.14.5 // indirect diff --git a/go.sum b/go.sum index 4c76aef9..1d170df3 100644 --- a/go.sum +++ b/go.sum @@ -53,8 +53,8 @@ github.com/cyberark/conjur-api-go v0.11.1 h1:vjaMkw0geJsA+ikMM6UDLg4VLFQWKo/B0i9 github.com/cyberark/conjur-api-go v0.11.1/go.mod h1:n1p46Hj9l8wkZjM17cVYdfcatyPboWyioLGlC0QszCs= github.com/cyberark/conjur-authn-k8s-client v0.26.0 h1:iU+bstEsiCOTTp5dJxnbp2xcishacTM5iBRXFarxKsQ= github.com/cyberark/conjur-authn-k8s-client v0.26.0/go.mod h1:biD4y/VHqPWBzUk/nLoflQ/87f/RAJF4QJnuboKJHOY= -github.com/cyberark/conjur-opentelemetry-tracer v0.0.1-1240 h1:iuvxDQEwpvG2Gr71bpkyACjGIWFj2BGlc7smRs8ZpUg= -github.com/cyberark/conjur-opentelemetry-tracer v0.0.1-1240/go.mod h1:7pNmWZ/E4jv5cJuFWuXbzHpX6T44JfAPfciTWVxR9Ec= +github.com/cyberark/conjur-opentelemetry-tracer v0.0.1-1254 h1:clon61za2H42N1+WKSyKzEBFyDaUnJcrFZ76U1HEjPM= +github.com/cyberark/conjur-opentelemetry-tracer v0.0.1-1254/go.mod h1:7pNmWZ/E4jv5cJuFWuXbzHpX6T44JfAPfciTWVxR9Ec= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=