From 7ba3571fb69597da266ff65a78dd5d4fc38b40e2 Mon Sep 17 00:00:00 2001 From: Gorka Lerchundi Osa Date: Thu, 4 Jan 2024 23:54:15 +0100 Subject: [PATCH] internal/credentials: make kubernetes auth service account namespaced This commit adds the ability to specify a namespace for the service account used to authenticate to Vault. This is useful when you want to use a service account in a different namespace than the one the secret (vaultStaticSecret, vaultDynamicSecret) is located. This change is backwards compatible, so if no namespace is specified, the service account will be looked up in the same namespace as the secret. Example: Here the service account that will be used to authenticate to Vault is the service account `default` that it is in the namespace "vault-secrets-operator-system". ```yaml apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultAuth metadata: name: static-auth namespace: app spec: vaultConnectionRef: vault-connection allowedNamespaces: - "*" method: kubernetes mount: demo-auth-mount kubernetes: role: role1 serviceAccount: vault-secrets-operator-system/default ``` Closes hashicorp/vault-secrets-operator#336 --- internal/common/common.go | 14 ++++++ internal/common/common_test.go | 57 ++++++++++++++++++++++++ internal/credentials/vault/kubernetes.go | 9 +++- 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/internal/common/common.go b/internal/common/common.go index 4d4de988..b6e85c7a 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -402,3 +402,17 @@ func NewSyncableSecretMetaData(obj ctrlclient.Object) (*SyncableSecretMetaData, return nil, fmt.Errorf("unsupported type %T", t) } } + +// GetKubernetesServiceAccountNamespacedName returns the NamespacedName for the Kubernetes VaultAuth's configured +// serviceAccount. +// If the serviceAccount is empty then defaults Namespace and Name will be returned. +func GetKubernetesServiceAccountNamespacedName(a *secretsv1beta1.VaultAuthConfigKubernetes, providerNamespace string) (types.NamespacedName, error) { + if a.ServiceAccount == "" && providerNamespace == "" { + return types.NamespacedName{}, fmt.Errorf("provider's default namespace is not set, this is a bug") + } + saRef, err := parseResourceRef(a.ServiceAccount, providerNamespace) + if err != nil { + return types.NamespacedName{}, err + } + return saRef, nil +} diff --git a/internal/common/common_test.go b/internal/common/common_test.go index e30351bf..1ee192e7 100644 --- a/internal/common/common_test.go +++ b/internal/common/common_test.go @@ -583,3 +583,60 @@ func TestGetHCPAuthForObj(t *testing.T) { }) } } + +func Test_GetKubernetesServiceAccountNamespacedName(t *testing.T) { + tests := []struct { + name string + a *secretsv1beta1.VaultAuthConfigKubernetes + providerNamespace string + want types.NamespacedName + wantErr assert.ErrorAssertionFunc + unsetDefaultsNS bool + }{ + { + name: "empty-sa-ref", + a: &secretsv1beta1.VaultAuthConfigKubernetes{ + ServiceAccount: "", + }, + providerNamespace: "test", + want: types.NamespacedName{ + Namespace: OperatorNamespace, + Name: consts.NameDefault, + }, + wantErr: assert.NoError, + }, + { + name: "with-sa-ref-with-ns", + a: &secretsv1beta1.VaultAuthConfigKubernetes{ + ServiceAccount: "foo/bar", + }, + providerNamespace: "baz", + want: types.NamespacedName{ + Name: "bar", + Namespace: "foo", + }, + wantErr: assert.NoError, + }, + { + name: "with-sa-ref-without-ns", + a: &secretsv1beta1.VaultAuthConfigKubernetes{ + ServiceAccount: "foo", + }, + providerNamespace: "baz", + want: types.NamespacedName{ + Namespace: "baz", + Name: "foo", + }, + wantErr: assert.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetKubernetesServiceAccountNamespacedName(tt.a, tt.providerNamespace) + if !tt.wantErr(t, err, fmt.Sprintf("getKubernetesServiceAccountNamespacedName(%v)", tt.a)) { + return + } + assert.Equalf(t, tt.want, got, "getKubernetesServiceAccountNamespacedName(%v)", tt.a) + }) + } +} diff --git a/internal/credentials/vault/kubernetes.go b/internal/credentials/vault/kubernetes.go index 830c1f42..75556574 100644 --- a/internal/credentials/vault/kubernetes.go +++ b/internal/credentials/vault/kubernetes.go @@ -12,6 +12,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" secretsv1beta1 "github.com/hashicorp/vault-secrets-operator/api/v1beta1" + "github.com/hashicorp/vault-secrets-operator/internal/common" "github.com/hashicorp/vault-secrets-operator/internal/helpers" ) @@ -56,9 +57,13 @@ func (l *KubernetesCredentialProvider) Init(ctx context.Context, client ctrlclie } func (l *KubernetesCredentialProvider) getServiceAccount(ctx context.Context, client ctrlclient.Client) (*corev1.ServiceAccount, error) { + a, err := common.GetKubernetesServiceAccountNamespacedName(l.authObj.Spec.Kubernetes, l.providerNamespace) + if err != nil { + return nil, err + } key := ctrlclient.ObjectKey{ - Namespace: l.providerNamespace, - Name: l.authObj.Spec.Kubernetes.ServiceAccount, + Namespace: a.Namespace, + Name: a.Name, } sa := &corev1.ServiceAccount{} if err := client.Get(ctx, key, sa); err != nil {