From b9b6a45d0fa125a97df6a9a857a454cdaac5d75f Mon Sep 17 00:00:00 2001 From: Anish Ramasekar Date: Wed, 26 Jan 2022 21:36:22 +0000 Subject: [PATCH] test: add e2e test with e2e-provider and add helm config Signed-off-by: Anish Ramasekar --- Makefile | 4 +- .../charts/secrets-store-csi-driver/README.md | 155 +++++++++--------- .../templates/csidriver.yaml | 4 + .../secrets-store-csi-driver/values.yaml | 6 + test/bats/e2e-provider.bats | 24 ++- test/bats/helpers.bash | 9 + test/e2eprovider/e2e_provider.go | 1 + test/e2eprovider/server/server.go | 49 +++++- test/e2eprovider/server/server_test.go | 30 ++++ 9 files changed, 201 insertions(+), 81 deletions(-) diff --git a/Makefile b/Makefile index 920de65b6..e9a816687 100644 --- a/Makefile +++ b/Makefile @@ -427,7 +427,9 @@ e2e-helm-deploy: --set linux.enabled=true \ --set syncSecret.enabled=true \ --set enableSecretRotation=true \ - --set rotationPollInterval=30s + --set rotationPollInterval=30s \ + --set tokenRequests[0].audience="aud1" \ + --set tokenRequests[1].audience="aud2" .PHONY: e2e-helm-upgrade e2e-helm-upgrade: diff --git a/manifest_staging/charts/secrets-store-csi-driver/README.md b/manifest_staging/charts/secrets-store-csi-driver/README.md index 08070fcff..83a542421 100644 --- a/manifest_staging/charts/secrets-store-csi-driver/README.md +++ b/manifest_staging/charts/secrets-store-csi-driver/README.md @@ -41,80 +41,81 @@ helm install csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver The following table lists the configurable parameters of the csi-secrets-store-provider-azure chart and their default values. -| Parameter | Description | Default | -| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | -| `nameOverride` | String to partially override secrets-store-csi-driver.fullname template with a string (will prepend the release name) | `""` | -| `fullnameOverride` | String to fully override secrets-store-csi-driver.fullname template with a string | `""` | -| `linux.image.repository` | Linux image repository | `k8s.gcr.io/csi-secrets-store/driver` | -| `linux.image.pullPolicy` | Linux image pull policy | `IfNotPresent` | -| `linux.image.tag` | Linux image tag | `v1.0.1` | -| `linux.crds.image.repository` | Linux crds image repository | `k8s.gcr.io/csi-secrets-store/driver-crds` | -| `linux.crds.image.pullPolicy` | Linux crds image pull policy | `IfNotPresent` | -| `linux.crds.image.tag` | Linux crds image tag | `v1.0.1` | -| `linux.affinity` | Linux affinity | `key: type; operator: NotIn; values: [virtual-kubelet]` | -| `linux.driver.resources` | The resource request/limits for the linux secrets-store container image | `limits: 200m CPU, 200Mi; requests: 50m CPU, 100Mi` | -| `linux.enabled` | Install secrets store csi driver on linux nodes | true | -| `linux.kubeletRootDir` | Configure the kubelet root dir | `/var/lib/kubelet` | -| `linux.providersDir` | Configure the providers root dir | `/etc/kubernetes/secrets-store-csi-providers` | -| `linux.nodeSelector` | Node Selector for the daemonset on linux nodes | `{}` | -| `linux.tolerations` | Tolerations for the daemonset on linux nodes | `[]` | -| `linux.metricsAddr` | The address the metric endpoint binds to | `:8095` | -| `linux.registrarImage.repository` | Linux node-driver-registrar image repository | `k8s.gcr.io/sig-storage/csi-node-driver-registrar` | -| `linux.registrarImage.pullPolicy` | Linux node-driver-registrar image pull policy | `IfNotPresent` | -| `linux.registrarImage.tag` | Linux node-driver-registrar image tag | `v2.4.0` | -| `linux.registrar.resources` | The resource request/limits for the linux node-driver-registrar container image | `limits: 100m CPU, 100Mi; requests: 10m CPU, 20Mi` | -| `linux.registrar.logVerbosity` | Log level for node-driver-registrar. Uses V logs (klog) | `5` | -| `linux.livenessProbeImage.repository` | Linux liveness-probe image repository | `k8s.gcr.io/sig-storage/livenessprobe` | -| `linux.livenessProbeImage.pullPolicy` | Linux liveness-probe image pull policy | `IfNotPresent` | -| `linux.livenessProbeImage.tag` | Linux liveness-probe image tag | `v2.5.0` | -| `linux.livenessProbe.resources` | The resource request/limits for the linux liveness-probe container image | `limits: 100m CPU, 100Mi; requests: 10m CPU, 20Mi` | -| `linux.env` | Environment variables to be passed for the daemonset on linux nodes | `[]` | -| `linux.priorityClassName` | Indicates the importance of a Pod relative to other Pods. | `""` | -| `linux.crds.annotations` | Linux *helm hook* annotations | `{}` | -| `linux.daemonsetAnnotations` | Linux *DaemonSet* annotations | `{}` | -| `linux.podAnnotations` | Linux *Pod* annotations | `{}` | -| `linux.podLabels` | Linux *Pod* labels | `{}` | -| `linux.volumes` | Linux volumes | `{}` | -| `linux.volumeMounts` | Linux volumeMounts | `{}` | -| `linux.updateStrategy` | Configure a custom update strategy for the daemonset on linux nodes | `RollingUpdate with 1 maxUnavailable` | -| `windows.image.repository` | Windows image repository | `k8s.gcr.io/csi-secrets-store/driver` | -| `windows.image.pullPolicy` | Windows image pull policy | `IfNotPresent` | -| `windows.image.tag` | Windows image tag | `v1.0.1` | -| `windows.affinity` | Windows affinity | `key: type; operator: NotIn; values: [virtual-kubelet]` | -| `windows.driver.resources` | The resource request/limits for the windows secrets-store container image | `limits: 400m CPU, 400Mi; requests: 50m CPU, 100Mi` | -| `windows.enabled` | Install secrets store csi driver on windows nodes | false | -| `windows.kubeletRootDir` | Configure the kubelet root dir | `C:\var\lib\kubelet` | -| `windows.providersDir` | Configure the providers root dir | `C:\k\secrets-store-csi-providers` | -| `windows.nodeSelector` | Node Selector for the daemonset on windows nodes | `{}` | -| `windows.tolerations` | Tolerations for the daemonset on windows nodes | `[]` | -| `windows.metricsAddr` | The address the metric endpoint binds to | `:8095` | -| `windows.registrarImage.repository` | Windows node-driver-registrar image repository | `k8s.gcr.io/sig-storage/csi-node-driver-registrar` | -| `windows.registrarImage.pullPolicy` | Windows node-driver-registrar image pull policy | `IfNotPresent` | -| `windows.registrarImage.tag` | Windows node-driver-registrar image tag | `v2.4.0` | -| `windows.registrar.resources` | The resource request/limits for the windows node-driver-registrar container image | `limits: 200m CPU, 200Mi; requests: 10m CPU, 20Mi` | -| `windows.registrar.logVerbosity` | Log level for node-driver-registrar. Uses V logs (klog) | `5` | -| `windows.livenessProbeImage.repository` | Windows liveness-probe image repository | `k8s.gcr.io/sig-storage/livenessprobe` | -| `windows.livenessProbeImage.pullPolicy` | Windows liveness-probe image pull policy | `IfNotPresent` | -| `windows.livenessProbeImage.tag` | Windows liveness-probe image tag | `v2.5.0` | -| `windows.livenessProbe.resources` | The resource request/limits for the windows liveness-probe container image | `limits: 200m CPU, 200Mi; requests: 10m CPU, 20Mi` | -| `windows.env` | Environment variables to be passed for the daemonset on windows nodes | `[]` | -| `windows.priorityClassName` | Indicates the importance of a Pod relative to other Pods. | `""` | -| `windows.daemonsetAnnotations` | Windows *DaemonSet* annotations | `{}` | -| `windows.podAnnotations` | Windows *Pod* annotations | `{}` | -| `windows.podLabels` | Windows *Pod* labels | `{}` | -| `windows.volumes` | Windows volumes | `{}` | -| `windows.volumeMounts` | Windows volumeMounts | `{}` | -| `windows.updateStrategy` | Configure a custom update strategy for the daemonset on windows nodes | `RollingUpdate with 1 maxUnavailable` | -| `logVerbosity` | Log level. Uses V logs (klog) | `0` | -| `logFormatJSON` | Use JSON logging format | `false` | -| `livenessProbe.port` | Liveness probe port | `9808` | -| `livenessProbe.logLevel` | Liveness probe container logging verbosity level | `2` | -| `maxCallRecvMsgSize` | Maximum size in bytes of gRPC response from plugins | `4194304` | -| `rbac.install` | Install default rbac roles and bindings | true | -| `rbac.pspEnabled` | If `true`, create and use a restricted pod security policy for Secrets Store CSI Driver pod(s) | `false` | -| `syncSecret.enabled` | Enable rbac roles and bindings required for syncing to Kubernetes native secrets | false | -| `enableSecretRotation` | Enable secret rotation feature [alpha] | `false` | -| `rotationPollInterval` | Secret rotation poll interval duration | `"120s"` | -| `providerHealthCheck` | Enable health check for configured providers | `false` | -| `providerHealthCheckInterval` | Provider healthcheck interval duration | `2m` | -| `imagePullSecrets` | One or more secrets to be used when pulling images | `""` | +| Parameter | Description | Default | +| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | +| `nameOverride` | String to partially override secrets-store-csi-driver.fullname template with a string (will prepend the release name) | `""` | +| `fullnameOverride` | String to fully override secrets-store-csi-driver.fullname template with a string | `""` | +| `linux.image.repository` | Linux image repository | `k8s.gcr.io/csi-secrets-store/driver` | +| `linux.image.pullPolicy` | Linux image pull policy | `IfNotPresent` | +| `linux.image.tag` | Linux image tag | `v1.0.1` | +| `linux.crds.image.repository` | Linux crds image repository | `k8s.gcr.io/csi-secrets-store/driver-crds` | +| `linux.crds.image.pullPolicy` | Linux crds image pull policy | `IfNotPresent` | +| `linux.crds.image.tag` | Linux crds image tag | `v1.0.1` | +| `linux.affinity` | Linux affinity | `key: type; operator: NotIn; values: [virtual-kubelet]` | +| `linux.driver.resources` | The resource request/limits for the linux secrets-store container image | `limits: 200m CPU, 200Mi; requests: 50m CPU, 100Mi` | +| `linux.enabled` | Install secrets store csi driver on linux nodes | true | +| `linux.kubeletRootDir` | Configure the kubelet root dir | `/var/lib/kubelet` | +| `linux.providersDir` | Configure the providers root dir | `/etc/kubernetes/secrets-store-csi-providers` | +| `linux.nodeSelector` | Node Selector for the daemonset on linux nodes | `{}` | +| `linux.tolerations` | Tolerations for the daemonset on linux nodes | `[]` | +| `linux.metricsAddr` | The address the metric endpoint binds to | `:8095` | +| `linux.registrarImage.repository` | Linux node-driver-registrar image repository | `k8s.gcr.io/sig-storage/csi-node-driver-registrar` | +| `linux.registrarImage.pullPolicy` | Linux node-driver-registrar image pull policy | `IfNotPresent` | +| `linux.registrarImage.tag` | Linux node-driver-registrar image tag | `v2.4.0` | +| `linux.registrar.resources` | The resource request/limits for the linux node-driver-registrar container image | `limits: 100m CPU, 100Mi; requests: 10m CPU, 20Mi` | +| `linux.registrar.logVerbosity` | Log level for node-driver-registrar. Uses V logs (klog) | `5` | +| `linux.livenessProbeImage.repository` | Linux liveness-probe image repository | `k8s.gcr.io/sig-storage/livenessprobe` | +| `linux.livenessProbeImage.pullPolicy` | Linux liveness-probe image pull policy | `IfNotPresent` | +| `linux.livenessProbeImage.tag` | Linux liveness-probe image tag | `v2.5.0` | +| `linux.livenessProbe.resources` | The resource request/limits for the linux liveness-probe container image | `limits: 100m CPU, 100Mi; requests: 10m CPU, 20Mi` | +| `linux.env` | Environment variables to be passed for the daemonset on linux nodes | `[]` | +| `linux.priorityClassName` | Indicates the importance of a Pod relative to other Pods. | `""` | +| `linux.crds.annotations` | Linux *helm hook* annotations | `{}` | +| `linux.daemonsetAnnotations` | Linux *DaemonSet* annotations | `{}` | +| `linux.podAnnotations` | Linux *Pod* annotations | `{}` | +| `linux.podLabels` | Linux *Pod* labels | `{}` | +| `linux.volumes` | Linux volumes | `{}` | +| `linux.volumeMounts` | Linux volumeMounts | `{}` | +| `linux.updateStrategy` | Configure a custom update strategy for the daemonset on linux nodes | `RollingUpdate with 1 maxUnavailable` | +| `windows.image.repository` | Windows image repository | `k8s.gcr.io/csi-secrets-store/driver` | +| `windows.image.pullPolicy` | Windows image pull policy | `IfNotPresent` | +| `windows.image.tag` | Windows image tag | `v1.0.1` | +| `windows.affinity` | Windows affinity | `key: type; operator: NotIn; values: [virtual-kubelet]` | +| `windows.driver.resources` | The resource request/limits for the windows secrets-store container image | `limits: 400m CPU, 400Mi; requests: 50m CPU, 100Mi` | +| `windows.enabled` | Install secrets store csi driver on windows nodes | false | +| `windows.kubeletRootDir` | Configure the kubelet root dir | `C:\var\lib\kubelet` | +| `windows.providersDir` | Configure the providers root dir | `C:\k\secrets-store-csi-providers` | +| `windows.nodeSelector` | Node Selector for the daemonset on windows nodes | `{}` | +| `windows.tolerations` | Tolerations for the daemonset on windows nodes | `[]` | +| `windows.metricsAddr` | The address the metric endpoint binds to | `:8095` | +| `windows.registrarImage.repository` | Windows node-driver-registrar image repository | `k8s.gcr.io/sig-storage/csi-node-driver-registrar` | +| `windows.registrarImage.pullPolicy` | Windows node-driver-registrar image pull policy | `IfNotPresent` | +| `windows.registrarImage.tag` | Windows node-driver-registrar image tag | `v2.4.0` | +| `windows.registrar.resources` | The resource request/limits for the windows node-driver-registrar container image | `limits: 200m CPU, 200Mi; requests: 10m CPU, 20Mi` | +| `windows.registrar.logVerbosity` | Log level for node-driver-registrar. Uses V logs (klog) | `5` | +| `windows.livenessProbeImage.repository` | Windows liveness-probe image repository | `k8s.gcr.io/sig-storage/livenessprobe` | +| `windows.livenessProbeImage.pullPolicy` | Windows liveness-probe image pull policy | `IfNotPresent` | +| `windows.livenessProbeImage.tag` | Windows liveness-probe image tag | `v2.5.0` | +| `windows.livenessProbe.resources` | The resource request/limits for the windows liveness-probe container image | `limits: 200m CPU, 200Mi; requests: 10m CPU, 20Mi` | +| `windows.env` | Environment variables to be passed for the daemonset on windows nodes | `[]` | +| `windows.priorityClassName` | Indicates the importance of a Pod relative to other Pods. | `""` | +| `windows.daemonsetAnnotations` | Windows *DaemonSet* annotations | `{}` | +| `windows.podAnnotations` | Windows *Pod* annotations | `{}` | +| `windows.podLabels` | Windows *Pod* labels | `{}` | +| `windows.volumes` | Windows volumes | `{}` | +| `windows.volumeMounts` | Windows volumeMounts | `{}` | +| `windows.updateStrategy` | Configure a custom update strategy for the daemonset on windows nodes | `RollingUpdate with 1 maxUnavailable` | +| `logVerbosity` | Log level. Uses V logs (klog) | `0` | +| `logFormatJSON` | Use JSON logging format | `false` | +| `livenessProbe.port` | Liveness probe port | `9808` | +| `livenessProbe.logLevel` | Liveness probe container logging verbosity level | `2` | +| `maxCallRecvMsgSize` | Maximum size in bytes of gRPC response from plugins | `4194304` | +| `rbac.install` | Install default rbac roles and bindings | true | +| `rbac.pspEnabled` | If `true`, create and use a restricted pod security policy for Secrets Store CSI Driver pod(s) | `false` | +| `syncSecret.enabled` | Enable rbac roles and bindings required for syncing to Kubernetes native secrets | false | +| `enableSecretRotation` | Enable secret rotation feature [alpha] | `false` | +| `rotationPollInterval` | Secret rotation poll interval duration | `"120s"` | +| `providerHealthCheck` | Enable health check for configured providers | `false` | +| `providerHealthCheckInterval` | Provider healthcheck interval duration | `2m` | +| `imagePullSecrets` | One or more secrets to be used when pulling images | `""` | +| `tokenRequests` | Token requests configuration for the csi driver. Refer to [doc](https://kubernetes-csi.github.io/docs/token-requests.html) for more info. | `""` | diff --git a/manifest_staging/charts/secrets-store-csi-driver/templates/csidriver.yaml b/manifest_staging/charts/secrets-store-csi-driver/templates/csidriver.yaml index bec85f39b..55bfaf57b 100644 --- a/manifest_staging/charts/secrets-store-csi-driver/templates/csidriver.yaml +++ b/manifest_staging/charts/secrets-store-csi-driver/templates/csidriver.yaml @@ -8,3 +8,7 @@ spec: # Added in Kubernetes 1.16 with default mode of Persistent. Secrets store csi driver needs Ephermeral to be set. volumeLifecycleModes: - Ephemeral + {{- if .Values.tokenRequests }} + tokenRequests: + {{- toYaml .Values.tokenRequests | nindent 2}} + {{- end }} diff --git a/manifest_staging/charts/secrets-store-csi-driver/values.yaml b/manifest_staging/charts/secrets-store-csi-driver/values.yaml index 9fcbc18f9..14dee94d9 100644 --- a/manifest_staging/charts/secrets-store-csi-driver/values.yaml +++ b/manifest_staging/charts/secrets-store-csi-driver/values.yaml @@ -207,3 +207,9 @@ providerHealthCheck: false providerHealthCheckInterval: 2m imagePullSecrets: [] + +## This allows CSI drivers to impersonate the pods that they mount the volumes for. +# refer to https://kubernetes-csi.github.io/docs/token-requests.html for more details. +tokenRequests: [] +# - audience: aud1 +# - audience: aud2 diff --git a/test/bats/e2e-provider.bats b/test/bats/e2e-provider.bats index ca3834a73..e9cfa8ddb 100644 --- a/test/bats/e2e-provider.bats +++ b/test/bats/e2e-provider.bats @@ -34,13 +34,33 @@ export LABEL_VALUE=${LABEL_VALUE:-"test"} # export the secrets-store API version to be used export API_VERSION=$(get_secrets_store_api_version) +# export the token requests audience configured in the CSIDriver +# refer to https://kubernetes-csi.github.io/docs/token-requests.html for more information +export VALIDATE_TOKENS_AUDIENCE=$(get_token_requests_audience) + +@test "setup mock provider validation config" { + if [[ -n "${VALIDATE_TOKENS_AUDIENCE}" ]]; then + # configure the mock provider to validate the token requests + kubectl create ns enable-token-requests + local curl_pod_name=curl-$(openssl rand -hex 5) + kubectl run ${curl_pod_name} -n enable-token-requests --image=curlimages/curl:7.75.0 --labels="util=enable-token-requests" -- tail -f /dev/null + kubectl wait -n enable-token-requests --for=condition=Ready --timeout=60s pod ${curl_pod_name} + local pod_ip=$(kubectl get pod -n kube-system -l app=csi-secrets-store-e2e-provider -o jsonpath="{.items[0].status.podIP}") + run kubectl exec ${curl_pod_name} -n enable-token-requests -- curl http://${pod_ip}:8080/validate-token-requests?audience=${VALIDATE_TOKENS_AUDIENCE} + kubectl delete pod -l util=enable-token-requests -n enable-token-requests --force --grace-period 0 + kubectl delete ns enable-token-requests + fi + + log_secrets_store_api_version + log_token_requests_audience +} + + @test "secretproviderclasses crd is established" { kubectl wait --for condition=established --timeout=60s crd/secretproviderclasses.secrets-store.csi.x-k8s.io run kubectl get crd/secretproviderclasses.secrets-store.csi.x-k8s.io assert_success - - log_secrets_store_api_version } @test "secretproviderclasspodstatuses crd is established" { diff --git a/test/bats/helpers.bash b/test/bats/helpers.bash index 6f4eeac36..c5982e418 100644 --- a/test/bats/helpers.bash +++ b/test/bats/helpers.bash @@ -137,3 +137,12 @@ get_secrets_store_api_version() { log_secrets_store_api_version() { echo "Testing secrets-store API version $API_VERSION" >&3 } + +get_token_requests_audience() { + local token_requests_audience=$(kubectl get csidriver secrets-store.csi.k8s.io -o go-template --template='{{range $i, $v := .spec.tokenRequests}}{{if $i}},{{end}}{{printf "%s" .audience}}{{end}}') + echo "${token_requests_audience}" +} + +log_token_requests_audience() { + echo "Testing token requests audience $VALIDATE_TOKENS_AUDIENCE" >&3 +} diff --git a/test/e2eprovider/e2e_provider.go b/test/e2eprovider/e2e_provider.go index e029cde22..0e1a486c4 100644 --- a/test/e2eprovider/e2e_provider.go +++ b/test/e2eprovider/e2e_provider.go @@ -69,6 +69,7 @@ func main() { os.Setenv("ROTATION_ENABLED", "false") http.HandleFunc("/rotation", server.RotationHandler) + http.HandleFunc("/validate-token-requests", server.ValidateTokenAudienceHandler) klog.Fatal(http.ListenAndServe(":8080", nil)) }() diff --git a/test/e2eprovider/server/server.go b/test/e2eprovider/server/server.go index 18879cb17..1c60079d6 100644 --- a/test/e2eprovider/server/server.go +++ b/test/e2eprovider/server/server.go @@ -48,7 +48,8 @@ This is mock key // podCache is a map of pod UID to check if secret has been rotated. podCache = map[string]bool{} - podUIDAttribute = "csi.storage.k8s.io/pod.uid" + podUIDAttribute = "csi.storage.k8s.io/pod.uid" + serviceAccountTokensAttribute = "csi.storage.k8s.io/serviceAccount.tokens" //nolint // RWMutex is to safely access podCache m sync.RWMutex @@ -168,6 +169,23 @@ func (s *Server) Mount(ctx context.Context, req *v1alpha1.MountRequest) (*v1alph resp.ObjectVersion = append(resp.ObjectVersion, version) } + // if validate token flag is set, we want to check the service account tokens as passed + // as part of the mount attributes. + // In case of 1.21+, kubelet will generate the token and pass it as part of the volume context. + // The driver will pass this to the provider as part of the mount request. + // For 1.20, the driver will generate the token and pass it to the provider as part of the mount request. + // Irrespective of the kubernetes version, the rotation handler in the driver will generate the token + // and pass it to the provider as part of the mount request. + // VALIDATE_TOKENS_AUDIENCE environment variable will be a comma separated list of audiences configured in the csidriver object + // If this env var is not set, this could mean we are running an older version of driver. + tokenAudiences := os.Getenv("VALIDATE_TOKENS_AUDIENCE") + klog.InfoS("tokenAudiences", "tokenAudiences", tokenAudiences) + if tokenAudiences != "" { + if err := validateTokens(tokenAudiences, attrib[serviceAccountTokensAttribute]); err != nil { + return nil, fmt.Errorf("failed to validate token, error: %w", err) + } + } + m.Lock() podCache[attrib[podUIDAttribute]] = true m.Unlock() @@ -219,3 +237,32 @@ func RotationHandler(w http.ResponseWriter, r *http.Request) { os.Setenv("ROTATION_ENABLED", r.FormValue("rotated")) klog.InfoS("Rotation response enabled") } + +// ValidateTokenAudienceHandler enables token validation for the mock provider +// This is only required because older version of the driver don't generate a token +// TODO(aramase): remove this after the supported driver releases are v1.1.0+ +func ValidateTokenAudienceHandler(w http.ResponseWriter, r *http.Request) { + // enable rotation response + os.Setenv("VALIDATE_TOKENS_AUDIENCE", r.FormValue("audience")) + klog.InfoS("Validation for token requests audience", "audience", os.Getenv("VALIDATE_TOKENS_AUDIENCE")) +} + +// validateTokens checks there are tokens for distinct audiences in the +// service account token attribute. +func validateTokens(tokenAudiences, saTokens string) error { + ta := strings.Split(strings.TrimSpace(tokenAudiences), ",") + if saTokens == "" { + return fmt.Errorf("service account tokens is not set") + } + tokens := make(map[string]interface{}) + if err := json.Unmarshal([]byte(saTokens), &tokens); err != nil { + return fmt.Errorf("failed to unmarshal service account tokens, error: %w", err) + } + for _, a := range ta { + if _, ok := tokens[a]; !ok { + return fmt.Errorf("service account token for audience %s is not set", a) + } + klog.InfoS("Validated service account token", "audience", a) + } + return nil +} diff --git a/test/e2eprovider/server/server_test.go b/test/e2eprovider/server/server_test.go index 80c8a444b..7cfd136cb 100644 --- a/test/e2eprovider/server/server_test.go +++ b/test/e2eprovider/server/server_test.go @@ -213,3 +213,33 @@ func TestRotation(t *testing.T) { t.Errorf("didn't get expected results: (-want +got):\n%s", diff) } } + +func TestValidateTokens(t *testing.T) { + tests := []struct { + name string + tokens string + audiences string + wantErr bool + }{ + { + name: "no tokens", + tokens: "", + audiences: "aud1,aud2", + wantErr: true, + }, + { + name: "matching tokens for audience", + tokens: `{"aud1":{"token":"eyJhbGciOiJSUzI1NiIsImtpZCI6InRhVDBxbzhQVEZ1ajB1S3BYUUxIclRsR01XakxjemJNOTlzWVMxSlNwbWcifQ.eyJhdWQiOlsiYXBpOi8vQXp1cmVBRGlUb2tlbkV4Y2hhbmdlIl0sImV4cCI6MTY0MzIzNDY0NywiaWF0IjoxNjQzMjMxMDQ3LCJpc3MiOiJodHRwczovL2t1YmVybmV0ZXMuZGVmYXVsdC5zdmMuY2x1c3Rlci5sb2NhbCIsImt1YmVybmV0ZXMuaW8iOnsibmFtZXNwYWNlIjoidGVzdC12MWFscGhhMSIsInBvZCI6eyJuYW1lIjoic2VjcmV0cy1zdG9yZS1pbmxpbmUtY3JkIiwidWlkIjoiYjBlYmZjMzUtZjEyNC00ZTEyLWI3N2UtYjM0MjM2N2IyMDNmIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkZWZhdWx0IiwidWlkIjoiMjViNGY1NzgtM2U4MC00NTczLWJlOGQtZTdmNDA5ZDI0MmI2In19LCJuYmYiOjE2NDMyMzEwNDcsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDp0ZXN0LXYxYWxwaGExOmRlZmF1bHQifQ.ALE46aKmtTV7dsuFOwDZqvEjdHFUTNP-JVjMxexTemmPA78fmPTUZF0P6zANumA03fjX3L-MZNR3PxmEZgKA9qEGIDsljLsUWsVBEquowuBh8yoBYkGkMJmRfmbfS3y7_4Q7AU3D9Drw4iAHcn1GwedjOQC0i589y3dkNNqf8saqHfXkbSSLtSE0f2uzI-PjuTKvR1kuojEVNKlEcA4wsKfoiRpkua17sHkHU0q9zxCMDCr_1f8xbigRnRx0wscU3vy-8KhF3zQtpcWkk3r4C5YSXut9F3xjz5J9DUQn2vNMfZg4tOdcR-9Xv9fbY5iujiSlS58GEktSEa3SE9wrCw\",\"expirationTimestamp\":\"2022-01-26T22:04:07Z\"},\"gcp\":{\"token\":\"eyJhbGciOiJSUzI1NiIsImtpZCI6InRhVDBxbzhQVEZ1ajB1S3BYUUxIclRsR01XakxjemJNOTlzWVMxSlNwbWcifQ.eyJhdWQiOlsiZ2NwIl0sImV4cCI6MTY0MzIzNDY0NywiaWF0IjoxNjQzMjMxMDQ3LCJpc3MiOiJodHRwczovL2t1YmVybmV0ZXMuZGVmYXVsdC5zdmMuY2x1c3Rlci5sb2NhbCIsImt1YmVybmV0ZXMuaW8iOnsibmFtZXNwYWNlIjoidGVzdC12MWFscGhhMSIsInBvZCI6eyJuYW1lIjoic2VjcmV0cy1zdG9yZS1pbmxpbmUtY3JkIiwidWlkIjoiYjBlYmZjMzUtZjEyNC00ZTEyLWI3N2UtYjM0MjM2N2IyMDNmIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkZWZhdWx0IiwidWlkIjoiMjViNGY1NzgtM2U4MC00NTczLWJlOGQtZTdmNDA5ZDI0MmI2In19LCJuYmYiOjE2NDMyMzEwNDcsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDp0ZXN0LXYxYWxwaGExOmRlZmF1bHQifQ.BT0YGI7bGdSNaIBqIEnVL0Ky5t-fynaemSGxjGdKOPl0E22UIVGDpAMUhaS19i20c-Dqs-Kn0N-R5QyDNpZg8vOL5KIFqu2kSYNbKxtQW7TPYIsV0d9wUZjLSr54DKrmyXNMGRoT2bwcF4yyfmO46eMmZSaXN8Y4lgapeabg6CBVVQYHD-GrgXf9jVLeJfCQkTuojK1iXOphyD6NqlGtVCaY1jWxbBMibN0q214vKvQboub8YMuvclGdzn_l_ZQSTjvhBj9I-W1t-JArVjqHoIb8_FlR9BSgzgL7V3Jki55vmiOdEYqMErJWrIZPP3s8qkU5hhO9rSVEd3LJHponvQ","expirationTimestamp":"2022-01-26T22:04:07Z"}}`, + audiences: "aud1", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := validateTokens(tt.audiences, tt.tokens); (err != nil) != tt.wantErr { + t.Errorf("validateTokens() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +}