Browse Source

feat: referent auth for gcp (#1887)

Signed-off-by: Moritz Johner <beller.moritz@googlemail.com>

Signed-off-by: Moritz Johner <beller.moritz@googlemail.com>
Moritz Johner 3 years ago
parent
commit
11c61d8581

+ 1 - 1
docs/introduction/stability-support.md

@@ -49,7 +49,7 @@ The following table show the support for features across different providers.
 | AWS Secrets Manager       |      x       |      x       |                      |                         |        x         |             |
 | AWS Parameter Store       |      x       |      x       |                      |                         |        x         |             |
 | Hashicorp Vault           |      x       |      x       |                      |                         |        x         |             |
-| GCP Secret Manager        |      x       |      x       |                      |                         |        x         |             |
+| GCP Secret Manager        |      x       |      x       |                      |            x            |        x         |             |
 | Azure Keyvault            |      x       |      x       |          x           |            x            |        x         |             |
 | Kubernetes                |      x       |      x       |                      |            x            |        x         |             |
 | IBM Cloud Secrets Manager |              |              |                      |                         |        x         |             |

+ 76 - 54
e2e/suites/provider/cases/gcp/gcp.go

@@ -29,39 +29,60 @@ import (
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 )
 
+const (
+	withStaticAuth         = "with service account"
+	withReferentStaticAuth = "with service acount from referent namespace"
+)
+
 // This test uses the global ESO.
 var _ = Describe("[gcp]", Label("gcp", "secretsmanager"), func() {
 	f := framework.New("eso-gcp")
 	prov := NewFromEnv(f, "")
 
 	DescribeTable("sync secrets", framework.TableFunc(f, prov),
-		Entry(common.SimpleDataSync(f)),
-		Entry(common.JSONDataWithProperty(f)),
-		Entry(common.JSONDataFromSync(f)),
-		Entry(common.JSONDataFromRewrite(f)),
-		Entry(common.NestedJSONWithGJSON(f)),
-		Entry(common.JSONDataWithTemplate(f)),
-		Entry(common.DockerJSONConfig(f)),
-		Entry(common.DataPropertyDockerconfigJSON(f)),
-		Entry(common.SSHKeySync(f)),
-		Entry(common.SSHKeySyncDataProperty(f)),
-		Entry(common.SyncWithoutTargetName(f)),
-		Entry(common.JSONDataWithoutTargetName(f)),
-		Entry(common.FindByName(f)),
-		Entry(common.FindByNameAndRewrite(f)),
-		Entry(common.FindByNameWithPath(f)),
-		Entry(common.FindByTag(f)),
-		Entry(common.FindByTagWithPath(f)),
-		Entry(common.SyncV1Alpha1(f)),
-		Entry("should sync p12 encoded cert secret", p12Cert),
+		framework.Compose(withStaticAuth, f, common.SimpleDataSync, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.JSONDataWithProperty, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.JSONDataFromSync, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.JSONDataFromRewrite, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.NestedJSONWithGJSON, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.JSONDataWithTemplate, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.DockerJSONConfig, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.DataPropertyDockerconfigJSON, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.SSHKeySync, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.SSHKeySyncDataProperty, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.SyncWithoutTargetName, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.JSONDataWithoutTargetName, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.FindByName, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.FindByNameAndRewrite, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.FindByNameWithPath, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.FindByTag, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.FindByTagWithPath, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.SyncV1Alpha1, useStaticAuth),
+		framework.Compose(withStaticAuth, f, p12Cert, useStaticAuth),
+
+		// referent auth
+		framework.Compose(withReferentStaticAuth, f, common.SimpleDataSync, useReferentAuth),
 	)
 })
 
+func useStaticAuth(tc *framework.TestCase) {
+	tc.ExternalSecret.Spec.SecretStoreRef.Name = tc.Framework.Namespace.Name
+	if tc.ExternalSecretV1Alpha1 != nil {
+		tc.ExternalSecretV1Alpha1.Spec.SecretStoreRef.Name = tc.Framework.Namespace.Name
+	}
+}
+
+func useReferentAuth(tc *framework.TestCase) {
+	tc.ExternalSecret.Spec.SecretStoreRef.Name = referentName(tc.Framework)
+	tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1beta1.ClusterSecretStoreKind
+}
+
 // P12Cert case creates a secret with a p12 cert containing a privkey and cert bundled together.
 // It uses templating to generate a k8s secret of type tls with pem values.
-var p12Cert = func(tc *framework.TestCase) {
-	cloudSecretName := fmt.Sprintf("%s-%s", tc.Framework.Namespace.Name, "p12-cert-example")
-	certPEM := `-----BEGIN CERTIFICATE-----
+func p12Cert(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "should sync p12 encoded cert secret", func(tc *framework.TestCase) {
+		cloudSecretName := fmt.Sprintf("%s-%s", tc.Framework.Namespace.Name, "p12-cert-example")
+		certPEM := `-----BEGIN CERTIFICATE-----
 MIIFQjCCBCqgAwIBAgISBHszg5W2maz/7CIxGrf7mqukMA0GCSqGSIb3DQEBCwUA
 MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
 EwJSMzAeFw0yMTA3MjQxMjQyMzNaFw0yMTEwMjIxMjQyMzFaMCgxJjAkBgNVBAMT
@@ -93,7 +114,7 @@ XMYitHfpGhc+DTTiTWMQ13J0b1j4yv8A7ZaG2366aa28oSTD6eQFhmVCBwa54j++
 IOwzHn5R
 -----END CERTIFICATE-----
 `
-	privkeyPEM := `-----BEGIN PRIVATE KEY-----
+		privkeyPEM := `-----BEGIN PRIVATE KEY-----
 MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDJFE51myQDyqca
 egyBDlHLkxVj+WCjcfOWEqrTa7bcnbDXjD4uIRTaFxIkpi/k5fKxt+rszna7bNdh
 lezqSuRBmVg2kXDul5nQm1RtWRKlJP9fhvUYkoNKRGzt9OL6/6lv05P2tNu13yN8
@@ -122,40 +143,41 @@ Jdx0ECYawviQoreDAyIXV6HouoeRbDtLZ9AJvxMoIjGcjAR2FQHc3yx4h/lf3Tfx
 x6HaRh+EUwU51von6M9lEF9/p5Q=
 -----END PRIVATE KEY-----
 `
-	blockCert, _ := pem.Decode([]byte(certPEM))
-	cert, _ := x509.ParseCertificate(blockCert.Bytes)
-	blockPrivKey, _ := pem.Decode([]byte(privkeyPEM))
-	privkey, _ := x509.ParsePKCS8PrivateKey(blockPrivKey.Bytes)
-	emptyCACerts := []*x509.Certificate{}
-	p12Cert, _ := p12.Encode(rand.Reader, privkey, cert, emptyCACerts, "")
-
-	tc.Secrets = map[string]framework.SecretEntry{
-		cloudSecretName: {Value: string(p12Cert)},
-	}
+		blockCert, _ := pem.Decode([]byte(certPEM))
+		cert, _ := x509.ParseCertificate(blockCert.Bytes)
+		blockPrivKey, _ := pem.Decode([]byte(privkeyPEM))
+		privkey, _ := x509.ParsePKCS8PrivateKey(blockPrivKey.Bytes)
+		emptyCACerts := []*x509.Certificate{}
+		p12Cert, _ := p12.Encode(rand.Reader, privkey, cert, emptyCACerts, "")
 
-	tc.ExpectedSecret = &v1.Secret{
-		Type: v1.SecretTypeTLS,
-		Data: map[string][]byte{
-			"tls.crt": []byte(certPEM),
-			"tls.key": []byte(privkeyPEM),
-		},
-	}
+		tc.Secrets = map[string]framework.SecretEntry{
+			cloudSecretName: {Value: string(p12Cert)},
+		}
 
-	tc.ExternalSecret.Spec.Data = []esv1beta1.ExternalSecretData{
-		{
-			SecretKey: "mysecret",
-			RemoteRef: esv1beta1.ExternalSecretDataRemoteRef{
-				Key: cloudSecretName,
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeTLS,
+			Data: map[string][]byte{
+				"tls.crt": []byte(certPEM),
+				"tls.key": []byte(privkeyPEM),
 			},
-		},
-	}
+		}
 
-	tc.ExternalSecret.Spec.Target.Template = &esv1beta1.ExternalSecretTemplate{
-		Type:          v1.SecretTypeTLS,
-		EngineVersion: esv1beta1.TemplateEngineV1,
-		Data: map[string]string{
-			"tls.crt": "{{ .mysecret | pkcs12cert | pemCertificate }}",
-			"tls.key": "{{ .mysecret | pkcs12key | pemPrivateKey }}",
-		},
+		tc.ExternalSecret.Spec.Data = []esv1beta1.ExternalSecretData{
+			{
+				SecretKey: "mysecret",
+				RemoteRef: esv1beta1.ExternalSecretDataRemoteRef{
+					Key: cloudSecretName,
+				},
+			},
+		}
+
+		tc.ExternalSecret.Spec.Target.Template = &esv1beta1.ExternalSecretTemplate{
+			Type:          v1.SecretTypeTLS,
+			EngineVersion: esv1beta1.TemplateEngineV1,
+			Data: map[string]string{
+				"tls.crt": "{{ .mysecret | pkcs12cert | pemCertificate }}",
+				"tls.key": "{{ .mysecret | pkcs12key | pemPrivateKey }}",
+			},
+		}
 	}
 }

+ 61 - 14
e2e/suites/provider/cases/gcp/provider.go

@@ -39,11 +39,6 @@ import (
 	gcpsm "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager"
 )
 
-const (
-	PodIDSecretStoreName        = "pod-identity"
-	staticCredentialsSecretName = "provider-secret"
-)
-
 // nolint // Better to keep names consistent even if it stutters;
 type GcpProvider struct {
 	ServiceAccountName      string
@@ -71,9 +66,10 @@ func NewGCPProvider(f *framework.Framework, credentials, projectID string,
 	}
 
 	BeforeEach(func() {
-		prov.CreateSAKeyStore(f.Namespace.Name)
-		prov.CreateSpecifcSASecretStore(f.Namespace.Name)
-		prov.CreatePodIDStore(f.Namespace.Name)
+		prov.CreateSAKeyStore()
+		prov.CreateReferentSAKeyStore()
+		prov.CreateSpecifcSASecretStore()
+		prov.CreatePodIDStore()
 	})
 
 	AfterEach(func() {
@@ -163,14 +159,20 @@ func makeStore(s *GcpProvider) *esv1beta1.SecretStore {
 	}
 }
 
-func (s *GcpProvider) CreateSAKeyStore(ns string) {
+const (
+	serviceAccountKey           = "secret-access-credentials"
+	PodIDSecretStoreName        = "pod-identity"
+	staticCredentialsSecretName = "provider-secret"
+)
+
+func (s *GcpProvider) CreateSAKeyStore() {
 	gcpCreds := &v1.Secret{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      staticCredentialsSecretName,
 			Namespace: s.framework.Namespace.Name,
 		},
 		StringData: map[string]string{
-			"secret-access-credentials": s.credentials,
+			serviceAccountKey: s.credentials,
 		},
 	}
 	err := s.framework.CRClient.Create(context.Background(), gcpCreds)
@@ -183,7 +185,7 @@ func (s *GcpProvider) CreateSAKeyStore(ns string) {
 		SecretRef: &esv1beta1.GCPSMAuthSecretRef{
 			SecretAccessKey: esmeta.SecretKeySelector{
 				Name: staticCredentialsSecretName,
-				Key:  "secret-access-credentials",
+				Key:  serviceAccountKey,
 			},
 		},
 	}
@@ -191,7 +193,52 @@ func (s *GcpProvider) CreateSAKeyStore(ns string) {
 	Expect(err).ToNot(HaveOccurred())
 }
 
-func (s *GcpProvider) CreatePodIDStore(ns string) {
+func (s *GcpProvider) CreateReferentSAKeyStore() {
+	gcpCreds := &v1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      referentName(s.framework),
+			Namespace: s.framework.Namespace.Name,
+		},
+		StringData: map[string]string{
+			serviceAccountKey: s.credentials,
+		},
+	}
+	err := s.framework.CRClient.Create(context.Background(), gcpCreds)
+	if err != nil {
+		err = s.framework.CRClient.Update(context.Background(), gcpCreds)
+		Expect(err).ToNot(HaveOccurred())
+	}
+
+	css := &esv1beta1.ClusterSecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: referentName(s.framework),
+		},
+		Spec: esv1beta1.SecretStoreSpec{
+			Controller: s.controllerClass,
+			Provider: &esv1beta1.SecretStoreProvider{
+				GCPSM: &esv1beta1.GCPSMProvider{
+					ProjectID: s.projectID,
+					Auth: esv1beta1.GCPSMAuth{
+						SecretRef: &esv1beta1.GCPSMAuthSecretRef{
+							SecretAccessKey: esmeta.SecretKeySelector{
+								Name: referentName(s.framework),
+								Key:  serviceAccountKey,
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+	err = s.framework.CRClient.Create(context.Background(), css)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func referentName(f *framework.Framework) string {
+	return "referent-auth-" + f.Namespace.Name
+}
+
+func (s *GcpProvider) CreatePodIDStore() {
 	secretStore := makeStore(s)
 	secretStore.ObjectMeta.Name = PodIDSecretStoreName
 	err := s.framework.CRClient.Create(context.Background(), secretStore)
@@ -202,7 +249,7 @@ func (s *GcpProvider) SAClusterSecretStoreName() string {
 	return "gcpsa-" + s.framework.Namespace.Name
 }
 
-func (s *GcpProvider) CreateSpecifcSASecretStore(ns string) {
+func (s *GcpProvider) CreateSpecifcSASecretStore() {
 	clusterSecretStore := &esv1beta1.ClusterSecretStore{
 		ObjectMeta: metav1.ObjectMeta{
 			Name: s.SAClusterSecretStoreName(),
@@ -219,7 +266,7 @@ func (s *GcpProvider) CreateSpecifcSASecretStore(ns string) {
 						ClusterName:     s.clusterName,
 						ServiceAccountRef: esmeta.ServiceAccountSelector{
 							Name:      s.ServiceAccountName,
-							Namespace: utilpointer.StringPtr(s.ServiceAccountNamespace),
+							Namespace: utilpointer.String(s.ServiceAccountNamespace),
 						},
 					},
 				},

+ 0 - 1
pkg/controllers/secretstore/common.go

@@ -99,7 +99,6 @@ func validateStore(ctx context.Context, namespace, controllerClass string, store
 		recorder.Event(store, v1.EventTypeWarning, esapi.ReasonInvalidProviderConfig, err.Error())
 		return fmt.Errorf(errStoreClient, err)
 	}
-
 	validationResult, err := cl.Validate()
 	if err != nil && validationResult != esapi.ValidationResultUnknown {
 		cond := NewSecretStoreCondition(esapi.SecretStoreReady, v1.ConditionFalse, esapi.ReasonValidationFailed, errUnableValidateStore)

+ 2 - 9
pkg/provider/gcp/secretmanager/auth.go

@@ -33,7 +33,6 @@ func NewTokenSource(ctx context.Context, auth esv1beta1.GCPSMAuth, projectID str
 	}
 	wi, err := newWorkloadIdentity(ctx, projectID)
 	if err != nil {
-		useMu.Unlock()
 		return nil, fmt.Errorf("unable to initialize workload identity")
 	}
 	ts, err = wi.TokenSource(ctx, auth, isClusterKind, kube, namespace)
@@ -54,14 +53,8 @@ func serviceAccountTokenSource(ctx context.Context, auth esv1beta1.GCPSMAuth, is
 		Name:      credentialsSecretName,
 		Namespace: namespace,
 	}
-
-	// only ClusterStore is allowed to set namespace (and then it's required)
-	if isClusterKind {
-		if credentialsSecretName != "" && sr.SecretAccessKey.Namespace == nil {
-			return nil, fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
-		} else if credentialsSecretName != "" {
-			objectKey.Namespace = *sr.SecretAccessKey.Namespace
-		}
+	if isClusterKind && sr.SecretAccessKey.Namespace != nil {
+		objectKey.Namespace = *sr.SecretAccessKey.Namespace
 	}
 	err := kube.Get(ctx, objectKey, credentialsSecret)
 	if err != nil {

+ 20 - 18
pkg/provider/gcp/secretmanager/client.go

@@ -38,21 +38,19 @@ import (
 )
 
 const (
-	CloudPlatformRole                         = "https://www.googleapis.com/auth/cloud-platform"
-	defaultVersion                            = "latest"
-	errGCPSMStore                             = "received invalid GCPSM SecretStore resource"
-	errUnableGetCredentials                   = "unable to get credentials: %w"
-	errClientClose                            = "unable to close SecretManager client: %w"
-	errMissingStoreSpec                       = "invalid: missing store spec"
-	errInvalidClusterStoreMissingSAKNamespace = "invalid ClusterSecretStore: missing GCP SecretAccessKey Namespace"
-	errInvalidClusterStoreMissingSANamespace  = "invalid ClusterSecretStore: missing GCP Service Account Namespace"
-	errFetchSAKSecret                         = "could not fetch SecretAccessKey secret: %w"
-	errMissingSAK                             = "missing SecretAccessKey"
-	errUnableProcessJSONCredentials           = "failed to process the provided JSON credentials: %w"
-	errUnableCreateGCPSMClient                = "failed to create GCP secretmanager client: %w"
-	errUninitalizedGCPProvider                = "provider GCP is not initialized"
-	errClientGetSecretAccess                  = "unable to access Secret from SecretManager Client: %w"
-	errJSONSecretUnmarshal                    = "unable to unmarshal secret: %w"
+	CloudPlatformRole               = "https://www.googleapis.com/auth/cloud-platform"
+	defaultVersion                  = "latest"
+	errGCPSMStore                   = "received invalid GCPSM SecretStore resource"
+	errUnableGetCredentials         = "unable to get credentials: %w"
+	errClientClose                  = "unable to close SecretManager client: %w"
+	errMissingStoreSpec             = "invalid: missing store spec"
+	errFetchSAKSecret               = "could not fetch SecretAccessKey secret: %w"
+	errMissingSAK                   = "missing SecretAccessKey"
+	errUnableProcessJSONCredentials = "failed to process the provided JSON credentials: %w"
+	errUnableCreateGCPSMClient      = "failed to create GCP secretmanager client: %w"
+	errUninitalizedGCPProvider      = "provider GCP is not initialized"
+	errClientGetSecretAccess        = "unable to access Secret from SecretManager Client: %w"
+	errJSONSecretUnmarshal          = "unable to unmarshal secret: %w"
 
 	errInvalidStore           = "invalid store"
 	errInvalidStoreSpec       = "invalid store spec"
@@ -64,9 +62,10 @@ const (
 )
 
 type Client struct {
-	smClient GoogleSecretManagerClient
-	kube     kclient.Client
-	store    *esv1beta1.GCPSMProvider
+	smClient  GoogleSecretManagerClient
+	kube      kclient.Client
+	store     *esv1beta1.GCPSMProvider
+	storeKind string
 
 	// namespace of the external secret
 	namespace        string
@@ -404,5 +403,8 @@ func (c *Client) Close(ctx context.Context) error {
 }
 
 func (c *Client) Validate() (esv1beta1.ValidationResult, error) {
+	if c.storeKind == esv1beta1.ClusterSecretStoreKind && isReferentSpec(c.store) {
+		return esv1beta1.ValidationResultUnknown, nil
+	}
 	return esv1beta1.ValidationResultReady, nil
 }

+ 24 - 2
pkg/provider/gcp/secretmanager/provider.go

@@ -19,6 +19,7 @@ import (
 	"sync"
 
 	secretmanager "cloud.google.com/go/secretmanager/apiv1"
+	"golang.org/x/oauth2"
 	"google.golang.org/api/option"
 	kclient "sigs.k8s.io/controller-runtime/pkg/client"
 
@@ -66,6 +67,7 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore,
 	client := &Client{
 		kube:      kube,
 		store:     gcpStore,
+		storeKind: store.GetKind(),
 		namespace: namespace,
 	}
 	defer func() {
@@ -80,6 +82,14 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore,
 		return nil, err
 	}
 	isClusterKind := store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind
+	// allow SecretStore controller validation to pass
+	// when using referent namespace.
+	if namespace == "" && isClusterKind && isReferentSpec(gcpStore) {
+		// dummy smClient to prevent closing the client twice
+		client.smClient, _ = secretmanager.NewClient(ctx, option.WithTokenSource(oauth2.StaticTokenSource(&oauth2.Token{})))
+		return client, nil
+	}
+
 	ts, err := NewTokenSource(ctx, gcpStore.Auth, clusterProjectID, isClusterKind, kube, namespace)
 	if err != nil {
 		return nil, fmt.Errorf(errUnableCreateGCPSMClient, err)
@@ -115,12 +125,12 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) error {
 		return fmt.Errorf(errInvalidGCPProv)
 	}
 	if g.Auth.SecretRef != nil {
-		if err := utils.ValidateSecretSelector(store, g.Auth.SecretRef.SecretAccessKey); err != nil {
+		if err := utils.ValidateReferentSecretSelector(store, g.Auth.SecretRef.SecretAccessKey); err != nil {
 			return fmt.Errorf(errInvalidAuthSecretRef, err)
 		}
 	}
 	if g.Auth.WorkloadIdentity != nil {
-		if err := utils.ValidateServiceAccountSelector(store, g.Auth.WorkloadIdentity.ServiceAccountRef); err != nil {
+		if err := utils.ValidateReferentServiceAccountSelector(store, g.Auth.WorkloadIdentity.ServiceAccountRef); err != nil {
 			return fmt.Errorf(errInvalidWISARef, err)
 		}
 	}
@@ -136,3 +146,15 @@ func clusterProjectID(spec *esv1beta1.SecretStoreSpec) (string, error) {
 		return "", fmt.Errorf(errNoProjectID)
 	}
 }
+
+func isReferentSpec(prov *esv1beta1.GCPSMProvider) bool {
+	if prov.Auth.SecretRef != nil &&
+		prov.Auth.SecretRef.SecretAccessKey.Namespace == nil {
+		return true
+	}
+	if prov.Auth.WorkloadIdentity != nil &&
+		prov.Auth.WorkloadIdentity.ServiceAccountRef.Namespace == nil {
+		return true
+	}
+	return false
+}

+ 1 - 4
pkg/provider/gcp/secretmanager/workload_identity.go

@@ -105,10 +105,7 @@ func (w *workloadIdentity) TokenSource(ctx context.Context, auth esv1beta1.GCPSM
 	}
 
 	// only ClusterStore is allowed to set namespace (and then it's required)
-	if isClusterKind {
-		if wi.ServiceAccountRef.Namespace == nil {
-			return nil, fmt.Errorf(errInvalidClusterStoreMissingSANamespace)
-		}
+	if isClusterKind && wi.ServiceAccountRef.Namespace != nil {
 		saKey.Namespace = *wi.ServiceAccountRef.Namespace
 	}
 

+ 18 - 2
pkg/provider/gcp/secretmanager/workload_identity_test.go

@@ -83,8 +83,8 @@ func TestWorkloadIdentity(t *testing.T) {
 			}),
 		),
 		composeTestcase(
-			defaultTestCase("invalid ClusterSecretStore: missing service account namespace"),
-			expErr("invalid ClusterSecretStore: missing GCP Service Account Namespace"),
+			defaultTestCase("ClusterSecretStore: referent auth / service account without namespace"),
+			expTokenSource(),
 			withStore(
 				composeStore(defaultClusterStore()),
 			),
@@ -99,6 +99,22 @@ func TestWorkloadIdentity(t *testing.T) {
 			}),
 		),
 		composeTestcase(
+			defaultTestCase("ClusterSecretStore: invalid service account"),
+			expErr("foobar"),
+			withStore(
+				composeStore(defaultClusterStore()),
+			),
+			withK8sResources([]client.Object{
+				&v1.ServiceAccount{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:        "does not exist",
+						Namespace:   "default",
+						Annotations: map[string]string{},
+					},
+				},
+			}),
+		),
+		composeTestcase(
 			defaultTestCase("return access token from GenerateAccessTokenRequest with ClusterSecretStore"),
 			expTokenSource(),
 			expectToken(defaultGenAccessToken),