Browse Source

aws secretsmanager/parameterstore referent auth (#1884)

* feat: implement referentAuth for aws

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

* feat: e2e tests

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

* Update pkg/provider/aws/provider.go

Co-authored-by: Gustavo Fernandes de Carvalho <gusfcarvalho@gmail.com>
Signed-off-by: Moritz Johner <moolen@users.noreply.github.com>

* Update pkg/provider/aws/provider.go

Co-authored-by: Gustavo Fernandes de Carvalho <gusfcarvalho@gmail.com>
Signed-off-by: Moritz Johner <moolen@users.noreply.github.com>

* feat: allow each credential to be referent

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

Signed-off-by: Moritz Johner <beller.moritz@googlemail.com>
Signed-off-by: Moritz Johner <moolen@users.noreply.github.com>
Co-authored-by: Gustavo Fernandes de Carvalho <gusfcarvalho@gmail.com>
Moritz Johner 3 years ago
parent
commit
5384954f46

+ 2 - 2
.github/workflows/e2e.yml

@@ -16,8 +16,8 @@ env:
   GO_VERSION: '1.19'
   GO_VERSION: '1.19'
   GINKGO_VERSION: 'v2.1.6'
   GINKGO_VERSION: 'v2.1.6'
   DOCKER_BUILDX_VERSION: 'v0.4.2'
   DOCKER_BUILDX_VERSION: 'v0.4.2'
-  KIND_VERSION: 'v0.14.0'
-  KIND_IMAGE: 'kindest/node:v1.24.2'
+  KIND_VERSION: 'v0.17.0'
+  KIND_IMAGE: 'kindest/node:v1.26.0'
 
 
   # Common users. We can't run a step 'if secrets.GHCR_USERNAME != ""' but we can run
   # Common users. We can't run a step 'if secrets.GHCR_USERNAME != ""' but we can run
   # a step 'if env.GHCR_USERNAME' != ""', so we copy these to succinctly test whether
   # a step 'if env.GHCR_USERNAME' != ""', so we copy these to succinctly test whether

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

@@ -46,8 +46,8 @@ The following table show the support for features across different providers.
 
 
 | Provider                  | find by name | find by tags | metadataPolicy Fetch | referent authentication | store validation | push secret |
 | Provider                  | find by name | find by tags | metadataPolicy Fetch | referent authentication | store validation | push secret |
 |---------------------------|:------------:|:------------:| :------------------: | :---------------------: | :--------------: | :---------: |
 |---------------------------|:------------:|:------------:| :------------------: | :---------------------: | :--------------: | :---------: |
-| AWS Secrets Manager       |      x       |      x       |                      |                         |        x         |             |
-| AWS Parameter Store       |      x       |      x       |                      |                         |        x         |             |
+| AWS Secrets Manager       |      x       |      x       |                      |            x            |        x         |             |
+| AWS Parameter Store       |      x       |      x       |                      |            x            |        x         |             |
 | Hashicorp Vault           |      x       |      x       |                      |                         |        x         |             |
 | Hashicorp Vault           |      x       |      x       |                      |                         |        x         |             |
 | GCP Secret Manager        |      x       |      x       |                      |            x            |        x         |             |
 | GCP Secret Manager        |      x       |      x       |                      |            x            |        x         |             |
 | Azure Keyvault            |      x       |      x       |          x           |            x            |        x         |             |
 | Azure Keyvault            |      x       |      x       |          x           |            x            |        x         |             |

+ 0 - 2
e2e/entrypoint.sh

@@ -33,9 +33,7 @@ ginkgo_args=(
   "--randomize-all"
   "--randomize-all"
   "--flake-attempts=2"
   "--flake-attempts=2"
   "-p"
   "-p"
-  "-progress"
   "-trace"
   "-trace"
-  "--slow-spec-threshold=5m"
   "-r"
   "-r"
   "-v"
   "-v"
   "-timeout=45m"
   "-timeout=45m"

+ 76 - 29
e2e/suites/provider/cases/aws/common.go

@@ -27,9 +27,10 @@ import (
 )
 )
 
 
 const (
 const (
-	WithReferencedIRSA          = "with referenced IRSA"
-	WithMountedIRSA             = "with mounted IRSA"
-	StaticCredentialsSecretName = "provider-secret"
+	WithReferencedIRSA                  = "with referenced IRSA"
+	WithMountedIRSA                     = "with mounted IRSA"
+	StaticCredentialsSecretName         = "provider-secret"
+	StaticReferentCredentialsSecretName = "referent-provider-secret"
 )
 )
 
 
 func ReferencedIRSAStoreName(f *framework.Framework) string {
 func ReferencedIRSAStoreName(f *framework.Framework) string {
@@ -50,6 +51,38 @@ func UseMountedIRSAStore(tc *framework.TestCase) {
 	tc.ExternalSecret.Spec.SecretStoreRef.Name = MountedIRSAStoreName(tc.Framework)
 	tc.ExternalSecret.Spec.SecretStoreRef.Name = MountedIRSAStoreName(tc.Framework)
 }
 }
 
 
+const (
+	StaticStoreName       = "aws-static-creds"
+	staticKeyID           = "kid"
+	staticSecretAccessKey = "sak"
+	staticySessionToken   = "st"
+)
+
+func newStaticStoreProvider(serviceType esv1beta1.AWSServiceType, region, secretName string) *esv1beta1.SecretStoreProvider {
+	return &esv1beta1.SecretStoreProvider{
+		AWS: &esv1beta1.AWSProvider{
+			Service: serviceType,
+			Region:  region,
+			Auth: esv1beta1.AWSAuth{
+				SecretRef: &esv1beta1.AWSAuthSecretRef{
+					AccessKeyID: esmetav1.SecretKeySelector{
+						Name: StaticReferentCredentialsSecretName,
+						Key:  staticKeyID,
+					},
+					SecretAccessKey: esmetav1.SecretKeySelector{
+						Name: StaticReferentCredentialsSecretName,
+						Key:  staticSecretAccessKey,
+					},
+					SessionToken: &esmetav1.SecretKeySelector{
+						Name: StaticReferentCredentialsSecretName,
+						Key:  staticySessionToken,
+					},
+				},
+			},
+		},
+	}
+}
+
 // StaticStore is namespaced and references
 // StaticStore is namespaced and references
 // static credentials from a secret.
 // static credentials from a secret.
 func SetupStaticStore(f *framework.Framework, kid, sak, st, region string, serviceType esv1beta1.AWSServiceType) {
 func SetupStaticStore(f *framework.Framework, kid, sak, st, region string, serviceType esv1beta1.AWSServiceType) {
@@ -59,9 +92,9 @@ func SetupStaticStore(f *framework.Framework, kid, sak, st, region string, servi
 			Namespace: f.Namespace.Name,
 			Namespace: f.Namespace.Name,
 		},
 		},
 		StringData: map[string]string{
 		StringData: map[string]string{
-			"kid": kid,
-			"sak": sak,
-			"st":  st,
+			staticKeyID:           kid,
+			staticSecretAccessKey: sak,
+			staticySessionToken:   st,
 		},
 		},
 	}
 	}
 	err := f.CRClient.Create(context.Background(), awsCreds)
 	err := f.CRClient.Create(context.Background(), awsCreds)
@@ -69,34 +102,48 @@ func SetupStaticStore(f *framework.Framework, kid, sak, st, region string, servi
 
 
 	secretStore := &esv1beta1.SecretStore{
 	secretStore := &esv1beta1.SecretStore{
 		ObjectMeta: metav1.ObjectMeta{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      f.Namespace.Name,
+			Name:      StaticStoreName,
 			Namespace: f.Namespace.Name,
 			Namespace: f.Namespace.Name,
 		},
 		},
 		Spec: esv1beta1.SecretStoreSpec{
 		Spec: esv1beta1.SecretStoreSpec{
-			Provider: &esv1beta1.SecretStoreProvider{
-				AWS: &esv1beta1.AWSProvider{
-					Service: serviceType,
-					Region:  region,
-					Auth: esv1beta1.AWSAuth{
-						SecretRef: &esv1beta1.AWSAuthSecretRef{
-							AccessKeyID: esmetav1.SecretKeySelector{
-								Name: StaticCredentialsSecretName,
-								Key:  "kid",
-							},
-							SecretAccessKey: esmetav1.SecretKeySelector{
-								Name: StaticCredentialsSecretName,
-								Key:  "sak",
-							},
-							SessionToken: &esmetav1.SecretKeySelector{
-								Name: StaticCredentialsSecretName,
-								Key:  "st",
-							},
-						},
-					},
-				},
-			},
+			Provider: newStaticStoreProvider(serviceType, region, StaticCredentialsSecretName),
+		},
+	}
+	err = f.CRClient.Create(context.Background(), secretStore)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+// CreateReferentStaticStore creates a CSS with referent auth and
+// creates a secret with static authentication credentials in the ExternalSecret namespace.
+func CreateReferentStaticStore(f *framework.Framework, kid, sak, st, region string, serviceType esv1beta1.AWSServiceType) {
+	ns := f.Namespace.Name
+
+	awsCreds := &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      StaticReferentCredentialsSecretName,
+			Namespace: ns,
+		},
+		StringData: map[string]string{
+			staticKeyID:           kid,
+			staticSecretAccessKey: sak,
+			staticySessionToken:   st,
+		},
+	}
+	err := f.CRClient.Create(context.Background(), awsCreds)
+	Expect(err).ToNot(HaveOccurred())
+
+	secretStore := &esv1beta1.ClusterSecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: ReferentSecretStoreName(f),
+		},
+		Spec: esv1beta1.SecretStoreSpec{
+			Provider: newStaticStoreProvider(serviceType, region, StaticReferentCredentialsSecretName),
 		},
 		},
 	}
 	}
 	err = f.CRClient.Create(context.Background(), secretStore)
 	err = f.CRClient.Create(context.Background(), secretStore)
 	Expect(err).ToNot(HaveOccurred())
 	Expect(err).ToNot(HaveOccurred())
 }
 }
+
+func ReferentSecretStoreName(f *framework.Framework) string {
+	return "referent-auth" + f.Namespace.Name
+}

+ 41 - 18
e2e/suites/provider/cases/aws/parameterstore/parameterstore.go

@@ -20,7 +20,14 @@ import (
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/ginkgo/v2"
 
 
 	"github.com/external-secrets/external-secrets-e2e/framework"
 	"github.com/external-secrets/external-secrets-e2e/framework"
+	awscommon "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
 	"github.com/external-secrets/external-secrets-e2e/suites/provider/cases/common"
 	"github.com/external-secrets/external-secrets-e2e/suites/provider/cases/common"
+	esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+const (
+	withStaticAuth         = "with static auth"
+	withReferentStaticAuth = "with static referent auth"
 )
 )
 
 
 var _ = Describe("[aws] ", Label("aws", "parameterstore"), func() {
 var _ = Describe("[aws] ", Label("aws", "parameterstore"), func() {
@@ -30,25 +37,41 @@ var _ = Describe("[aws] ", Label("aws", "parameterstore"), func() {
 	DescribeTable("sync secrets",
 	DescribeTable("sync secrets",
 		framework.TableFunc(f,
 		framework.TableFunc(f,
 			prov),
 			prov),
-		Entry(common.SimpleDataSync(f)),
-		Entry(common.NestedJSONWithGJSON(f)),
-		Entry(common.JSONDataFromSync(f)),
-		Entry(common.JSONDataFromRewrite(f)),
-		Entry(common.JSONDataWithProperty(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.SyncV1Alpha1(f)),
-		Entry(common.DeletionPolicyDelete(f)),
+		framework.Compose(withStaticAuth, f, common.SimpleDataSync, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.NestedJSONWithGJSON, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.JSONDataFromSync, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.JSONDataFromRewrite, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.JSONDataWithProperty, 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.SyncV1Alpha1, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.DeletionPolicyDelete, useStaticAuth),
+
+		// referent auth
+		framework.Compose(withReferentStaticAuth, f, common.SimpleDataSync, useReferentStaticAuth),
 
 
 		// These are specific to parameterstore
 		// These are specific to parameterstore
-		Entry(FindByName(f)),
-		Entry(FindByNameWithPath(f)),
-		Entry(FindByTag(f)),
-		Entry(FindByTagWithPath(f)),
+		framework.Compose(withStaticAuth, f, FindByName, useStaticAuth),
+		framework.Compose(withStaticAuth, f, FindByNameWithPath, useStaticAuth),
+		framework.Compose(withStaticAuth, f, FindByTag, useStaticAuth),
+		framework.Compose(withStaticAuth, f, FindByTagWithPath, useStaticAuth),
 	)
 	)
 })
 })
+
+func useStaticAuth(tc *framework.TestCase) {
+	tc.ExternalSecret.Spec.SecretStoreRef.Name = awscommon.StaticStoreName
+	if tc.ExternalSecretV1Alpha1 != nil {
+		tc.ExternalSecretV1Alpha1.Spec.SecretStoreRef.Name = awscommon.StaticStoreName
+	}
+}
+
+func useReferentStaticAuth(tc *framework.TestCase) {
+	tc.ExternalSecret.Spec.SecretStoreRef.Name = awscommon.ReferentSecretStoreName(tc.Framework)
+	tc.ExternalSecret.Spec.SecretStoreRef.Kind = esapi.ClusterSecretStoreKind
+}

+ 23 - 11
e2e/suites/provider/cases/aws/parameterstore/provider.go

@@ -34,7 +34,7 @@ import (
 
 
 	"github.com/external-secrets/external-secrets-e2e/framework"
 	"github.com/external-secrets/external-secrets-e2e/framework"
 	"github.com/external-secrets/external-secrets-e2e/framework/log"
 	"github.com/external-secrets/external-secrets-e2e/framework/log"
-	common "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
+	awscommon "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
 	esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
 )
 )
@@ -68,19 +68,15 @@ func NewProvider(f *framework.Framework, kid, sak, st, region, saName, saNamespa
 	}
 	}
 
 
 	BeforeEach(func() {
 	BeforeEach(func() {
-		common.SetupStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceParameterStore)
+		awscommon.SetupStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceParameterStore)
+		awscommon.CreateReferentStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceParameterStore)
 		prov.SetupReferencedIRSAStore()
 		prov.SetupReferencedIRSAStore()
 		prov.SetupMountedIRSAStore()
 		prov.SetupMountedIRSAStore()
 	})
 	})
 
 
 	AfterEach(func() {
 	AfterEach(func() {
-		// Cleanup ClusterSecretStore
-		err := prov.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
-			ObjectMeta: metav1.ObjectMeta{
-				Name: common.ReferencedIRSAStoreName(f),
-			},
-		})
-		Expect(err).ToNot(HaveOccurred())
+		prov.TeardownReferencedIRSAStore()
+		prov.TeardownMountedIRSAStore()
 	})
 	})
 
 
 	return prov
 	return prov
@@ -132,7 +128,7 @@ func (s *Provider) DeleteSecret(key string) {
 func (s *Provider) SetupMountedIRSAStore() {
 func (s *Provider) SetupMountedIRSAStore() {
 	secretStore := &esv1beta1.SecretStore{
 	secretStore := &esv1beta1.SecretStore{
 		ObjectMeta: metav1.ObjectMeta{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      common.MountedIRSAStoreName(s.framework),
+			Name:      awscommon.MountedIRSAStoreName(s.framework),
 			Namespace: s.framework.Namespace.Name,
 			Namespace: s.framework.Namespace.Name,
 		},
 		},
 		Spec: esv1beta1.SecretStoreSpec{
 		Spec: esv1beta1.SecretStoreSpec{
@@ -149,13 +145,21 @@ func (s *Provider) SetupMountedIRSAStore() {
 	Expect(err).ToNot(HaveOccurred())
 	Expect(err).ToNot(HaveOccurred())
 }
 }
 
 
+func (s *Provider) TeardownMountedIRSAStore() {
+	s.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: awscommon.MountedIRSAStoreName(s.framework),
+		},
+	})
+}
+
 // ReferncedIRSAStore is a ClusterStore
 // ReferncedIRSAStore is a ClusterStore
 // that references a (IRSA-) ServiceAccount in the default namespace.
 // that references a (IRSA-) ServiceAccount in the default namespace.
 func (s *Provider) SetupReferencedIRSAStore() {
 func (s *Provider) SetupReferencedIRSAStore() {
 	log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
 	log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
 	secretStore := &esv1beta1.ClusterSecretStore{
 	secretStore := &esv1beta1.ClusterSecretStore{
 		ObjectMeta: metav1.ObjectMeta{
 		ObjectMeta: metav1.ObjectMeta{
-			Name: common.ReferencedIRSAStoreName(s.framework),
+			Name: awscommon.ReferencedIRSAStoreName(s.framework),
 		},
 		},
 	}
 	}
 	_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
 	_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
@@ -177,3 +181,11 @@ func (s *Provider) SetupReferencedIRSAStore() {
 	})
 	})
 	Expect(err).ToNot(HaveOccurred())
 	Expect(err).ToNot(HaveOccurred())
 }
 }
+
+func (s *Provider) TeardownReferencedIRSAStore() {
+	s.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: awscommon.ReferencedIRSAStoreName(s.framework),
+		},
+	})
+}

+ 24 - 11
e2e/suites/provider/cases/aws/secretsmanager/provider.go

@@ -35,7 +35,7 @@ import (
 
 
 	"github.com/external-secrets/external-secrets-e2e/framework"
 	"github.com/external-secrets/external-secrets-e2e/framework"
 	"github.com/external-secrets/external-secrets-e2e/framework/log"
 	"github.com/external-secrets/external-secrets-e2e/framework/log"
-	common "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
+	awscommon "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
 	esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
 )
 )
@@ -69,19 +69,16 @@ func NewProvider(f *framework.Framework, kid, sak, st, region, saName, saNamespa
 	}
 	}
 
 
 	BeforeEach(func() {
 	BeforeEach(func() {
-		common.SetupStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceSecretsManager)
+		awscommon.SetupStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceSecretsManager)
+		awscommon.CreateReferentStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceSecretsManager)
 		prov.SetupReferencedIRSAStore()
 		prov.SetupReferencedIRSAStore()
 		prov.SetupMountedIRSAStore()
 		prov.SetupMountedIRSAStore()
 	})
 	})
 
 
 	AfterEach(func() {
 	AfterEach(func() {
-		// Cleanup ClusterSecretStore
-		err := prov.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
-			ObjectMeta: metav1.ObjectMeta{
-				Name: common.ReferencedIRSAStoreName(f),
-			},
-		})
-		Expect(err).ToNot(HaveOccurred())
+		prov.TeardownReferencedIRSAStore()
+		prov.TeardownMountedIRSAStore()
+
 	})
 	})
 
 
 	return prov
 	return prov
@@ -150,7 +147,7 @@ func (s *Provider) DeleteSecret(key string) {
 func (s *Provider) SetupMountedIRSAStore() {
 func (s *Provider) SetupMountedIRSAStore() {
 	secretStore := &esv1beta1.SecretStore{
 	secretStore := &esv1beta1.SecretStore{
 		ObjectMeta: metav1.ObjectMeta{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      common.MountedIRSAStoreName(s.framework),
+			Name:      awscommon.MountedIRSAStoreName(s.framework),
 			Namespace: s.framework.Namespace.Name,
 			Namespace: s.framework.Namespace.Name,
 		},
 		},
 		Spec: esv1beta1.SecretStoreSpec{
 		Spec: esv1beta1.SecretStoreSpec{
@@ -167,13 +164,21 @@ func (s *Provider) SetupMountedIRSAStore() {
 	Expect(err).ToNot(HaveOccurred())
 	Expect(err).ToNot(HaveOccurred())
 }
 }
 
 
+func (s *Provider) TeardownMountedIRSAStore() {
+	s.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: awscommon.MountedIRSAStoreName(s.framework),
+		},
+	})
+}
+
 // ReferncedIRSAStore is a ClusterStore
 // ReferncedIRSAStore is a ClusterStore
 // that references a (IRSA-) ServiceAccount in the default namespace.
 // that references a (IRSA-) ServiceAccount in the default namespace.
 func (s *Provider) SetupReferencedIRSAStore() {
 func (s *Provider) SetupReferencedIRSAStore() {
 	log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
 	log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
 	secretStore := &esv1beta1.ClusterSecretStore{
 	secretStore := &esv1beta1.ClusterSecretStore{
 		ObjectMeta: metav1.ObjectMeta{
 		ObjectMeta: metav1.ObjectMeta{
-			Name: common.ReferencedIRSAStoreName(s.framework),
+			Name: awscommon.ReferencedIRSAStoreName(s.framework),
 		},
 		},
 	}
 	}
 	_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
 	_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
@@ -195,3 +200,11 @@ func (s *Provider) SetupReferencedIRSAStore() {
 	})
 	})
 	Expect(err).ToNot(HaveOccurred())
 	Expect(err).ToNot(HaveOccurred())
 }
 }
+
+func (s *Provider) TeardownReferencedIRSAStore() {
+	s.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: awscommon.ReferencedIRSAStoreName(s.framework),
+		},
+	})
+}

+ 40 - 18
e2e/suites/provider/cases/aws/secretsmanager/secretsmanager.go

@@ -20,7 +20,14 @@ import (
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/ginkgo/v2"
 
 
 	"github.com/external-secrets/external-secrets-e2e/framework"
 	"github.com/external-secrets/external-secrets-e2e/framework"
+	awscommon "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
 	"github.com/external-secrets/external-secrets-e2e/suites/provider/cases/common"
 	"github.com/external-secrets/external-secrets-e2e/suites/provider/cases/common"
+	esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+const (
+	withStaticAuth         = "with static auth"
+	withReferentStaticAuth = "with static referent auth"
 )
 )
 
 
 var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
 var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
@@ -30,23 +37,38 @@ var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
 	DescribeTable("sync secrets",
 	DescribeTable("sync secrets",
 		framework.TableFunc(f,
 		framework.TableFunc(f,
 			prov),
 			prov),
-		Entry(common.SimpleDataSync(f)),
-		Entry(common.NestedJSONWithGJSON(f)),
-		Entry(common.JSONDataFromSync(f)),
-		Entry(common.JSONDataFromRewrite(f)),
-		Entry(common.JSONDataWithProperty(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.FindByNameWithPath(f)),
-		Entry(common.FindByTag(f)),
-		Entry(common.FindByTagWithPath(f)),
-		Entry(common.SyncV1Alpha1(f)),
-		Entry(common.DeletionPolicyDelete(f)),
+		framework.Compose(withStaticAuth, f, common.SimpleDataSync, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.NestedJSONWithGJSON, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.JSONDataFromSync, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.JSONDataFromRewrite, useStaticAuth),
+		framework.Compose(withStaticAuth, f, common.JSONDataWithProperty, 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.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, common.DeletionPolicyDelete, useStaticAuth),
+
+		// referent auth
+		framework.Compose(withStaticAuth, f, common.SimpleDataSync, useReferentStaticAuth),
 	)
 	)
 })
 })
+
+func useStaticAuth(tc *framework.TestCase) {
+	tc.ExternalSecret.Spec.SecretStoreRef.Name = awscommon.StaticStoreName
+	if tc.ExternalSecretV1Alpha1 != nil {
+		tc.ExternalSecretV1Alpha1.Spec.SecretStoreRef.Name = awscommon.StaticStoreName
+	}
+}
+
+func useReferentStaticAuth(tc *framework.TestCase) {
+	tc.ExternalSecret.Spec.SecretStoreRef.Name = awscommon.ReferentSecretStoreName(tc.Framework)
+	tc.ExternalSecret.Spec.SecretStoreRef.Kind = esapi.ClusterSecretStoreKind
+}

+ 23 - 26
pkg/provider/aws/auth/auth.go

@@ -87,7 +87,7 @@ func New(ctx context.Context, store esv1beta1.GenericStore, kube client.Client,
 	// use credentials via service account token
 	// use credentials via service account token
 	jwtAuth := prov.Auth.JWTAuth
 	jwtAuth := prov.Auth.JWTAuth
 	if jwtAuth != nil {
 	if jwtAuth != nil {
-		creds, err = sessionFromServiceAccount(ctx, prov.Auth, prov.Region, isClusterKind, kube, namespace, jwtProvider)
+		creds, err = credsFromServiceAccount(ctx, prov.Auth, prov.Region, isClusterKind, kube, namespace, jwtProvider)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
@@ -97,7 +97,7 @@ func New(ctx context.Context, store esv1beta1.GenericStore, kube client.Client,
 	secretRef := prov.Auth.SecretRef
 	secretRef := prov.Auth.SecretRef
 	if secretRef != nil {
 	if secretRef != nil {
 		log.V(1).Info("using credentials from secretRef")
 		log.V(1).Info("using credentials from secretRef")
-		creds, err = sessionFromSecretRef(ctx, prov.Auth, isClusterKind, kube, namespace)
+		creds, err = credsFromSecretRef(ctx, prov.Auth, isClusterKind, kube, namespace)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
@@ -141,7 +141,7 @@ func NewGeneratorSession(ctx context.Context, auth esv1beta1.AWSAuth, role, regi
 	// use credentials via service account token
 	// use credentials via service account token
 	jwtAuth := auth.JWTAuth
 	jwtAuth := auth.JWTAuth
 	if jwtAuth != nil {
 	if jwtAuth != nil {
-		creds, err = sessionFromServiceAccount(ctx, auth, region, false, kube, namespace, jwtProvider)
+		creds, err = credsFromServiceAccount(ctx, auth, region, false, kube, namespace, jwtProvider)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
@@ -151,7 +151,7 @@ func NewGeneratorSession(ctx context.Context, auth esv1beta1.AWSAuth, role, regi
 	secretRef := auth.SecretRef
 	secretRef := auth.SecretRef
 	if secretRef != nil {
 	if secretRef != nil {
 		log.V(1).Info("using credentials from secretRef")
 		log.V(1).Info("using credentials from secretRef")
-		creds, err = sessionFromSecretRef(ctx, auth, false, kube, namespace)
+		creds, err = credsFromSecretRef(ctx, auth, false, kube, namespace)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
@@ -178,16 +178,16 @@ func NewGeneratorSession(ctx context.Context, auth esv1beta1.AWSAuth, role, regi
 	return sess, nil
 	return sess, nil
 }
 }
 
 
-func sessionFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, isClusterKind bool, kube client.Client, namespace string) (*credentials.Credentials, error) {
+// credsFromSecretRef pulls access-key / secret-access-key from a secretRef to
+// construct a aws.Credentials object
+// The namespace of the external secret is used if the ClusterSecretStore does not specify a namespace (referentAuth)
+// If the ClusterSecretStore defines a namespace it will take precedence.
+func credsFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, isClusterKind bool, kube client.Client, namespace string) (*credentials.Credentials, error) {
 	ke := client.ObjectKey{
 	ke := client.ObjectKey{
 		Name:      auth.SecretRef.AccessKeyID.Name,
 		Name:      auth.SecretRef.AccessKeyID.Name,
-		Namespace: namespace, // default to ExternalSecret namespace
+		Namespace: namespace,
 	}
 	}
-	// only ClusterStore is allowed to set namespace (and then it's required)
-	if isClusterKind {
-		if auth.SecretRef.AccessKeyID.Namespace == nil {
-			return nil, fmt.Errorf(errInvalidClusterStoreMissingAKIDNamespace)
-		}
+	if isClusterKind && auth.SecretRef.AccessKeyID.Namespace != nil {
 		ke.Namespace = *auth.SecretRef.AccessKeyID.Namespace
 		ke.Namespace = *auth.SecretRef.AccessKeyID.Namespace
 	}
 	}
 	akSecret := v1.Secret{}
 	akSecret := v1.Secret{}
@@ -197,13 +197,9 @@ func sessionFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, isCluster
 	}
 	}
 	ke = client.ObjectKey{
 	ke = client.ObjectKey{
 		Name:      auth.SecretRef.SecretAccessKey.Name,
 		Name:      auth.SecretRef.SecretAccessKey.Name,
-		Namespace: namespace, // default to ExternalSecret namespace
+		Namespace: namespace,
 	}
 	}
-	// only ClusterStore is allowed to set namespace (and then it's required)
-	if isClusterKind {
-		if auth.SecretRef.SecretAccessKey.Namespace == nil {
-			return nil, fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
-		}
+	if isClusterKind && auth.SecretRef.SecretAccessKey.Namespace != nil {
 		ke.Namespace = *auth.SecretRef.SecretAccessKey.Namespace
 		ke.Namespace = *auth.SecretRef.SecretAccessKey.Namespace
 	}
 	}
 	sakSecret := v1.Secret{}
 	sakSecret := v1.Secret{}
@@ -224,13 +220,9 @@ func sessionFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, isCluster
 	if auth.SecretRef.SessionToken != nil {
 	if auth.SecretRef.SessionToken != nil {
 		ke = client.ObjectKey{
 		ke = client.ObjectKey{
 			Name:      auth.SecretRef.SessionToken.Name,
 			Name:      auth.SecretRef.SessionToken.Name,
-			Namespace: namespace, // default to ExternalSecret namespace
+			Namespace: namespace,
 		}
 		}
-		// only ClusterStore is allowed to set namespace (and then it's required)
-		if isClusterKind {
-			if auth.SecretRef.SessionToken.Namespace == nil {
-				return nil, fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
-			}
+		if isClusterKind && auth.SecretRef.SessionToken.Namespace != nil {
 			ke.Namespace = *auth.SecretRef.SessionToken.Namespace
 			ke.Namespace = *auth.SecretRef.SessionToken.Namespace
 		}
 		}
 		stSecret := v1.Secret{}
 		stSecret := v1.Secret{}
@@ -244,9 +236,14 @@ func sessionFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, isCluster
 	return credentials.NewStaticCredentials(aks, sak, sessionToken), err
 	return credentials.NewStaticCredentials(aks, sak, sessionToken), err
 }
 }
 
 
-func sessionFromServiceAccount(ctx context.Context, auth esv1beta1.AWSAuth, region string, isClusterKind bool, kube client.Client, namespace string, jwtProvider jwtProviderFactory) (*credentials.Credentials, error) {
+// credsFromServiceAccount uses a Kubernetes Service Account to acquire temporary
+// credentials using aws.AssumeRoleWithWebIdentity. It will assume the role defined
+// in the ServiceAccount annotation.
+// If the ClusterSecretStore does not define a namespace it will use the namespace from the ExternalSecret (referentAuth).
+// If the ClusterSecretStore defines the namespace it will take precedence.
+func credsFromServiceAccount(ctx context.Context, auth esv1beta1.AWSAuth, region string, isClusterKind bool, kube client.Client, namespace string, jwtProvider jwtProviderFactory) (*credentials.Credentials, error) {
 	name := auth.JWTAuth.ServiceAccountRef.Name
 	name := auth.JWTAuth.ServiceAccountRef.Name
-	if isClusterKind {
+	if isClusterKind && auth.JWTAuth.ServiceAccountRef.Namespace != nil {
 		namespace = *auth.JWTAuth.ServiceAccountRef.Namespace
 		namespace = *auth.JWTAuth.ServiceAccountRef.Namespace
 	}
 	}
 	sa := v1.ServiceAccount{}
 	sa := v1.ServiceAccount{}
@@ -327,7 +324,7 @@ func DefaultSTSProvider(sess *session.Session) stsiface.STSAPI {
 	return sts.New(sess)
 	return sts.New(sess)
 }
 }
 
 
-// getAWSSession check if an AWS session should be reused
+// getAWSSession checks if an AWS session should be reused
 // it returns the aws session or an error.
 // it returns the aws session or an error.
 func getAWSSession(config *aws.Config, enableCache bool, name, kind, namespace, resourceVersion string) (*session.Session, error) {
 func getAWSSession(config *aws.Config, enableCache bool, name, kind, namespace, resourceVersion string) (*session.Session, error) {
 	tmpSession := SessionCache{
 	tmpSession := SessionCache{

+ 16 - 2
pkg/provider/aws/auth/auth_test.go

@@ -311,7 +311,7 @@ func TestNewSession(t *testing.T) {
 			expectedSecretKey: "2222",
 			expectedSecretKey: "2222",
 		},
 		},
 		{
 		{
-			name:      "namespace is mandatory when using ClusterStore with SecretKeySelector",
+			name:      "ClusterStore should use credentials from a ExternalSecret namespace (referentAuth)",
 			namespace: esNamespaceKey,
 			namespace: esNamespaceKey,
 			store: &esv1beta1.ClusterSecretStore{
 			store: &esv1beta1.ClusterSecretStore{
 				TypeMeta: metav1.TypeMeta{
 				TypeMeta: metav1.TypeMeta{
@@ -337,7 +337,21 @@ func TestNewSession(t *testing.T) {
 					},
 					},
 				},
 				},
 			},
 			},
-			expectErr: "invalid ClusterSecretStore: missing AWS AccessKeyID Namespace",
+			secrets: []v1.Secret{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "onesecret",
+						Namespace: esNamespaceKey,
+					},
+					Data: map[string][]byte{
+						"one": []byte("7777"),
+						"two": []byte("4444"),
+					},
+				},
+			},
+			expectProvider:    true,
+			expectedKeyID:     "7777",
+			expectedSecretKey: "4444",
 		},
 		},
 		{
 		{
 			name:      "jwt auth via cluster secret store",
 			name:      "jwt auth via cluster secret store",

+ 12 - 5
pkg/provider/aws/parameterstore/parameterstore.go

@@ -42,8 +42,9 @@ var (
 
 
 // ParameterStore is a provider for AWS ParameterStore.
 // ParameterStore is a provider for AWS ParameterStore.
 type ParameterStore struct {
 type ParameterStore struct {
-	sess   *session.Session
-	client PMInterface
+	sess         *session.Session
+	client       PMInterface
+	referentAuth bool
 }
 }
 
 
 // PMInterface is a subset of the parameterstore api.
 // PMInterface is a subset of the parameterstore api.
@@ -61,10 +62,11 @@ const (
 )
 )
 
 
 // New constructs a ParameterStore Provider that is specific to a store.
 // New constructs a ParameterStore Provider that is specific to a store.
-func New(sess *session.Session, cfg *aws.Config) (*ParameterStore, error) {
+func New(sess *session.Session, cfg *aws.Config, referentAuth bool) (*ParameterStore, error) {
 	return &ParameterStore{
 	return &ParameterStore{
-		sess:   sess,
-		client: ssm.New(sess, cfg),
+		sess:         sess,
+		referentAuth: referentAuth,
+		client:       ssm.New(sess, cfg),
 	}, nil
 	}, nil
 }
 }
 
 
@@ -377,6 +379,11 @@ func (pm *ParameterStore) Close(ctx context.Context) error {
 }
 }
 
 
 func (pm *ParameterStore) Validate() (esv1beta1.ValidationResult, error) {
 func (pm *ParameterStore) Validate() (esv1beta1.ValidationResult, error) {
+	// skip validation stack because it depends on the namespace
+	// of the ExternalSecret
+	if pm.referentAuth {
+		return esv1beta1.ValidationResultUnknown, nil
+	}
 	_, err := pm.sess.Config.Credentials.Get()
 	_, err := pm.sess.Config.Credentials.Get()
 	if err != nil {
 	if err != nil {
 		return esv1beta1.ValidationResultError, err
 		return esv1beta1.ValidationResultError, err

+ 26 - 5
pkg/provider/aws/provider.go

@@ -23,6 +23,7 @@ import (
 	awsclient "github.com/aws/aws-sdk-go/aws/client"
 	awsclient "github.com/aws/aws-sdk-go/aws/client"
 	"github.com/aws/aws-sdk-go/aws/endpoints"
 	"github.com/aws/aws-sdk-go/aws/endpoints"
 	"github.com/aws/aws-sdk-go/aws/request"
 	"github.com/aws/aws-sdk-go/aws/request"
+	"github.com/aws/aws-sdk-go/aws/session"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@@ -68,17 +69,22 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) error {
 
 
 	// case: static credentials
 	// case: static credentials
 	if prov.Auth.SecretRef != nil {
 	if prov.Auth.SecretRef != nil {
-		if err := utils.ValidateSecretSelector(store, prov.Auth.SecretRef.AccessKeyID); err != nil {
+		if err := utils.ValidateReferentSecretSelector(store, prov.Auth.SecretRef.AccessKeyID); err != nil {
 			return fmt.Errorf("invalid Auth.SecretRef.AccessKeyID: %w", err)
 			return fmt.Errorf("invalid Auth.SecretRef.AccessKeyID: %w", err)
 		}
 		}
-		if err := utils.ValidateSecretSelector(store, prov.Auth.SecretRef.SecretAccessKey); err != nil {
+		if err := utils.ValidateReferentSecretSelector(store, prov.Auth.SecretRef.SecretAccessKey); err != nil {
 			return fmt.Errorf("invalid Auth.SecretRef.SecretAccessKey: %w", err)
 			return fmt.Errorf("invalid Auth.SecretRef.SecretAccessKey: %w", err)
 		}
 		}
+		if prov.Auth.SecretRef.SessionToken != nil {
+			if err := utils.ValidateReferentSecretSelector(store, *prov.Auth.SecretRef.SessionToken); err != nil {
+				return fmt.Errorf("invalid Auth.SecretRef.SessionToken: %w", err)
+			}
+		}
 	}
 	}
 
 
 	// case: jwt credentials
 	// case: jwt credentials
 	if prov.Auth.JWTAuth != nil && prov.Auth.JWTAuth.ServiceAccountRef != nil {
 	if prov.Auth.JWTAuth != nil && prov.Auth.JWTAuth.ServiceAccountRef != nil {
-		if err := utils.ValidateServiceAccountSelector(store, *prov.Auth.JWTAuth.ServiceAccountRef); err != nil {
+		if err := utils.ValidateReferentServiceAccountSelector(store, *prov.Auth.JWTAuth.ServiceAccountRef); err != nil {
 			return fmt.Errorf("invalid Auth.JWT.ServiceAccountRef: %w", err)
 			return fmt.Errorf("invalid Auth.JWT.ServiceAccountRef: %w", err)
 		}
 		}
 	}
 	}
@@ -114,6 +120,21 @@ func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Cl
 	storeSpec := store.GetSpec()
 	storeSpec := store.GetSpec()
 	var cfg *aws.Config
 	var cfg *aws.Config
 
 
+	// allow SecretStore controller validation to pass
+	// when using referent namespace.
+	if util.IsReferentSpec(prov.Auth) && namespace == "" &&
+		store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind {
+		cfg = aws.NewConfig().WithRegion("eu-west-1").WithEndpointResolver(awsauth.ResolveEndpoint())
+		sess := &session.Session{Config: cfg}
+		switch prov.Service {
+		case esv1beta1.AWSServiceSecretsManager:
+			return secretsmanager.New(sess, cfg, true)
+		case esv1beta1.AWSServiceParameterStore:
+			return parameterstore.New(sess, cfg, true)
+		}
+		return nil, fmt.Errorf(errUnknownProviderService, prov.Service)
+	}
+
 	sess, err := awsauth.New(ctx, store, kube, namespace, assumeRoler, awsauth.DefaultJWTProvider)
 	sess, err := awsauth.New(ctx, store, kube, namespace, assumeRoler, awsauth.DefaultJWTProvider)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf(errUnableCreateSession, err)
 		return nil, fmt.Errorf(errUnableCreateSession, err)
@@ -146,9 +167,9 @@ func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Cl
 
 
 	switch prov.Service {
 	switch prov.Service {
 	case esv1beta1.AWSServiceSecretsManager:
 	case esv1beta1.AWSServiceSecretsManager:
-		return secretsmanager.New(sess, cfg)
+		return secretsmanager.New(sess, cfg, false)
 	case esv1beta1.AWSServiceParameterStore:
 	case esv1beta1.AWSServiceParameterStore:
-		return parameterstore.New(sess, cfg)
+		return parameterstore.New(sess, cfg, false)
 	}
 	}
 	return nil, fmt.Errorf(errUnknownProviderService, prov.Service)
 	return nil, fmt.Errorf(errUnknownProviderService, prov.Service)
 }
 }

+ 6 - 6
pkg/provider/aws/provider_test.go

@@ -238,8 +238,8 @@ func TestValidateStore(t *testing.T) {
 			},
 			},
 		},
 		},
 		{
 		{
-			name:    "invalid static creds auth / SecretAccessKey missing namespace",
-			wantErr: true,
+			name:    "referentAuth static creds / SecretAccessKey without namespace",
+			wantErr: false,
 			args: args{
 			args: args{
 				store: &esv1beta1.ClusterSecretStore{
 				store: &esv1beta1.ClusterSecretStore{
 					TypeMeta: v1.TypeMeta{
 					TypeMeta: v1.TypeMeta{
@@ -263,8 +263,8 @@ func TestValidateStore(t *testing.T) {
 			},
 			},
 		},
 		},
 		{
 		{
-			name:    "invalid static creds auth / AccessKeyID missing namespace",
-			wantErr: true,
+			name:    "referentAuth static creds / AccessKeyID without namespace",
+			wantErr: false,
 			args: args{
 			args: args{
 				store: &esv1beta1.ClusterSecretStore{
 				store: &esv1beta1.ClusterSecretStore{
 					TypeMeta: v1.TypeMeta{
 					TypeMeta: v1.TypeMeta{
@@ -288,8 +288,8 @@ func TestValidateStore(t *testing.T) {
 			},
 			},
 		},
 		},
 		{
 		{
-			name:    "invalid jwt auth: missing sa selector namespace",
-			wantErr: true,
+			name:    "referentAuth jwt: sa selector without namespace",
+			wantErr: false,
 			args: args{
 			args: args{
 				store: &esv1beta1.ClusterSecretStore{
 				store: &esv1beta1.ClusterSecretStore{
 					TypeMeta: v1.TypeMeta{
 					TypeMeta: v1.TypeMeta{

+ 14 - 7
pkg/provider/aws/secretsmanager/secretsmanager.go

@@ -41,9 +41,10 @@ var _ esv1beta1.SecretsClient = &SecretsManager{}
 
 
 // SecretsManager is a provider for AWS SecretsManager.
 // SecretsManager is a provider for AWS SecretsManager.
 type SecretsManager struct {
 type SecretsManager struct {
-	sess   *session.Session
-	client SMInterface
-	cache  map[string]*awssm.GetSecretValueOutput
+	sess         *session.Session
+	client       SMInterface
+	referentAuth bool
+	cache        map[string]*awssm.GetSecretValueOutput
 }
 }
 
 
 // SMInterface is a subset of the smiface api.
 // SMInterface is a subset of the smiface api.
@@ -67,11 +68,12 @@ const (
 var log = ctrl.Log.WithName("provider").WithName("aws").WithName("secretsmanager")
 var log = ctrl.Log.WithName("provider").WithName("aws").WithName("secretsmanager")
 
 
 // New creates a new SecretsManager client.
 // New creates a new SecretsManager client.
-func New(sess *session.Session, cfg *aws.Config) (*SecretsManager, error) {
+func New(sess *session.Session, cfg *aws.Config, referentAuth bool) (*SecretsManager, error) {
 	return &SecretsManager{
 	return &SecretsManager{
-		sess:   sess,
-		client: awssm.New(sess, cfg),
-		cache:  make(map[string]*awssm.GetSecretValueOutput),
+		sess:         sess,
+		client:       awssm.New(sess, cfg),
+		referentAuth: referentAuth,
+		cache:        make(map[string]*awssm.GetSecretValueOutput),
 	}, nil
 	}, nil
 }
 }
 
 
@@ -407,6 +409,11 @@ func (sm *SecretsManager) Close(ctx context.Context) error {
 }
 }
 
 
 func (sm *SecretsManager) Validate() (esv1beta1.ValidationResult, error) {
 func (sm *SecretsManager) Validate() (esv1beta1.ValidationResult, error) {
+	// skip validation stack because it depends on the namespace
+	// of the ExternalSecret
+	if sm.referentAuth {
+		return esv1beta1.ValidationResultUnknown, nil
+	}
 	_, err := sm.sess.Config.Credentials.Get()
 	_, err := sm.sess.Config.Credentials.Get()
 	if err != nil {
 	if err != nil {
 		return esv1beta1.ValidationResultError, err
 		return esv1beta1.ValidationResultError, err

+ 14 - 0
pkg/provider/aws/util/provider.go

@@ -45,3 +45,17 @@ func GetAWSProvider(store esv1beta1.GenericStore) (*esv1beta1.AWSProvider, error
 	}
 	}
 	return prov, nil
 	return prov, nil
 }
 }
+
+func IsReferentSpec(prov esv1beta1.AWSAuth) bool {
+	if prov.JWTAuth != nil && prov.JWTAuth.ServiceAccountRef != nil && prov.JWTAuth.ServiceAccountRef.Namespace == nil {
+		return true
+	}
+	if prov.SecretRef != nil &&
+		(prov.SecretRef.AccessKeyID.Namespace == nil ||
+			prov.SecretRef.SecretAccessKey.Namespace == nil ||
+			(prov.SecretRef.SessionToken != nil && prov.SecretRef.SessionToken.Namespace == nil)) {
+		return true
+	}
+
+	return false
+}

+ 1 - 1
terraform/aws/modules/cluster/main.tf

@@ -4,7 +4,7 @@ provider "aws" {
 
 
 locals {
 locals {
   name            = var.cluster_name
   name            = var.cluster_name
-  cluster_version = "1.22"
+  cluster_version = "1.24"
   region          = var.cluster_region
   region          = var.cluster_region
 
 
   serviceaccount_name      = var.irsa_sa_name
   serviceaccount_name      = var.irsa_sa_name