Browse Source

feat(aws): add jwt authentication

Moritz Johner 4 years ago
parent
commit
466938522c
34 changed files with 1451 additions and 843 deletions
  1. 2 2
      .github/workflows/e2e.yml
  2. 14 5
      apis/externalsecrets/v1alpha1/secretstore_aws_types.go
  3. 30 5
      apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go
  4. 6 0
      deploy/charts/external-secrets/templates/rbac.yaml
  5. 22 4
      deploy/crds/external-secrets.io_clustersecretstores.yaml
  6. 22 4
      deploy/crds/external-secrets.io_secretstores.yaml
  7. BIN
      docs/pictures/diagrams-provider-aws-auth-pod-identity.png
  8. BIN
      docs/pictures/diagrams-provider-aws-auth-secret-ref.png
  9. BIN
      docs/pictures/diagrams-provider-aws-auth-service-account.png
  10. 1 1
      docs/pictures/diagrams.drawio
  11. 81 5
      docs/snippets/provider-aws-access.md
  12. 47 2
      docs/spec.md
  13. 1 1
      e2e/Makefile
  14. 4 0
      e2e/k8s/eso.values.yaml
  15. 2 0
      e2e/k8s/localstack.values.yaml
  16. 8 0
      e2e/kind.yaml
  17. 4 4
      e2e/suite/aws/provider.go
  18. 87 1
      e2e/suite/aws/secretsmanager.go
  19. 231 0
      pkg/provider/aws/auth/auth.go
  20. 534 0
      pkg/provider/aws/auth/auth_test.go
  21. 25 1
      pkg/provider/aws/session/fake/assumeroler.go
  22. 54 0
      pkg/provider/aws/auth/resolver.go
  23. 58 0
      pkg/provider/aws/auth/resolver_test.go
  24. 50 0
      pkg/provider/aws/auth/token_fetcher.go
  25. 63 0
      pkg/provider/aws/auth/token_fetcher_test.go
  26. 0 10
      pkg/provider/aws/parameterstore/parameterstore_test.go
  27. 8 136
      pkg/provider/aws/provider.go
  28. 2 481
      pkg/provider/aws/provider_test.go
  29. 0 10
      pkg/provider/aws/secretsmanager/secretsmanager_test.go
  30. 0 71
      pkg/provider/aws/session/session.go
  31. 0 91
      pkg/provider/aws/session/session_test.go
  32. 6 9
      pkg/provider/aws/util/errors.go
  33. 42 0
      pkg/provider/aws/util/errors_test.go
  34. 47 0
      pkg/provider/aws/util/provider.go

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

@@ -68,7 +68,7 @@ jobs:
       with:
       with:
         version: "v0.11.1"
         version: "v0.11.1"
         wait: 10m
         wait: 10m
-        node_image: kindest/node:v1.20.2
+        node_image: kindest/node:v1.20.7
         name: external-secrets
         name: external-secrets
 
 
     - name: Run e2e Tests
     - name: Run e2e Tests
@@ -125,7 +125,7 @@ jobs:
       with:
       with:
         version: "v0.11.1"
         version: "v0.11.1"
         wait: 10m
         wait: 10m
-        node_image: kindest/node:v1.20.2
+        node_image: kindest/node:v1.20.7
         name: external-secrets
         name: external-secrets
 
 
     - name: Run e2e Tests
     - name: Run e2e Tests

+ 14 - 5
apis/externalsecrets/v1alpha1/secretstore_aws_types.go

@@ -18,12 +18,17 @@ import (
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 )
 )
 
 
-// AWSAuth contains a secretRef for credentials.
+// AWSAuth tells the controller how to do authentication with aws.
+// Only one of secretRef or jwt can be specified.
+// if none is specified the controller will load credentials using the aws sdk defaults.
 type AWSAuth struct {
 type AWSAuth struct {
-	SecretRef AWSAuthSecretRef `json:"secretRef"`
+	// +optional
+	SecretRef *AWSAuthSecretRef `json:"secretRef,omitempty"`
+	// +optional
+	JWTAuth *AWSJWTAuth `json:"jwt,omitempty"`
 }
 }
 
 
-// AWSAuthSecretRef holds secret references for aws credentials
+// AWSAuthSecretRef holds secret references for AWS credentials
 // both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
 // both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
 type AWSAuthSecretRef struct {
 type AWSAuthSecretRef struct {
 	// The AccessKeyID is used for authentication
 	// The AccessKeyID is used for authentication
@@ -33,6 +38,11 @@ type AWSAuthSecretRef struct {
 	SecretAccessKey esmeta.SecretKeySelector `json:"secretAccessKeySecretRef,omitempty"`
 	SecretAccessKey esmeta.SecretKeySelector `json:"secretAccessKeySecretRef,omitempty"`
 }
 }
 
 
+// Authenticate against AWS using service account tokens.
+type AWSJWTAuth struct {
+	ServiceAccountRef *esmeta.ServiceAccountSelector `json:"serviceAccountRef,omitempty"`
+}
+
 // AWSServiceType is a enum that defines the service/API that is used to fetch the secrets.
 // AWSServiceType is a enum that defines the service/API that is used to fetch the secrets.
 // +kubebuilder:validation:Enum=SecretsManager;ParameterStore
 // +kubebuilder:validation:Enum=SecretsManager;ParameterStore
 type AWSServiceType string
 type AWSServiceType string
@@ -54,9 +64,8 @@ type AWSProvider struct {
 	// Auth defines the information necessary to authenticate against AWS
 	// Auth defines the information necessary to authenticate against AWS
 	// if not set aws sdk will infer credentials from your environment
 	// if not set aws sdk will infer credentials from your environment
 	// see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials
 	// see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials
-	// +nullable
 	// +optional
 	// +optional
-	Auth *AWSAuth `json:"auth"`
+	Auth AWSAuth `json:"auth"`
 
 
 	// Role is a Role ARN which the SecretManager provider will assume
 	// Role is a Role ARN which the SecretManager provider will assume
 	// +optional
 	// +optional

+ 30 - 5
apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go

@@ -27,7 +27,16 @@ import (
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *AWSAuth) DeepCopyInto(out *AWSAuth) {
 func (in *AWSAuth) DeepCopyInto(out *AWSAuth) {
 	*out = *in
 	*out = *in
-	in.SecretRef.DeepCopyInto(&out.SecretRef)
+	if in.SecretRef != nil {
+		in, out := &in.SecretRef, &out.SecretRef
+		*out = new(AWSAuthSecretRef)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.JWTAuth != nil {
+		in, out := &in.JWTAuth, &out.JWTAuth
+		*out = new(AWSJWTAuth)
+		(*in).DeepCopyInto(*out)
+	}
 }
 }
 
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSAuth.
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSAuth.
@@ -58,15 +67,31 @@ func (in *AWSAuthSecretRef) DeepCopy() *AWSAuthSecretRef {
 }
 }
 
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *AWSProvider) DeepCopyInto(out *AWSProvider) {
+func (in *AWSJWTAuth) DeepCopyInto(out *AWSJWTAuth) {
 	*out = *in
 	*out = *in
-	if in.Auth != nil {
-		in, out := &in.Auth, &out.Auth
-		*out = new(AWSAuth)
+	if in.ServiceAccountRef != nil {
+		in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
+		*out = new(metav1.ServiceAccountSelector)
 		(*in).DeepCopyInto(*out)
 		(*in).DeepCopyInto(*out)
 	}
 	}
 }
 }
 
 
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSJWTAuth.
+func (in *AWSJWTAuth) DeepCopy() *AWSJWTAuth {
+	if in == nil {
+		return nil
+	}
+	out := new(AWSJWTAuth)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *AWSProvider) DeepCopyInto(out *AWSProvider) {
+	*out = *in
+	in.Auth.DeepCopyInto(&out.Auth)
+}
+
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSProvider.
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSProvider.
 func (in *AWSProvider) DeepCopy() *AWSProvider {
 func (in *AWSProvider) DeepCopy() *AWSProvider {
 	if in == nil {
 	if in == nil {

+ 6 - 0
deploy/charts/external-secrets/templates/rbac.yaml

@@ -54,6 +54,12 @@ rules:
   - apiGroups:
   - apiGroups:
     - ""
     - ""
     resources:
     resources:
+    - "serviceaccounts/token"
+    verbs:
+    - "create"
+  - apiGroups:
+    - ""
+    resources:
     - "events"
     - "events"
     verbs:
     verbs:
     - "create"
     - "create"

+ 22 - 4
deploy/crds/external-secrets.io_clustersecretstores.yaml

@@ -62,11 +62,31 @@ spec:
                         description: 'Auth defines the information necessary to authenticate
                         description: 'Auth defines the information necessary to authenticate
                           against AWS if not set aws sdk will infer credentials from
                           against AWS if not set aws sdk will infer credentials from
                           your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials'
                           your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials'
-                        nullable: true
                         properties:
                         properties:
+                          jwt:
+                            description: Authenticate against AWS using service account
+                              tokens.
+                            properties:
+                              serviceAccountRef:
+                                description: A reference to a ServiceAccount resource.
+                                properties:
+                                  name:
+                                    description: The name of the ServiceAccount resource
+                                      being referred to.
+                                    type: string
+                                  namespace:
+                                    description: Namespace of the resource being referred
+                                      to. Ignored if referent is not cluster-scoped.
+                                      cluster-scoped defaults to the namespace of
+                                      the referent.
+                                    type: string
+                                required:
+                                - name
+                                type: object
+                            type: object
                           secretRef:
                           secretRef:
                             description: AWSAuthSecretRef holds secret references
                             description: AWSAuthSecretRef holds secret references
-                              for aws credentials both AccessKeyID and SecretAccessKey
+                              for AWS credentials both AccessKeyID and SecretAccessKey
                               must be defined in order to properly authenticate.
                               must be defined in order to properly authenticate.
                             properties:
                             properties:
                               accessKeyIDSecretRef:
                               accessKeyIDSecretRef:
@@ -114,8 +134,6 @@ spec:
                                 - name
                                 - name
                                 type: object
                                 type: object
                             type: object
                             type: object
-                        required:
-                        - secretRef
                         type: object
                         type: object
                       region:
                       region:
                         description: AWS Region to be used for the provider
                         description: AWS Region to be used for the provider

+ 22 - 4
deploy/crds/external-secrets.io_secretstores.yaml

@@ -62,11 +62,31 @@ spec:
                         description: 'Auth defines the information necessary to authenticate
                         description: 'Auth defines the information necessary to authenticate
                           against AWS if not set aws sdk will infer credentials from
                           against AWS if not set aws sdk will infer credentials from
                           your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials'
                           your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials'
-                        nullable: true
                         properties:
                         properties:
+                          jwt:
+                            description: Authenticate against AWS using service account
+                              tokens.
+                            properties:
+                              serviceAccountRef:
+                                description: A reference to a ServiceAccount resource.
+                                properties:
+                                  name:
+                                    description: The name of the ServiceAccount resource
+                                      being referred to.
+                                    type: string
+                                  namespace:
+                                    description: Namespace of the resource being referred
+                                      to. Ignored if referent is not cluster-scoped.
+                                      cluster-scoped defaults to the namespace of
+                                      the referent.
+                                    type: string
+                                required:
+                                - name
+                                type: object
+                            type: object
                           secretRef:
                           secretRef:
                             description: AWSAuthSecretRef holds secret references
                             description: AWSAuthSecretRef holds secret references
-                              for aws credentials both AccessKeyID and SecretAccessKey
+                              for AWS credentials both AccessKeyID and SecretAccessKey
                               must be defined in order to properly authenticate.
                               must be defined in order to properly authenticate.
                             properties:
                             properties:
                               accessKeyIDSecretRef:
                               accessKeyIDSecretRef:
@@ -114,8 +134,6 @@ spec:
                                 - name
                                 - name
                                 type: object
                                 type: object
                             type: object
                             type: object
-                        required:
-                        - secretRef
                         type: object
                         type: object
                       region:
                       region:
                         description: AWS Region to be used for the provider
                         description: AWS Region to be used for the provider

BIN
docs/pictures/diagrams-provider-aws-auth-pod-identity.png


BIN
docs/pictures/diagrams-provider-aws-auth-secret-ref.png


BIN
docs/pictures/diagrams-provider-aws-auth-service-account.png


File diff suppressed because it is too large
+ 1 - 1
docs/pictures/diagrams.drawio


+ 81 - 5
docs/snippets/provider-aws-access.md

@@ -1,9 +1,85 @@
 ## AWS Authentication
 ## AWS Authentication
 
 
-Access to AWS providers can be granted in various ways:
+### Controller's Pod Identity
 
 
-* [IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html): IAM roles for service accounts.
-* Per pod IAM authentication: [kiam](https://github.com/uswitch/kiam) or [kube2iam](https://github.com/jtblin/kube2iam).
-* Directly provide AWS credentials to the External Secrets Operator pod by using environment variables.
+![Pod Identity Authentication](./pictures/diagrams-provider-aws-auth-pod-identity.png)
 
 
-Additionally, before fetching a secret from a store, ESO is able to assume role (as a proxy so to speak). It is advisable to use multiple roles in a multi-tenant environment.
+This is basicially a zero-configuration authentication method that inherits the credentials from the runtime environment using the [aws sdk default credential chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default).
+
+You can attach a role to the pod using [IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html), [kiam](https://github.com/uswitch/kiam) or [kube2iam](https://github.com/jtblin/kube2iam). When no other authentication method is configured in the `Kind=Secretstore` this role is used to make all API calls against AWS Secrets Manager or SSM Parameter Store.
+
+Based on the Pod's identity you can do a `sts:assumeRole` before fetching the secrets to limit access to certain keys in your provider. This is optional.
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: SecretStore
+metadata:
+  name: team-b-store
+spec:
+  provider:
+    aws:
+      service: SecretsManager
+      # optional: do a sts:assumeRole before fetching secrets
+      role: team-b
+```
+
+### Access Key ID & Secret Access Key
+![SecretRef](./pictures/diagrams-provider-aws-auth-secret-ref.png)
+
+You can store Access Key ID & Secret Access Key in a `Kind=Secret` and reference it from a SecretStore.
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: SecretStore
+metadata:
+  name: team-b-store
+spec:
+  provider:
+    aws:
+      service: SecretsManager
+      # optional: assume role before fetching secrets
+      role: team-b
+      auth:
+        secretRef:
+          accessKeyIDSecretRef:
+            name: awssm-secret
+            key: access-key
+          secretAccessKeySecretRef:
+            name: awssm-secret
+            key: secret-access-key
+```
+
+### EKS Service Account credentials
+
+![Service Account](./pictures/diagrams-provider-aws-auth-service-account.png)
+
+This feature lets you use short-lived service account tokens to authenticate with AWS.
+You must have [Service Account Volume Projection](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection) enabled - it is by default on EKS. See [EKS guide](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-technical-overview.html) on how to set up IAM roles for service accounts.
+
+The big advantage of this approach is that ESO runs without any credentials.
+
+```yaml
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  annotations:
+    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/team-a
+  name: my-serviceaccount
+  namespace: default
+```
+
+Reference the service account from above in the Secret Store:
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: SecretStore
+metadata:
+  name: secretstore-sample
+spec:
+  provider:
+    aws:
+      service: SecretsManager
+      auth:
+        jwt:
+          serviceAccountRef:
+            name: my-serviceaccount
+```

+ 47 - 2
docs/spec.md

@@ -17,7 +17,9 @@ Resource Types:
 <a href="#external-secrets.io/v1alpha1.AWSProvider">AWSProvider</a>)
 <a href="#external-secrets.io/v1alpha1.AWSProvider">AWSProvider</a>)
 </p>
 </p>
 <p>
 <p>
-<p>AWSAuth contains a secretRef for credentials.</p>
+<p>AWSAuth tells the controller how to do authentication with aws.
+Only one of secretRef or jwt can be specified.
+if none is specified the controller will load credentials using the aws sdk defaults</p>
 </p>
 </p>
 <table>
 <table>
 <thead>
 <thead>
@@ -37,6 +39,20 @@ AWSAuthSecretRef
 </em>
 </em>
 </td>
 </td>
 <td>
 <td>
+<em>(Optional)</em>
+</td>
+</tr>
+<tr>
+<td>
+<code>jwt</code></br>
+<em>
+<a href="#external-secrets.io/v1alpha1.AWSJWTAuth">
+AWSJWTAuth
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
 </td>
 </td>
 </tr>
 </tr>
 </tbody>
 </tbody>
@@ -48,7 +64,7 @@ AWSAuthSecretRef
 <a href="#external-secrets.io/v1alpha1.AWSAuth">AWSAuth</a>)
 <a href="#external-secrets.io/v1alpha1.AWSAuth">AWSAuth</a>)
 </p>
 </p>
 <p>
 <p>
-<p>AWSAuthSecretRef holds secret references for aws credentials
+<p>AWSAuthSecretRef holds secret references for AWS credentials
 both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.</p>
 both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.</p>
 </p>
 </p>
 <table>
 <table>
@@ -83,6 +99,35 @@ github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
 </tr>
 </tr>
 </tbody>
 </tbody>
 </table>
 </table>
+<h3 id="external-secrets.io/v1alpha1.AWSJWTAuth">AWSJWTAuth
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1alpha1.AWSAuth">AWSAuth</a>)
+</p>
+<p>
+<p>Authenticate against AWS using service account tokens</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>serviceAccountRef</code></br>
+<em>
+github.com/external-secrets/external-secrets/apis/meta/v1.ServiceAccountSelector
+</em>
+</td>
+<td>
+</td>
+</tr>
+</tbody>
+</table>
 <h3 id="external-secrets.io/v1alpha1.AWSProvider">AWSProvider
 <h3 id="external-secrets.io/v1alpha1.AWSProvider">AWSProvider
 </h3>
 </h3>
 <p>
 <p>

+ 1 - 1
e2e/Makefile

@@ -4,7 +4,7 @@ SHELL       := /bin/bash
 
 
 IMG_TAG     = test
 IMG_TAG     = test
 IMG         = local/external-secrets-e2e:$(IMG_TAG)
 IMG         = local/external-secrets-e2e:$(IMG_TAG)
-K8S_VERSION = "1.19.1"
+K8S_VERSION = "1.20.7"
 export FOCUS := $(FOCUS)
 export FOCUS := $(FOCUS)
 
 
 start-kind: ## Start kind cluster
 start-kind: ## Start kind cluster

+ 4 - 0
e2e/k8s/eso.values.yaml

@@ -5,3 +5,7 @@ image:
 extraEnv:
 extraEnv:
   - name: AWS_SECRETSMANAGER_ENDPOINT
   - name: AWS_SECRETSMANAGER_ENDPOINT
     value: "http://localstack.default"
     value: "http://localstack.default"
+  - name: AWS_STS_ENDPOINT
+    value: "http://localstack.default"
+  - name: AWS_SSM_ENDPOINT
+    value: "http://localstack.default"

+ 2 - 0
e2e/k8s/localstack.values.yaml

@@ -1,3 +1,5 @@
+image:
+  tag: "0.12.14"
 service:
 service:
   type: ClusterIP
   type: ClusterIP
   edgeService:
   edgeService:

+ 8 - 0
e2e/kind.yaml

@@ -2,6 +2,14 @@ kind: Cluster
 apiVersion: kind.x-k8s.io/v1alpha4
 apiVersion: kind.x-k8s.io/v1alpha4
 kubeadmConfigPatches:
 kubeadmConfigPatches:
 - |
 - |
+  kind: ClusterConfiguration
+  apiServer:
+    extraArgs:
+      api-audiences: "sts.amazonaws.com"
+      service-account-key-file: "/etc/kubernetes/pki/sa.pub"
+      service-account-signing-key-file: "/etc/kubernetes/pki/sa.key"
+      service-account-issuer: "https://s3-XXXXXXXXXX.amazonaws.com/XXXXXXXXXXXXXXXXXXXXX"
+- |
   apiVersion: kubelet.config.k8s.io/v1beta1
   apiVersion: kubelet.config.k8s.io/v1beta1
   kind: KubeletConfiguration
   kind: KubeletConfiguration
   metadata:
   metadata:

+ 4 - 4
e2e/suite/aws/provider.go

@@ -32,7 +32,7 @@ import (
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/e2e/framework"
 	"github.com/external-secrets/external-secrets/e2e/framework"
-	prov "github.com/external-secrets/external-secrets/pkg/provider/aws"
+	"github.com/external-secrets/external-secrets/pkg/provider/aws/auth"
 )
 )
 
 
 type SMProvider struct {
 type SMProvider struct {
@@ -45,7 +45,7 @@ func newSMProvider(f *framework.Framework, url string) *SMProvider {
 	sess, err := session.NewSessionWithOptions(session.Options{
 	sess, err := session.NewSessionWithOptions(session.Options{
 		Config: aws.Config{
 		Config: aws.Config{
 			Credentials: credentials.NewStaticCredentials("foobar", "foobar", "secret-manager"),
 			Credentials: credentials.NewStaticCredentials("foobar", "foobar", "secret-manager"),
-			EndpointResolver: prov.ResolveEndpointWithServiceMap(map[string]string{
+			EndpointResolver: auth.ResolveEndpointWithServiceMap(map[string]string{
 				"secretsmanager": url,
 				"secretsmanager": url,
 			}),
 			}),
 			Region: aws.String("eu-east-1"),
 			Region: aws.String("eu-east-1"),
@@ -103,8 +103,8 @@ func (s *SMProvider) BeforeEach() {
 				AWS: &esv1alpha1.AWSProvider{
 				AWS: &esv1alpha1.AWSProvider{
 					Service: esv1alpha1.AWSServiceSecretsManager,
 					Service: esv1alpha1.AWSServiceSecretsManager,
 					Region:  "us-east-1",
 					Region:  "us-east-1",
-					Auth: &esv1alpha1.AWSAuth{
-						SecretRef: esv1alpha1.AWSAuthSecretRef{
+					Auth: esv1alpha1.AWSAuth{
+						SecretRef: &esv1alpha1.AWSAuthSecretRef{
 							AccessKeyID: esmeta.SecretKeySelector{
 							AccessKeyID: esmeta.SecretKeySelector{
 								Name: "provider-secret",
 								Name: "provider-secret",
 								Key:  "kid",
 								Key:  "kid",

+ 87 - 1
e2e/suite/aws/secretsmanager.go

@@ -14,26 +14,112 @@ limitations under the License.
 package aws
 package aws
 
 
 import (
 import (
+	"context"
+	"fmt"
 
 
 	// nolint
 	// nolint
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/ginkgo"
+
 	// nolint
 	// nolint
 	. "github.com/onsi/ginkgo/extensions/table"
 	. "github.com/onsi/ginkgo/extensions/table"
 
 
+	// nolint
+	. "github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/e2e/framework"
 	"github.com/external-secrets/external-secrets/e2e/framework"
 	"github.com/external-secrets/external-secrets/e2e/suite/common"
 	"github.com/external-secrets/external-secrets/e2e/suite/common"
 )
 )
 
 
 var _ = Describe("[aws] ", func() {
 var _ = Describe("[aws] ", func() {
 	f := framework.New("eso-aws")
 	f := framework.New("eso-aws")
+	prov := newSMProvider(f, "http://localstack.default")
+
+	jwt := func(tc *framework.TestCase) {
+		saName := "my-sa"
+		err := f.CRClient.Create(context.Background(), &v1.ServiceAccount{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      saName,
+				Namespace: f.Namespace.Name,
+				Annotations: map[string]string{
+					"eks.amazonaws.com/role-arn": "arn:aws:iam::account:role/my-example-role",
+				},
+			},
+		})
+		Expect(err).ToNot(HaveOccurred())
+
+		// create secret store
+		secretStore := &esv1alpha1.SecretStore{
+			TypeMeta: metav1.TypeMeta{
+				Kind:       esv1alpha1.SecretStoreKind,
+				APIVersion: esv1alpha1.SchemeGroupVersion.String(),
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      f.Namespace.Name,
+				Namespace: f.Namespace.Name,
+			},
+			Spec: esv1alpha1.SecretStoreSpec{
+				Provider: &esv1alpha1.SecretStoreProvider{
+					AWS: &esv1alpha1.AWSProvider{
+						Service: esv1alpha1.AWSServiceSecretsManager,
+						Region:  "us-east-1",
+						Auth: esv1alpha1.AWSAuth{
+							JWTAuth: &esv1alpha1.AWSJWTAuth{
+								ServiceAccountRef: &esmeta.ServiceAccountSelector{
+									Name:      saName,
+									Namespace: &f.Namespace.Name,
+								},
+							},
+						},
+					},
+				},
+			},
+		}
+		err = f.CRClient.Patch(context.Background(), secretStore, client.Apply, client.FieldOwner("e2e-case"), client.ForceOwnership)
+		Expect(err).ToNot(HaveOccurred())
+
+		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
+		secretKey2 := fmt.Sprintf("%s-%s", f.Namespace.Name, "other")
+		secretValue := "bar"
+		tc.Secrets = map[string]string{
+			secretKey1: secretValue,
+			secretKey2: secretValue,
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				secretKey1: []byte(secretValue),
+				secretKey2: []byte(secretValue),
+			},
+		}
+		tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
+			{
+				SecretKey: secretKey1,
+				RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+					Key: secretKey1,
+				},
+			},
+			{
+				SecretKey: secretKey2,
+				RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+					Key: secretKey2,
+				},
+			},
+		}
+	}
 
 
 	DescribeTable("sync secrets",
 	DescribeTable("sync secrets",
 		framework.TableFunc(f,
 		framework.TableFunc(f,
-			newSMProvider(f, "http://localstack.default")),
+			prov),
 		Entry(common.SimpleDataSync(f)),
 		Entry(common.SimpleDataSync(f)),
 		Entry(common.NestedJSONWithGJSON(f)),
 		Entry(common.NestedJSONWithGJSON(f)),
 		Entry(common.JSONDataFromSync(f)),
 		Entry(common.JSONDataFromSync(f)),
 		Entry(common.JSONDataWithProperty(f)),
 		Entry(common.JSONDataWithProperty(f)),
 		Entry(common.JSONDataWithTemplate(f)),
 		Entry(common.JSONDataWithTemplate(f)),
+		Entry("should sync secrets with jwt auth", jwt),
 	)
 	)
 })
 })

+ 231 - 0
pkg/provider/aws/auth/auth.go

@@ -0,0 +1,231 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package auth
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/credentials"
+	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
+	"github.com/aws/aws-sdk-go/aws/defaults"
+	"github.com/aws/aws-sdk-go/aws/request"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/sts"
+	"github.com/aws/aws-sdk-go/service/sts/stsiface"
+	v1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/kubernetes"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	"github.com/external-secrets/external-secrets/pkg/provider/aws/util"
+)
+
+// Config contains configuration to create a new AWS provider.
+type Config struct {
+	AssumeRole string
+	Region     string
+	APIRetries int
+}
+
+var log = ctrl.Log.WithName("provider").WithName("aws")
+
+const (
+	roleARNAnnotation = "eks.amazonaws.com/role-arn"
+
+	errInvalidClusterStoreMissingAKIDNamespace = "invalid ClusterSecretStore: missing AWS AccessKeyID Namespace"
+	errInvalidClusterStoreMissingSAKNamespace  = "invalid ClusterSecretStore: missing AWS SecretAccessKey Namespace"
+	errFetchAKIDSecret                         = "could not fetch accessKeyID secret: %w"
+	errFetchSAKSecret                          = "could not fetch SecretAccessKey secret: %w"
+	errMissingSAK                              = "missing SecretAccessKey"
+	errMissingAKID                             = "missing AccessKeyID"
+)
+
+// New creates a new aws session based on the provided store
+// it uses the following authentication mechanisms in order:
+// * service-account token authentication via AssumeRoleWithWebIdentity
+// * static credentials from a Kind=Secret, optionally with doing a AssumeRole.
+// * sdk default provider chain, see: https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default
+func New(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string, assumeRoler STSProvider, jwtProvider jwtProviderFactory) (*session.Session, error) {
+	prov, err := util.GetAWSProvider(store)
+	if err != nil {
+		return nil, err
+	}
+	var creds *credentials.Credentials
+
+	// use credentials via service account token
+	jwtAuth := prov.Auth.JWTAuth
+	if jwtAuth != nil {
+		creds, err = sessionFromServiceAccount(ctx, prov, store, kube, namespace, jwtProvider)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// use credentials from sercretRef
+	secretRef := prov.Auth.SecretRef
+	if secretRef != nil {
+		log.V(1).Info("using credentials from secretRef")
+		creds, err = sessionFromSecretRef(ctx, prov, store, kube, namespace)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	config := aws.NewConfig().WithEndpointResolver(ResolveEndpoint())
+	if creds != nil {
+		config.WithCredentials(creds)
+	}
+	if prov.Region != "" {
+		config.WithRegion(prov.Region)
+	}
+	handlers := defaults.Handlers()
+	handlers.Build.PushBack(request.WithAppendUserAgent("external-secrets"))
+	sess, err := session.NewSessionWithOptions(session.Options{
+		Config:            *config,
+		Handlers:          handlers,
+		SharedConfigState: session.SharedConfigDisable,
+	})
+	if err != nil {
+		return nil, err
+	}
+	if prov.Role != "" {
+		stsclient := assumeRoler(sess)
+		sess.Config.WithCredentials(stscreds.NewCredentialsWithClient(stsclient, prov.Role))
+	}
+	log.Info("using aws session", "region", *sess.Config.Region, "credentials", creds)
+	return sess, nil
+}
+
+func sessionFromSecretRef(ctx context.Context, prov *esv1alpha1.AWSProvider, store esv1alpha1.GenericStore, kube client.Client, namespace string) (*credentials.Credentials, error) {
+	ke := client.ObjectKey{
+		Name:      prov.Auth.SecretRef.AccessKeyID.Name,
+		Namespace: namespace, // default to ExternalSecret namespace
+	}
+	// only ClusterStore is allowed to set namespace (and then it's required)
+	if store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
+		if prov.Auth.SecretRef.AccessKeyID.Namespace == nil {
+			return nil, fmt.Errorf(errInvalidClusterStoreMissingAKIDNamespace)
+		}
+		ke.Namespace = *prov.Auth.SecretRef.AccessKeyID.Namespace
+	}
+	akSecret := v1.Secret{}
+	err := kube.Get(ctx, ke, &akSecret)
+	if err != nil {
+		return nil, fmt.Errorf(errFetchAKIDSecret, err)
+	}
+	ke = client.ObjectKey{
+		Name:      prov.Auth.SecretRef.SecretAccessKey.Name,
+		Namespace: namespace, // default to ExternalSecret namespace
+	}
+	// only ClusterStore is allowed to set namespace (and then it's required)
+	if store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
+		if prov.Auth.SecretRef.SecretAccessKey.Namespace == nil {
+			return nil, fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
+		}
+		ke.Namespace = *prov.Auth.SecretRef.SecretAccessKey.Namespace
+	}
+	sakSecret := v1.Secret{}
+	err = kube.Get(ctx, ke, &sakSecret)
+	if err != nil {
+		return nil, fmt.Errorf(errFetchSAKSecret, err)
+	}
+	sak := string(sakSecret.Data[prov.Auth.SecretRef.SecretAccessKey.Key])
+	aks := string(akSecret.Data[prov.Auth.SecretRef.AccessKeyID.Key])
+	if sak == "" {
+		return nil, fmt.Errorf(errMissingSAK)
+	}
+	if aks == "" {
+		return nil, fmt.Errorf(errMissingAKID)
+	}
+
+	return credentials.NewStaticCredentials(aks, sak, ""), err
+}
+
+func sessionFromServiceAccount(ctx context.Context, prov *esv1alpha1.AWSProvider, store esv1alpha1.GenericStore, kube client.Client, namespace string, jwtProvider jwtProviderFactory) (*credentials.Credentials, error) {
+	if store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
+		namespace = *prov.Auth.JWTAuth.ServiceAccountRef.Namespace
+	}
+	name := prov.Auth.JWTAuth.ServiceAccountRef.Name
+	sa := v1.ServiceAccount{}
+	err := kube.Get(ctx, types.NamespacedName{
+		Name:      name,
+		Namespace: namespace,
+	}, &sa)
+	if err != nil {
+		return nil, err
+	}
+	// the service account is expected to have a well-known annotation
+	// this is used as input to assumeRoleWithWebIdentity
+	roleArn := sa.Annotations[roleARNAnnotation]
+	if roleArn == "" {
+		return nil, fmt.Errorf("an IAM role must be associated with service account %s (namespace: %s)", name, namespace)
+	}
+	jwtProv, err := jwtProvider(name, namespace, roleArn, prov.Region)
+	if err != nil {
+		return nil, err
+	}
+
+	log.V(1).Info("using credentials via service account", "role", roleArn, "region", prov.Region)
+	return credentials.NewCredentials(jwtProv), nil
+}
+
+type jwtProviderFactory func(name, namespace, roleArn, region string) (credentials.Provider, error)
+
+// DefaultJWTProvider returns a credentials.Provider that calls the AssumeRoleWithWebidentity
+// controller-runtime/client does not support TokenRequest or other subresource APIs
+// so we need to construct our own client and use it to fetch tokens.
+func DefaultJWTProvider(name, namespace, roleArn, region string) (credentials.Provider, error) {
+	cfg, err := ctrlcfg.GetConfig()
+	if err != nil {
+		return nil, err
+	}
+	clientset, err := kubernetes.NewForConfig(cfg)
+	if err != nil {
+		return nil, err
+	}
+	handlers := defaults.Handlers()
+	handlers.Build.PushBack(request.WithAppendUserAgent("external-secrets"))
+	awscfg := aws.NewConfig().WithEndpointResolver(ResolveEndpoint())
+	if region != "" {
+		awscfg.WithRegion(region)
+	}
+	sess, err := session.NewSessionWithOptions(session.Options{
+		Config:            *awscfg,
+		SharedConfigState: session.SharedConfigDisable,
+		Handlers:          handlers,
+	})
+	if err != nil {
+		return nil, err
+	}
+	tokenFetcher := &authTokenFetcher{
+		Namespace:      namespace,
+		ServiceAccount: name,
+		k8sClient:      clientset.CoreV1(),
+	}
+
+	return stscreds.NewWebIdentityRoleProviderWithToken(
+		sts.New(sess), roleArn, "external-secrets-provider-aws", tokenFetcher), nil
+}
+
+type STSProvider func(*session.Session) stsiface.STSAPI
+
+func DefaultSTSProvider(sess *session.Session) stsiface.STSAPI {
+	return sts.New(sess)
+}

+ 534 - 0
pkg/provider/aws/auth/auth_test.go

@@ -0,0 +1,534 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package auth
+
+import (
+	"context"
+	"os"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/credentials"
+	awssess "github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/sts"
+	"github.com/aws/aws-sdk-go/service/sts/stsiface"
+	"github.com/stretchr/testify/assert"
+	authv1 "k8s.io/api/authentication/v1"
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+	fakesess "github.com/external-secrets/external-secrets/pkg/provider/aws/auth/fake"
+)
+
+func TestNewSession(t *testing.T) {
+	rows := []TestSessionRow{
+		{
+			name:      "nil store",
+			expectErr: "found nil store",
+			store:     nil,
+		},
+		{
+			name:      "not store spec",
+			expectErr: "storeSpec is missing provider",
+			store:     &esv1alpha1.SecretStore{},
+		},
+		{
+			name:      "store spec has no provider",
+			expectErr: "storeSpec is missing provider",
+			store: &esv1alpha1.SecretStore{
+				Spec: esv1alpha1.SecretStoreSpec{},
+			},
+		},
+		{
+			name:      "spec has no awssm field",
+			expectErr: "Missing AWS field",
+			store: &esv1alpha1.SecretStore{
+				Spec: esv1alpha1.SecretStoreSpec{
+					Provider: &esv1alpha1.SecretStoreProvider{},
+				},
+			},
+		},
+		{
+			name: "configure aws using environment variables",
+			store: &esv1alpha1.SecretStore{
+				Spec: esv1alpha1.SecretStoreSpec{
+					Provider: &esv1alpha1.SecretStoreProvider{
+						AWS: &esv1alpha1.AWSProvider{},
+					},
+				},
+			},
+			env: map[string]string{
+				"AWS_ACCESS_KEY_ID":     "1111",
+				"AWS_SECRET_ACCESS_KEY": "2222",
+			},
+			expectProvider:    true,
+			expectedKeyID:     "1111",
+			expectedSecretKey: "2222",
+		},
+		{
+			name: "configure aws using environment variables + assume role",
+			stsProvider: func(*awssess.Session) stsiface.STSAPI {
+				return &fakesess.AssumeRoler{
+					AssumeRoleFunc: func(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) {
+						assert.Equal(t, *input.RoleArn, "foo-bar-baz")
+						return &sts.AssumeRoleOutput{
+							AssumedRoleUser: &sts.AssumedRoleUser{
+								Arn:           aws.String("1123132"),
+								AssumedRoleId: aws.String("xxxxx"),
+							},
+							Credentials: &sts.Credentials{
+								AccessKeyId:     aws.String("3333"),
+								SecretAccessKey: aws.String("4444"),
+								Expiration:      aws.Time(time.Now().Add(time.Hour)),
+								SessionToken:    aws.String("6666"),
+							},
+						}, nil
+					},
+				}
+			},
+			store: &esv1alpha1.SecretStore{
+				Spec: esv1alpha1.SecretStoreSpec{
+					Provider: &esv1alpha1.SecretStoreProvider{
+						AWS: &esv1alpha1.AWSProvider{
+							Role: "foo-bar-baz",
+						},
+					},
+				},
+			},
+			env: map[string]string{
+				"AWS_ACCESS_KEY_ID":     "1111",
+				"AWS_SECRET_ACCESS_KEY": "2222",
+			},
+			expectProvider:    true,
+			expectedKeyID:     "3333",
+			expectedSecretKey: "4444",
+		},
+		{
+			name:      "error out when secret with credentials does not exist",
+			namespace: "foo",
+			store: &esv1alpha1.SecretStore{
+				Spec: esv1alpha1.SecretStoreSpec{
+					Provider: &esv1alpha1.SecretStoreProvider{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: esv1alpha1.AWSAuth{
+								SecretRef: &esv1alpha1.AWSAuthSecretRef{
+									AccessKeyID: esmeta.SecretKeySelector{
+										Name: "othersecret",
+										Key:  "one",
+									},
+									SecretAccessKey: esmeta.SecretKeySelector{
+										Name: "othersecret",
+										Key:  "two",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			expectErr: `secrets "othersecret" not found`,
+		},
+		{
+			name:      "use credentials from secret to configure aws",
+			namespace: "foo",
+			store: &esv1alpha1.SecretStore{
+				Spec: esv1alpha1.SecretStoreSpec{
+					Provider: &esv1alpha1.SecretStoreProvider{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: esv1alpha1.AWSAuth{
+								SecretRef: &esv1alpha1.AWSAuthSecretRef{
+									AccessKeyID: esmeta.SecretKeySelector{
+										Name: "onesecret",
+										// Namespace is not set
+										Key: "one",
+									},
+									SecretAccessKey: esmeta.SecretKeySelector{
+										Name: "onesecret",
+										// Namespace is not set
+										Key: "two",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			secrets: []v1.Secret{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "onesecret",
+						Namespace: "foo",
+					},
+					Data: map[string][]byte{
+						"one": []byte("1111"),
+						"two": []byte("2222"),
+					},
+				},
+			},
+			expectProvider:    true,
+			expectedKeyID:     "1111",
+			expectedSecretKey: "2222",
+		},
+		{
+			name:      "error out when secret key does not exist",
+			namespace: "foo",
+			store: &esv1alpha1.SecretStore{
+				Spec: esv1alpha1.SecretStoreSpec{
+					Provider: &esv1alpha1.SecretStoreProvider{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: esv1alpha1.AWSAuth{
+								SecretRef: &esv1alpha1.AWSAuthSecretRef{
+									AccessKeyID: esmeta.SecretKeySelector{
+										Name: "brokensecret",
+										Key:  "one",
+									},
+									SecretAccessKey: esmeta.SecretKeySelector{
+										Name: "brokensecret",
+										Key:  "two",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			secrets: []v1.Secret{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "brokensecret",
+						Namespace: "foo",
+					},
+					Data: map[string][]byte{},
+				},
+			},
+			expectErr: "missing SecretAccessKey",
+		},
+		{
+			name:      "should not be able to access secrets from different namespace",
+			namespace: "foo",
+			store: &esv1alpha1.SecretStore{
+				Spec: esv1alpha1.SecretStoreSpec{
+					Provider: &esv1alpha1.SecretStoreProvider{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: esv1alpha1.AWSAuth{
+								SecretRef: &esv1alpha1.AWSAuthSecretRef{
+									AccessKeyID: esmeta.SecretKeySelector{
+										Name:      "onesecret",
+										Namespace: aws.String("evil"), // this should not be possible!
+										Key:       "one",
+									},
+									SecretAccessKey: esmeta.SecretKeySelector{
+										Name:      "onesecret",
+										Namespace: aws.String("evil"),
+										Key:       "two",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			secrets: []v1.Secret{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "onesecret",
+						Namespace: "evil",
+					},
+					Data: map[string][]byte{
+						"one": []byte("1111"),
+						"two": []byte("2222"),
+					},
+				},
+			},
+			expectErr: `secrets "onesecret" not found`,
+		},
+		{
+			name:      "ClusterStore should use credentials from a specific namespace",
+			namespace: "es-namespace",
+			store: &esv1alpha1.ClusterSecretStore{
+				TypeMeta: metav1.TypeMeta{
+					APIVersion: esv1alpha1.ClusterSecretStoreKindAPIVersion,
+					Kind:       esv1alpha1.ClusterSecretStoreKind,
+				},
+				Spec: esv1alpha1.SecretStoreSpec{
+					Provider: &esv1alpha1.SecretStoreProvider{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: esv1alpha1.AWSAuth{
+								SecretRef: &esv1alpha1.AWSAuthSecretRef{
+									AccessKeyID: esmeta.SecretKeySelector{
+										Name:      "onesecret",
+										Namespace: aws.String("platform-team-ns"),
+										Key:       "one",
+									},
+									SecretAccessKey: esmeta.SecretKeySelector{
+										Name:      "onesecret",
+										Namespace: aws.String("platform-team-ns"),
+										Key:       "two",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			secrets: []v1.Secret{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "onesecret",
+						Namespace: "platform-team-ns",
+					},
+					Data: map[string][]byte{
+						"one": []byte("1111"),
+						"two": []byte("2222"),
+					},
+				},
+			},
+			expectProvider:    true,
+			expectedKeyID:     "1111",
+			expectedSecretKey: "2222",
+		},
+		{
+			name:      "namespace is mandatory when using ClusterStore with SecretKeySelector",
+			namespace: "es-namespace",
+			store: &esv1alpha1.ClusterSecretStore{
+				TypeMeta: metav1.TypeMeta{
+					APIVersion: esv1alpha1.ClusterSecretStoreKindAPIVersion,
+					Kind:       esv1alpha1.ClusterSecretStoreKind,
+				},
+				Spec: esv1alpha1.SecretStoreSpec{
+					Provider: &esv1alpha1.SecretStoreProvider{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: esv1alpha1.AWSAuth{
+								SecretRef: &esv1alpha1.AWSAuthSecretRef{
+									AccessKeyID: esmeta.SecretKeySelector{
+										Name: "onesecret",
+										Key:  "one",
+									},
+									SecretAccessKey: esmeta.SecretKeySelector{
+										Name: "onesecret",
+										Key:  "two",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			expectErr: "invalid ClusterSecretStore: missing AWS AccessKeyID Namespace",
+		},
+		{
+			name:      "jwt auth via cluster secret store",
+			namespace: "es-namespace",
+			sa: &v1.ServiceAccount{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "my-service-account",
+					Namespace: "other-ns",
+					Annotations: map[string]string{
+						roleARNAnnotation: "my-sa-role",
+					},
+				},
+			},
+			jwtProvider: func(name, namespace, roleArn, region string) (credentials.Provider, error) {
+				assert.Equal(t, "my-service-account", name)
+				assert.Equal(t, "other-ns", namespace)
+				assert.Equal(t, "my-sa-role", roleArn)
+				return fakesess.CredentialsProvider{
+					RetrieveFunc: func() (credentials.Value, error) {
+						return credentials.Value{
+							AccessKeyID:     "3333",
+							SecretAccessKey: "4444",
+							SessionToken:    "1234",
+							ProviderName:    "fake",
+						}, nil
+					},
+					IsExpiredFunc: func() bool { return false },
+				}, nil
+			},
+			store: &esv1alpha1.ClusterSecretStore{
+				TypeMeta: metav1.TypeMeta{
+					APIVersion: esv1alpha1.ClusterSecretStoreKindAPIVersion,
+					Kind:       esv1alpha1.ClusterSecretStoreKind,
+				},
+				Spec: esv1alpha1.SecretStoreSpec{
+					Provider: &esv1alpha1.SecretStoreProvider{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: esv1alpha1.AWSAuth{
+								JWTAuth: &esv1alpha1.AWSJWTAuth{
+									ServiceAccountRef: &esmeta.ServiceAccountSelector{
+										Name:      "my-service-account",
+										Namespace: aws.String("other-ns"),
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			expectProvider:    true,
+			expectedKeyID:     "3333",
+			expectedSecretKey: "4444",
+		},
+	}
+	for i := range rows {
+		row := rows[i]
+		t.Run(row.name, func(t *testing.T) {
+			testRow(t, row)
+		})
+	}
+}
+
+type TestSessionRow struct {
+	name              string
+	store             esv1alpha1.GenericStore
+	secrets           []v1.Secret
+	sa                *v1.ServiceAccount
+	jwtProvider       jwtProviderFactory
+	namespace         string
+	stsProvider       STSProvider
+	expectProvider    bool
+	expectErr         string
+	expectedKeyID     string
+	expectedSecretKey string
+	env               map[string]string
+}
+
+func testRow(t *testing.T, row TestSessionRow) {
+	kc := clientfake.NewClientBuilder().Build()
+	for i := range row.secrets {
+		err := kc.Create(context.Background(), &row.secrets[i])
+		assert.Nil(t, err)
+	}
+	for k, v := range row.env {
+		os.Setenv(k, v)
+	}
+	if row.sa != nil {
+		err := kc.Create(context.Background(), row.sa)
+		assert.Nil(t, err)
+	}
+	err := kc.Create(context.Background(), &authv1.TokenRequest{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "my-service-account",
+			Namespace: "other-ns",
+		},
+	})
+	assert.Nil(t, err)
+	defer func() {
+		for k := range row.env {
+			os.Unsetenv(k)
+		}
+	}()
+	s, err := New(context.Background(), row.store, kc, row.namespace, row.stsProvider, row.jwtProvider)
+	if !ErrorContains(err, row.expectErr) {
+		t.Errorf("expected error %s but found %s", row.expectErr, err.Error())
+	}
+	// pass test on expected error
+	if err != nil {
+		return
+	}
+	if row.expectProvider && s == nil {
+		t.Errorf("expected provider object, found nil")
+		return
+	}
+	creds, _ := s.Config.Credentials.Get()
+	assert.Equal(t, row.expectedKeyID, creds.AccessKeyID)
+	assert.Equal(t, row.expectedSecretKey, creds.SecretAccessKey)
+}
+
+func TestSMEnvCredentials(t *testing.T) {
+	k8sClient := clientfake.NewClientBuilder().Build()
+	os.Setenv("AWS_SECRET_ACCESS_KEY", "1111")
+	os.Setenv("AWS_ACCESS_KEY_ID", "2222")
+	defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
+	defer os.Unsetenv("AWS_ACCESS_KEY_ID")
+	s, err := New(context.Background(), &esv1alpha1.SecretStore{
+		Spec: esv1alpha1.SecretStoreSpec{
+			Provider: &esv1alpha1.SecretStoreProvider{
+				// defaults
+				AWS: &esv1alpha1.AWSProvider{},
+			},
+		},
+	}, k8sClient, "example-ns", DefaultSTSProvider, nil)
+	assert.Nil(t, err)
+	assert.NotNil(t, s)
+	creds, err := s.Config.Credentials.Get()
+	assert.Nil(t, err)
+	assert.Equal(t, creds.AccessKeyID, "2222")
+	assert.Equal(t, creds.SecretAccessKey, "1111")
+}
+
+func TestSMAssumeRole(t *testing.T) {
+	k8sClient := clientfake.NewClientBuilder().Build()
+	sts := &fakesess.AssumeRoler{
+		AssumeRoleFunc: func(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) {
+			// make sure the correct role is passed in
+			assert.Equal(t, *input.RoleArn, "my-awesome-role")
+			return &sts.AssumeRoleOutput{
+				AssumedRoleUser: &sts.AssumedRoleUser{
+					Arn:           aws.String("1123132"),
+					AssumedRoleId: aws.String("xxxxx"),
+				},
+				Credentials: &sts.Credentials{
+					AccessKeyId:     aws.String("3333"),
+					SecretAccessKey: aws.String("4444"),
+					Expiration:      aws.Time(time.Now().Add(time.Hour)),
+					SessionToken:    aws.String("6666"),
+				},
+			}, nil
+		},
+	}
+	os.Setenv("AWS_SECRET_ACCESS_KEY", "1111")
+	os.Setenv("AWS_ACCESS_KEY_ID", "2222")
+	defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
+	defer os.Unsetenv("AWS_ACCESS_KEY_ID")
+	s, err := New(context.Background(), &esv1alpha1.SecretStore{
+		Spec: esv1alpha1.SecretStoreSpec{
+			Provider: &esv1alpha1.SecretStoreProvider{
+				// do assume role!
+				AWS: &esv1alpha1.AWSProvider{
+					Role: "my-awesome-role",
+				},
+			},
+		},
+	}, k8sClient, "example-ns", func(se *awssess.Session) stsiface.STSAPI {
+		// check if the correct temporary credentials were used
+		creds, err := se.Config.Credentials.Get()
+		assert.Nil(t, err)
+		assert.Equal(t, creds.AccessKeyID, "2222")
+		assert.Equal(t, creds.SecretAccessKey, "1111")
+		return sts
+	}, nil)
+	assert.Nil(t, err)
+	assert.NotNil(t, s)
+
+	creds, err := s.Config.Credentials.Get()
+	assert.Nil(t, err)
+	assert.Equal(t, creds.AccessKeyID, "3333")
+	assert.Equal(t, creds.SecretAccessKey, "4444")
+}
+
+func ErrorContains(out error, want string) bool {
+	if out == nil {
+		return want == ""
+	}
+	if want == "" {
+		return false
+	}
+	return strings.Contains(out.Error(), want)
+}

+ 25 - 1
pkg/provider/aws/session/fake/assumeroler.go

@@ -13,12 +13,36 @@ limitations under the License.
 */
 */
 package fake
 package fake
 
 
-import "github.com/aws/aws-sdk-go/service/sts"
+import (
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/credentials"
+	"github.com/aws/aws-sdk-go/aws/request"
+	"github.com/aws/aws-sdk-go/service/sts"
+	"github.com/aws/aws-sdk-go/service/sts/stsiface"
+)
 
 
 type AssumeRoler struct {
 type AssumeRoler struct {
+	stsiface.STSAPI
 	AssumeRoleFunc func(*sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
 	AssumeRoleFunc func(*sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
 }
 }
 
 
 func (f *AssumeRoler) AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) {
 func (f *AssumeRoler) AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) {
 	return f.AssumeRoleFunc(input)
 	return f.AssumeRoleFunc(input)
 }
 }
+
+func (f *AssumeRoler) AssumeRoleWithContext(ctx aws.Context, input *sts.AssumeRoleInput, opts ...request.Option) (*sts.AssumeRoleOutput, error) {
+	return f.AssumeRoleFunc(input)
+}
+
+type CredentialsProvider struct {
+	RetrieveFunc  func() (credentials.Value, error)
+	IsExpiredFunc func() bool
+}
+
+func (t CredentialsProvider) Retrieve() (credentials.Value, error) {
+	return t.RetrieveFunc()
+}
+
+func (t CredentialsProvider) IsExpired() bool {
+	return t.IsExpiredFunc()
+}

+ 54 - 0
pkg/provider/aws/auth/resolver.go

@@ -0,0 +1,54 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package auth
+
+import (
+	"os"
+
+	"github.com/aws/aws-sdk-go/aws/endpoints"
+)
+
+const (
+	SecretsManagerEndpointEnv = "AWS_SECRETSMANAGER_ENDPOINT"
+	STSEndpointEnv            = "AWS_STS_ENDPOINT"
+	SSMEndpointEnv            = "AWS_SSM_ENDPOINT"
+)
+
+// ResolveEndpoint returns a ResolverFunc with
+// customizable endpoints.
+func ResolveEndpoint() endpoints.ResolverFunc {
+	customEndpoints := make(map[string]string)
+	if v := os.Getenv(SecretsManagerEndpointEnv); v != "" {
+		customEndpoints["secretsmanager"] = v
+	}
+	if v := os.Getenv(SSMEndpointEnv); v != "" {
+		customEndpoints["ssm"] = v
+	}
+	if v := os.Getenv(STSEndpointEnv); v != "" {
+		customEndpoints["sts"] = v
+	}
+	return ResolveEndpointWithServiceMap(customEndpoints)
+}
+
+func ResolveEndpointWithServiceMap(customEndpoints map[string]string) endpoints.ResolverFunc {
+	defaultResolver := endpoints.DefaultResolver()
+	return func(service, region string, opts ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) {
+		if ep, ok := customEndpoints[service]; ok {
+			return endpoints.ResolvedEndpoint{
+				URL: ep,
+			}, nil
+		}
+		return defaultResolver.EndpointFor(service, region, opts...)
+	}
+}

+ 58 - 0
pkg/provider/aws/auth/resolver_test.go

@@ -0,0 +1,58 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package auth
+
+import (
+	"os"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestResolver(t *testing.T) {
+	tbl := []struct {
+		env     string
+		service string
+		url     string
+	}{
+		{
+			env:     SecretsManagerEndpointEnv,
+			service: "secretsmanager",
+			url:     "http://sm.foo",
+		},
+		{
+			env:     SSMEndpointEnv,
+			service: "ssm",
+			url:     "http://ssm.foo",
+		},
+		{
+			env:     STSEndpointEnv,
+			service: "sts",
+			url:     "http://sts.foo",
+		},
+	}
+
+	for _, item := range tbl {
+		os.Setenv(item.env, item.url)
+		defer os.Unsetenv(item.env)
+	}
+
+	f := ResolveEndpoint()
+
+	for _, item := range tbl {
+		ep, err := f.EndpointFor(item.service, "")
+		assert.Nil(t, err)
+		assert.Equal(t, item.url, ep.URL)
+	}
+}

+ 50 - 0
pkg/provider/aws/auth/token_fetcher.go

@@ -0,0 +1,50 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package auth
+
+import (
+	"fmt"
+
+	"github.com/aws/aws-sdk-go/aws/credentials"
+	authv1 "k8s.io/api/authentication/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
+)
+
+// mostly taken from:
+// https://github.com/aws/secrets-store-csi-driver-provider-aws/blob/main/auth/auth.go#L140-L145
+const (
+	tokenAudience = "sts.amazonaws.com"
+)
+
+type authTokenFetcher struct {
+	Namespace      string
+	ServiceAccount string
+	k8sClient      corev1.CoreV1Interface
+}
+
+// FetchToken satisfies the stscreds.TokenFetcher interface
+// it is used to generate service account tokens which are consumed by the aws sdk.
+func (p authTokenFetcher) FetchToken(ctx credentials.Context) ([]byte, error) {
+	log.V(1).Info("fetching token", "ns", p.Namespace, "sa", p.ServiceAccount)
+	tokRsp, err := p.k8sClient.ServiceAccounts(p.Namespace).CreateToken(ctx, p.ServiceAccount, &authv1.TokenRequest{
+		Spec: authv1.TokenRequestSpec{
+			Audiences: []string{tokenAudience},
+		},
+	}, metav1.CreateOptions{})
+	if err != nil {
+		return nil, fmt.Errorf("error creating service account token: %w", err)
+	}
+	return []byte(tokRsp.Status.Token), nil
+}

+ 63 - 0
pkg/provider/aws/auth/token_fetcher_test.go

@@ -0,0 +1,63 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package auth
+
+import (
+	"context"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	authv1 "k8s.io/api/authentication/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	k8sv1 "k8s.io/client-go/kubernetes/typed/core/v1"
+)
+
+func TestTokenFetcher(t *testing.T) {
+	tf := &authTokenFetcher{
+		ServiceAccount: "foobar",
+		Namespace:      "example",
+		k8sClient:      &mockK8sV1{},
+	}
+	token, err := tf.FetchToken(context.Background())
+	assert.Nil(t, err)
+	assert.Equal(t, []byte("FAKETOKEN"), token)
+}
+
+// Mock K8s client for creating tokens.
+type mockK8sV1 struct {
+	k8sv1.CoreV1Interface
+}
+
+func (m *mockK8sV1) ServiceAccounts(namespace string) k8sv1.ServiceAccountInterface {
+	return &mockK8sV1SA{v1mock: m}
+}
+
+// Mock the K8s service account client.
+type mockK8sV1SA struct {
+	k8sv1.ServiceAccountInterface
+	v1mock *mockK8sV1
+}
+
+func (ma *mockK8sV1SA) CreateToken(
+	ctx context.Context,
+	serviceAccountName string,
+	tokenRequest *authv1.TokenRequest,
+	opts metav1.CreateOptions,
+) (*authv1.TokenRequest, error) {
+	return &authv1.TokenRequest{
+		Status: authv1.TokenRequestStatus{
+			Token: "FAKETOKEN",
+		},
+	}, nil
+}

+ 0 - 10
pkg/provider/aws/parameterstore/parameterstore_test.go

@@ -22,21 +22,11 @@ import (
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/service/ssm"
 	"github.com/aws/aws-sdk-go/service/ssm"
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp"
-	"github.com/stretchr/testify/assert"
 
 
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	fake "github.com/external-secrets/external-secrets/pkg/provider/aws/parameterstore/fake"
 	fake "github.com/external-secrets/external-secrets/pkg/provider/aws/parameterstore/fake"
-	sess "github.com/external-secrets/external-secrets/pkg/provider/aws/session"
 )
 )
 
 
-func TestConstructor(t *testing.T) {
-	s, err := sess.New("1111", "2222", "foo", "", nil)
-	assert.Nil(t, err)
-	c, err := New(s)
-	assert.Nil(t, err)
-	assert.NotNil(t, c.client)
-}
-
 type parameterstoreTestCase struct {
 type parameterstoreTestCase struct {
 	fakeClient     *fake.Client
 	fakeClient     *fake.Client
 	apiInput       *ssm.GetParameterInput
 	apiInput       *ssm.GetParameterInput

+ 8 - 136
pkg/provider/aws/provider.go

@@ -17,57 +17,37 @@ package aws
 import (
 import (
 	"context"
 	"context"
 	"fmt"
 	"fmt"
-	"os"
 
 
-	"github.com/aws/aws-sdk-go/aws/endpoints"
-	"github.com/aws/aws-sdk-go/aws/session"
-	v1 "k8s.io/api/core/v1"
-	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	"github.com/external-secrets/external-secrets/pkg/provider"
 	"github.com/external-secrets/external-secrets/pkg/provider"
+	awsauth "github.com/external-secrets/external-secrets/pkg/provider/aws/auth"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/parameterstore"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/parameterstore"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/secretsmanager"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/secretsmanager"
-	awssess "github.com/external-secrets/external-secrets/pkg/provider/aws/session"
+	"github.com/external-secrets/external-secrets/pkg/provider/aws/util"
 	"github.com/external-secrets/external-secrets/pkg/provider/schema"
 	"github.com/external-secrets/external-secrets/pkg/provider/schema"
 )
 )
 
 
 // Provider satisfies the provider interface.
 // Provider satisfies the provider interface.
 type Provider struct{}
 type Provider struct{}
 
 
-var log = ctrl.Log.WithName("provider").WithName("aws")
-
 const (
 const (
-	SecretsManagerEndpointEnv = "AWS_SECRETSMANAGER_ENDPOINT"
-	STSEndpointEnv            = "AWS_STS_ENDPOINT"
-	SSMEndpointEnv            = "AWS_SSM_ENDPOINT"
-
-	errUnableCreateSession                     = "unable to create session: %w"
-	errUnknownProviderService                  = "unknown AWS Provider Service: %s"
-	errInvalidClusterStoreMissingAKIDNamespace = "invalid ClusterSecretStore: missing AWS AccessKeyID Namespace"
-	errInvalidClusterStoreMissingSAKNamespace  = "invalid ClusterSecretStore: missing AWS SecretAccessKey Namespace"
-	errFetchAKIDSecret                         = "could not fetch accessKeyID secret: %w"
-	errFetchSAKSecret                          = "could not fetch SecretAccessKey secret: %w"
-	errMissingSAK                              = "missing SecretAccessKey"
-	errMissingAKID                             = "missing AccessKeyID"
-	errNilStore                                = "found nil store"
-	errMissingStoreSpec                        = "store is missing spec"
-	errMissingProvider                         = "storeSpec is missing provider"
-	errInvalidProvider                         = "invalid provider spec. Missing AWS field in store %s"
+	errUnableCreateSession    = "unable to create session: %w"
+	errUnknownProviderService = "unknown AWS Provider Service: %s"
 )
 )
 
 
 // NewClient constructs a new secrets client based on the provided store.
 // NewClient constructs a new secrets client based on the provided store.
 func (p *Provider) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
 func (p *Provider) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
-	return newClient(ctx, store, kube, namespace, awssess.DefaultSTSProvider)
+	return newClient(ctx, store, kube, namespace, awsauth.DefaultSTSProvider)
 }
 }
 
 
-func newClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string, assumeRoler awssess.STSProvider) (provider.SecretsClient, error) {
-	prov, err := getAWSProvider(store)
+func newClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string, assumeRoler awsauth.STSProvider) (provider.SecretsClient, error) {
+	prov, err := util.GetAWSProvider(store)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	sess, err := newSession(ctx, store, kube, namespace, assumeRoler)
+	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)
 	}
 	}
@@ -80,114 +60,6 @@ func newClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.C
 	return nil, fmt.Errorf(errUnknownProviderService, prov.Service)
 	return nil, fmt.Errorf(errUnknownProviderService, prov.Service)
 }
 }
 
 
-// newSession creates a new aws session based on a store
-// it looks up credentials at the provided secrets.
-func newSession(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string, assumeRoler awssess.STSProvider) (*session.Session, error) {
-	prov, err := getAWSProvider(store)
-	if err != nil {
-		return nil, err
-	}
-	var sak, aks string
-	// use provided credentials via secret reference
-	if prov.Auth != nil {
-		log.V(1).Info("fetching secrets for authentication")
-		ke := client.ObjectKey{
-			Name:      prov.Auth.SecretRef.AccessKeyID.Name,
-			Namespace: namespace, // default to ExternalSecret namespace
-		}
-		// only ClusterStore is allowed to set namespace (and then it's required)
-		if store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
-			if prov.Auth.SecretRef.AccessKeyID.Namespace == nil {
-				return nil, fmt.Errorf(errInvalidClusterStoreMissingAKIDNamespace)
-			}
-			ke.Namespace = *prov.Auth.SecretRef.AccessKeyID.Namespace
-		}
-		akSecret := v1.Secret{}
-		err := kube.Get(ctx, ke, &akSecret)
-		if err != nil {
-			return nil, fmt.Errorf(errFetchAKIDSecret, err)
-		}
-		ke = client.ObjectKey{
-			Name:      prov.Auth.SecretRef.SecretAccessKey.Name,
-			Namespace: namespace, // default to ExternalSecret namespace
-		}
-		// only ClusterStore is allowed to set namespace (and then it's required)
-		if store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
-			if prov.Auth.SecretRef.SecretAccessKey.Namespace == nil {
-				return nil, fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
-			}
-			ke.Namespace = *prov.Auth.SecretRef.SecretAccessKey.Namespace
-		}
-		sakSecret := v1.Secret{}
-		err = kube.Get(ctx, ke, &sakSecret)
-		if err != nil {
-			return nil, fmt.Errorf(errFetchSAKSecret, err)
-		}
-		sak = string(sakSecret.Data[prov.Auth.SecretRef.SecretAccessKey.Key])
-		aks = string(akSecret.Data[prov.Auth.SecretRef.AccessKeyID.Key])
-		if sak == "" {
-			return nil, fmt.Errorf(errMissingSAK)
-		}
-		if aks == "" {
-			return nil, fmt.Errorf(errMissingAKID)
-		}
-	}
-	session, err := awssess.New(sak, aks, prov.Region, prov.Role, assumeRoler)
-	if err != nil {
-		return nil, err
-	}
-	session.Config.EndpointResolver = ResolveEndpoint()
-	return session, nil
-}
-
-// getAWSProvider does the necessary nil checks on the generic store
-// it returns the aws provider or an error.
-func getAWSProvider(store esv1alpha1.GenericStore) (*esv1alpha1.AWSProvider, error) {
-	if store == nil {
-		return nil, fmt.Errorf(errNilStore)
-	}
-	spc := store.GetSpec()
-	if spc == nil {
-		return nil, fmt.Errorf(errMissingStoreSpec)
-	}
-	if spc.Provider == nil {
-		return nil, fmt.Errorf(errMissingProvider)
-	}
-	prov := spc.Provider.AWS
-	if prov == nil {
-		return nil, fmt.Errorf(errInvalidProvider, store.GetObjectMeta().String())
-	}
-	return prov, nil
-}
-
-// ResolveEndpoint returns a ResolverFunc with
-// customizable endpoints.
-func ResolveEndpoint() endpoints.ResolverFunc {
-	customEndpoints := make(map[string]string)
-	if v := os.Getenv(SecretsManagerEndpointEnv); v != "" {
-		customEndpoints["secretsmanager"] = v
-	}
-	if v := os.Getenv(SSMEndpointEnv); v != "" {
-		customEndpoints["ssm"] = v
-	}
-	if v := os.Getenv(STSEndpointEnv); v != "" {
-		customEndpoints["sts"] = v
-	}
-	return ResolveEndpointWithServiceMap(customEndpoints)
-}
-
-func ResolveEndpointWithServiceMap(customEndpoints map[string]string) endpoints.ResolverFunc {
-	defaultResolver := endpoints.DefaultResolver()
-	return func(service, region string, opts ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) {
-		if ep, ok := customEndpoints[service]; ok {
-			return endpoints.ResolvedEndpoint{
-				URL: ep,
-			}, nil
-		}
-		return defaultResolver.EndpointFor(service, region, opts...)
-	}
-}
-
 func init() {
 func init() {
 	schema.Register(&Provider{}, &esv1alpha1.SecretStoreProvider{
 	schema.Register(&Provider{}, &esv1alpha1.SecretStoreProvider{
 		AWS: &esv1alpha1.AWSProvider{},
 		AWS: &esv1alpha1.AWSProvider{},

+ 2 - 481
pkg/provider/aws/provider_test.go

@@ -16,26 +16,16 @@ package aws
 
 
 import (
 import (
 	"context"
 	"context"
-	"os"
-	"strings"
 	"testing"
 	"testing"
-	"time"
 
 
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws"
-	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
-	awssess "github.com/aws/aws-sdk-go/aws/session"
-	"github.com/aws/aws-sdk-go/service/sts"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
-	v1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
 	clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/parameterstore"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/parameterstore"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/secretsmanager"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/secretsmanager"
-	session "github.com/external-secrets/external-secrets/pkg/provider/aws/session"
-	fakesess "github.com/external-secrets/external-secrets/pkg/provider/aws/session/fake"
 )
 )
 
 
 func TestProvider(t *testing.T) {
 func TestProvider(t *testing.T) {
@@ -118,8 +108,8 @@ func TestProvider(t *testing.T) {
 					Provider: &esv1alpha1.SecretStoreProvider{
 					Provider: &esv1alpha1.SecretStoreProvider{
 						AWS: &esv1alpha1.AWSProvider{
 						AWS: &esv1alpha1.AWSProvider{
 							Service: esv1alpha1.AWSServiceParameterStore,
 							Service: esv1alpha1.AWSServiceParameterStore,
-							Auth: &esv1alpha1.AWSAuth{
-								SecretRef: esv1alpha1.AWSAuthSecretRef{
+							Auth: esv1alpha1.AWSAuth{
+								SecretRef: &esv1alpha1.AWSAuthSecretRef{
 									AccessKeyID: esmeta.SecretKeySelector{
 									AccessKeyID: esmeta.SecretKeySelector{
 										Name:      "foo",
 										Name:      "foo",
 										Namespace: aws.String("NOOP"),
 										Namespace: aws.String("NOOP"),
@@ -147,472 +137,3 @@ func TestProvider(t *testing.T) {
 		})
 		})
 	}
 	}
 }
 }
-
-func TestNewSession(t *testing.T) {
-	rows := []TestSessionRow{
-		{
-			name:      "nil store",
-			expectErr: "found nil store",
-			store:     nil,
-		},
-		{
-			name:      "not store spec",
-			expectErr: "storeSpec is missing provider",
-			store:     &esv1alpha1.SecretStore{},
-		},
-		{
-			name:      "store spec has no provider",
-			expectErr: "storeSpec is missing provider",
-			store: &esv1alpha1.SecretStore{
-				Spec: esv1alpha1.SecretStoreSpec{},
-			},
-		},
-		{
-			name:      "spec has no awssm field",
-			expectErr: "Missing AWS field",
-			store: &esv1alpha1.SecretStore{
-				Spec: esv1alpha1.SecretStoreSpec{
-					Provider: &esv1alpha1.SecretStoreProvider{},
-				},
-			},
-		},
-		{
-			name: "configure aws using environment variables",
-			store: &esv1alpha1.SecretStore{
-				Spec: esv1alpha1.SecretStoreSpec{
-					Provider: &esv1alpha1.SecretStoreProvider{
-						AWS: &esv1alpha1.AWSProvider{},
-					},
-				},
-			},
-			env: map[string]string{
-				"AWS_ACCESS_KEY_ID":     "1111",
-				"AWS_SECRET_ACCESS_KEY": "2222",
-			},
-			expectProvider:    true,
-			expectedKeyID:     "1111",
-			expectedSecretKey: "2222",
-		},
-		{
-			name: "configure aws using environment variables + assume role",
-
-			stsProvider: func(*awssess.Session) stscreds.AssumeRoler {
-				return &fakesess.AssumeRoler{
-					AssumeRoleFunc: func(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) {
-						assert.Equal(t, *input.RoleArn, "foo-bar-baz")
-						return &sts.AssumeRoleOutput{
-							AssumedRoleUser: &sts.AssumedRoleUser{
-								Arn:           aws.String("1123132"),
-								AssumedRoleId: aws.String("xxxxx"),
-							},
-							Credentials: &sts.Credentials{
-								AccessKeyId:     aws.String("3333"),
-								SecretAccessKey: aws.String("4444"),
-								Expiration:      aws.Time(time.Now().Add(time.Hour)),
-								SessionToken:    aws.String("6666"),
-							},
-						}, nil
-					},
-				}
-			},
-			store: &esv1alpha1.SecretStore{
-				Spec: esv1alpha1.SecretStoreSpec{
-					Provider: &esv1alpha1.SecretStoreProvider{
-						AWS: &esv1alpha1.AWSProvider{
-							Role: "foo-bar-baz",
-						},
-					},
-				},
-			},
-			env: map[string]string{
-				"AWS_ACCESS_KEY_ID":     "1111",
-				"AWS_SECRET_ACCESS_KEY": "2222",
-			},
-			expectProvider:    true,
-			expectedKeyID:     "3333",
-			expectedSecretKey: "4444",
-		},
-		{
-			name:      "error out when secret with credentials does not exist",
-			namespace: "foo",
-			store: &esv1alpha1.SecretStore{
-				Spec: esv1alpha1.SecretStoreSpec{
-					Provider: &esv1alpha1.SecretStoreProvider{
-						AWS: &esv1alpha1.AWSProvider{
-							Auth: &esv1alpha1.AWSAuth{
-								SecretRef: esv1alpha1.AWSAuthSecretRef{
-									AccessKeyID: esmeta.SecretKeySelector{
-										Name: "othersecret",
-										Key:  "one",
-									},
-									SecretAccessKey: esmeta.SecretKeySelector{
-										Name: "othersecret",
-										Key:  "two",
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: `secrets "othersecret" not found`,
-		},
-		{
-			name:      "use credentials from secret to configure aws",
-			namespace: "foo",
-			store: &esv1alpha1.SecretStore{
-				Spec: esv1alpha1.SecretStoreSpec{
-					Provider: &esv1alpha1.SecretStoreProvider{
-						AWS: &esv1alpha1.AWSProvider{
-							Auth: &esv1alpha1.AWSAuth{
-								SecretRef: esv1alpha1.AWSAuthSecretRef{
-									AccessKeyID: esmeta.SecretKeySelector{
-										Name: "onesecret",
-										// Namespace is not set
-										Key: "one",
-									},
-									SecretAccessKey: esmeta.SecretKeySelector{
-										Name: "onesecret",
-										// Namespace is not set
-										Key: "two",
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			secrets: []v1.Secret{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name:      "onesecret",
-						Namespace: "foo",
-					},
-					Data: map[string][]byte{
-						"one": []byte("1111"),
-						"two": []byte("2222"),
-					},
-				},
-			},
-			expectProvider:    true,
-			expectedKeyID:     "1111",
-			expectedSecretKey: "2222",
-		},
-		{
-			name:      "error out when secret key does not exist",
-			namespace: "foo",
-			store: &esv1alpha1.SecretStore{
-				Spec: esv1alpha1.SecretStoreSpec{
-					Provider: &esv1alpha1.SecretStoreProvider{
-						AWS: &esv1alpha1.AWSProvider{
-							Auth: &esv1alpha1.AWSAuth{
-								SecretRef: esv1alpha1.AWSAuthSecretRef{
-									AccessKeyID: esmeta.SecretKeySelector{
-										Name: "brokensecret",
-										Key:  "one",
-									},
-									SecretAccessKey: esmeta.SecretKeySelector{
-										Name: "brokensecret",
-										Key:  "two",
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			secrets: []v1.Secret{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name:      "brokensecret",
-						Namespace: "foo",
-					},
-					Data: map[string][]byte{},
-				},
-			},
-			expectErr: "missing SecretAccessKey",
-		},
-		{
-			name:      "should not be able to access secrets from different namespace",
-			namespace: "foo",
-			store: &esv1alpha1.SecretStore{
-				Spec: esv1alpha1.SecretStoreSpec{
-					Provider: &esv1alpha1.SecretStoreProvider{
-						AWS: &esv1alpha1.AWSProvider{
-							Auth: &esv1alpha1.AWSAuth{
-								SecretRef: esv1alpha1.AWSAuthSecretRef{
-									AccessKeyID: esmeta.SecretKeySelector{
-										Name:      "onesecret",
-										Namespace: aws.String("evil"), // this should not be possible!
-										Key:       "one",
-									},
-									SecretAccessKey: esmeta.SecretKeySelector{
-										Name:      "onesecret",
-										Namespace: aws.String("evil"),
-										Key:       "two",
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			secrets: []v1.Secret{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name:      "onesecret",
-						Namespace: "evil",
-					},
-					Data: map[string][]byte{
-						"one": []byte("1111"),
-						"two": []byte("2222"),
-					},
-				},
-			},
-			expectErr: `secrets "onesecret" not found`,
-		},
-		{
-			name:      "ClusterStore should use credentials from a specific namespace",
-			namespace: "es-namespace",
-			store: &esv1alpha1.ClusterSecretStore{
-				TypeMeta: metav1.TypeMeta{
-					APIVersion: esv1alpha1.ClusterSecretStoreKindAPIVersion,
-					Kind:       esv1alpha1.ClusterSecretStoreKind,
-				},
-				Spec: esv1alpha1.SecretStoreSpec{
-					Provider: &esv1alpha1.SecretStoreProvider{
-						AWS: &esv1alpha1.AWSProvider{
-							Auth: &esv1alpha1.AWSAuth{
-								SecretRef: esv1alpha1.AWSAuthSecretRef{
-									AccessKeyID: esmeta.SecretKeySelector{
-										Name:      "onesecret",
-										Namespace: aws.String("platform-team-ns"),
-										Key:       "one",
-									},
-									SecretAccessKey: esmeta.SecretKeySelector{
-										Name:      "onesecret",
-										Namespace: aws.String("platform-team-ns"),
-										Key:       "two",
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			secrets: []v1.Secret{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name:      "onesecret",
-						Namespace: "platform-team-ns",
-					},
-					Data: map[string][]byte{
-						"one": []byte("1111"),
-						"two": []byte("2222"),
-					},
-				},
-			},
-			expectProvider:    true,
-			expectedKeyID:     "1111",
-			expectedSecretKey: "2222",
-		},
-		{
-			name:      "namespace is mandatory when using ClusterStore with SecretKeySelector",
-			namespace: "es-namespace",
-			store: &esv1alpha1.ClusterSecretStore{
-				TypeMeta: metav1.TypeMeta{
-					APIVersion: esv1alpha1.ClusterSecretStoreKindAPIVersion,
-					Kind:       esv1alpha1.ClusterSecretStoreKind,
-				},
-				Spec: esv1alpha1.SecretStoreSpec{
-					Provider: &esv1alpha1.SecretStoreProvider{
-						AWS: &esv1alpha1.AWSProvider{
-							Auth: &esv1alpha1.AWSAuth{
-								SecretRef: esv1alpha1.AWSAuthSecretRef{
-									AccessKeyID: esmeta.SecretKeySelector{
-										Name: "onesecret",
-										Key:  "one",
-									},
-									SecretAccessKey: esmeta.SecretKeySelector{
-										Name: "onesecret",
-										Key:  "two",
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectErr: "invalid ClusterSecretStore: missing AWS AccessKeyID Namespace",
-		},
-	}
-	for i := range rows {
-		row := rows[i]
-		t.Run(row.name, func(t *testing.T) {
-			testRow(t, row)
-		})
-	}
-}
-
-type TestSessionRow struct {
-	name              string
-	store             esv1alpha1.GenericStore
-	secrets           []v1.Secret
-	namespace         string
-	stsProvider       session.STSProvider
-	expectProvider    bool
-	expectErr         string
-	expectedKeyID     string
-	expectedSecretKey string
-	env               map[string]string
-}
-
-func testRow(t *testing.T, row TestSessionRow) {
-	kc := clientfake.NewClientBuilder().Build()
-	for i := range row.secrets {
-		err := kc.Create(context.Background(), &row.secrets[i])
-		assert.Nil(t, err)
-	}
-	for k, v := range row.env {
-		os.Setenv(k, v)
-	}
-	defer func() {
-		for k := range row.env {
-			os.Unsetenv(k)
-		}
-	}()
-	s, err := newSession(context.Background(), row.store, kc, row.namespace, row.stsProvider)
-	if !ErrorContains(err, row.expectErr) {
-		t.Errorf("expected error %s but found %s", row.expectErr, err.Error())
-	}
-	// pass test on expected error
-	if err != nil {
-		return
-	}
-	if row.expectProvider && s == nil {
-		t.Errorf("expected provider object, found nil")
-		return
-	}
-	creds, _ := s.Config.Credentials.Get()
-	assert.Equal(t, creds.AccessKeyID, row.expectedKeyID)
-	assert.Equal(t, creds.SecretAccessKey, row.expectedSecretKey)
-}
-
-func TestSMEnvCredentials(t *testing.T) {
-	k8sClient := clientfake.NewClientBuilder().Build()
-	os.Setenv("AWS_SECRET_ACCESS_KEY", "1111")
-	os.Setenv("AWS_ACCESS_KEY_ID", "2222")
-	defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
-	defer os.Unsetenv("AWS_ACCESS_KEY_ID")
-	s, err := newSession(context.Background(), &esv1alpha1.SecretStore{
-		Spec: esv1alpha1.SecretStoreSpec{
-			Provider: &esv1alpha1.SecretStoreProvider{
-				// defaults
-				AWS: &esv1alpha1.AWSProvider{},
-			},
-		},
-	}, k8sClient, "example-ns", session.DefaultSTSProvider)
-	assert.Nil(t, err)
-	assert.NotNil(t, s)
-	creds, err := s.Config.Credentials.Get()
-	assert.Nil(t, err)
-	assert.Equal(t, creds.AccessKeyID, "2222")
-	assert.Equal(t, creds.SecretAccessKey, "1111")
-}
-
-func TestSMAssumeRole(t *testing.T) {
-	k8sClient := clientfake.NewClientBuilder().Build()
-	sts := &fakesess.AssumeRoler{
-		AssumeRoleFunc: func(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) {
-			// make sure the correct role is passed in
-			assert.Equal(t, *input.RoleArn, "my-awesome-role")
-			return &sts.AssumeRoleOutput{
-				AssumedRoleUser: &sts.AssumedRoleUser{
-					Arn:           aws.String("1123132"),
-					AssumedRoleId: aws.String("xxxxx"),
-				},
-				Credentials: &sts.Credentials{
-					AccessKeyId:     aws.String("3333"),
-					SecretAccessKey: aws.String("4444"),
-					Expiration:      aws.Time(time.Now().Add(time.Hour)),
-					SessionToken:    aws.String("6666"),
-				},
-			}, nil
-		},
-	}
-	os.Setenv("AWS_SECRET_ACCESS_KEY", "1111")
-	os.Setenv("AWS_ACCESS_KEY_ID", "2222")
-	defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
-	defer os.Unsetenv("AWS_ACCESS_KEY_ID")
-	s, err := newSession(context.Background(), &esv1alpha1.SecretStore{
-		Spec: esv1alpha1.SecretStoreSpec{
-			Provider: &esv1alpha1.SecretStoreProvider{
-				// do assume role!
-				AWS: &esv1alpha1.AWSProvider{
-					Role: "my-awesome-role",
-				},
-			},
-		},
-	}, k8sClient, "example-ns", func(se *awssess.Session) stscreds.AssumeRoler {
-		// check if the correct temporary credentials were used
-		creds, err := se.Config.Credentials.Get()
-		assert.Nil(t, err)
-		assert.Equal(t, creds.AccessKeyID, "2222")
-		assert.Equal(t, creds.SecretAccessKey, "1111")
-		return sts
-	})
-	assert.Nil(t, err)
-	assert.NotNil(t, s)
-
-	creds, err := s.Config.Credentials.Get()
-	assert.Nil(t, err)
-	assert.Equal(t, creds.AccessKeyID, "3333")
-	assert.Equal(t, creds.SecretAccessKey, "4444")
-}
-
-func TestResolver(t *testing.T) {
-	tbl := []struct {
-		env     string
-		service string
-		url     string
-	}{
-		{
-			env:     SecretsManagerEndpointEnv,
-			service: "secretsmanager",
-			url:     "http://sm.foo",
-		},
-		{
-			env:     SSMEndpointEnv,
-			service: "ssm",
-			url:     "http://ssm.foo",
-		},
-		{
-			env:     STSEndpointEnv,
-			service: "sts",
-			url:     "http://sts.foo",
-		},
-	}
-
-	for _, item := range tbl {
-		os.Setenv(item.env, item.url)
-		defer os.Unsetenv(item.env)
-	}
-
-	f := ResolveEndpoint()
-
-	for _, item := range tbl {
-		ep, err := f.EndpointFor(item.service, "")
-		assert.Nil(t, err)
-		assert.Equal(t, item.url, ep.URL)
-	}
-}
-
-func ErrorContains(out error, want string) bool {
-	if out == nil {
-		return want == ""
-	}
-	if want == "" {
-		return false
-	}
-	return strings.Contains(out.Error(), want)
-}

+ 0 - 10
pkg/provider/aws/secretsmanager/secretsmanager_test.go

@@ -22,21 +22,11 @@ import (
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws"
 	awssm "github.com/aws/aws-sdk-go/service/secretsmanager"
 	awssm "github.com/aws/aws-sdk-go/service/secretsmanager"
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp"
-	"github.com/stretchr/testify/assert"
 
 
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	fakesm "github.com/external-secrets/external-secrets/pkg/provider/aws/secretsmanager/fake"
 	fakesm "github.com/external-secrets/external-secrets/pkg/provider/aws/secretsmanager/fake"
-	sess "github.com/external-secrets/external-secrets/pkg/provider/aws/session"
 )
 )
 
 
-func TestConstructor(t *testing.T) {
-	s, err := sess.New("1111", "2222", "foo", "", nil)
-	assert.Nil(t, err)
-	c, err := New(s)
-	assert.Nil(t, err)
-	assert.NotNil(t, c.client)
-}
-
 type secretsManagerTestCase struct {
 type secretsManagerTestCase struct {
 	fakeClient     *fakesm.Client
 	fakeClient     *fakesm.Client
 	apiInput       *awssm.GetSecretValueInput
 	apiInput       *awssm.GetSecretValueInput

+ 0 - 71
pkg/provider/aws/session/session.go

@@ -1,71 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package session
-
-import (
-	"fmt"
-
-	"github.com/aws/aws-sdk-go/aws"
-	"github.com/aws/aws-sdk-go/aws/credentials"
-	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
-	"github.com/aws/aws-sdk-go/aws/request"
-	awssess "github.com/aws/aws-sdk-go/aws/session"
-	"github.com/aws/aws-sdk-go/service/sts"
-	ctrl "sigs.k8s.io/controller-runtime"
-)
-
-// Config contains configuration to create a new AWS provider.
-type Config struct {
-	AssumeRole string
-	Region     string
-	APIRetries int
-}
-
-var log = ctrl.Log.WithName("provider").WithName("aws")
-
-// New creates a new aws session based on the supported input methods.
-// https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials
-func New(sak, aks, region, role string, stsprovider STSProvider) (*awssess.Session, error) {
-	config := aws.NewConfig()
-	sessionOpts := awssess.Options{
-		Config: *config,
-	}
-	if sak != "" && aks != "" {
-		sessionOpts.Config.Credentials = credentials.NewStaticCredentials(aks, sak, "")
-		sessionOpts.SharedConfigState = awssess.SharedConfigDisable
-	}
-	sess, err := awssess.NewSessionWithOptions(sessionOpts)
-	if err != nil {
-		return nil, fmt.Errorf("unable to create aws session: %w", err)
-	}
-	if region != "" {
-		log.V(1).Info("using region", "region", region)
-		sess.Config.WithRegion(region)
-	}
-
-	if role != "" {
-		log.V(1).Info("assuming role", "role", role)
-		stsclient := stsprovider(sess)
-		sess.Config.WithCredentials(stscreds.NewCredentialsWithClient(stsclient, role))
-	}
-	sess.Handlers.Build.PushBack(request.WithAppendUserAgent("external-secrets"))
-	return sess, nil
-}
-
-type STSProvider func(*awssess.Session) stscreds.AssumeRoler
-
-func DefaultSTSProvider(sess *awssess.Session) stscreds.AssumeRoler {
-	return sts.New(sess)
-}

+ 0 - 91
pkg/provider/aws/session/session_test.go

@@ -1,91 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package session
-
-import (
-	"testing"
-	"time"
-
-	"github.com/aws/aws-sdk-go/aws"
-	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
-	"github.com/aws/aws-sdk-go/aws/session"
-	"github.com/aws/aws-sdk-go/service/sts"
-	"github.com/stretchr/testify/assert"
-
-	fakesess "github.com/external-secrets/external-secrets/pkg/provider/aws/session/fake"
-)
-
-func TestSession(t *testing.T) {
-	tbl := []struct {
-		test              string
-		aks               string
-		sak               string
-		region            string
-		role              string
-		sts               STSProvider
-		expectedKeyID     string
-		expectedSecretKey string
-	}{
-		{
-			test:              "test default role provider",
-			aks:               "2222",
-			sak:               "1111",
-			region:            "xxxxx",
-			role:              "",
-			sts:               DefaultSTSProvider,
-			expectedSecretKey: "1111",
-			expectedKeyID:     "2222",
-		},
-		{
-			test:   "test custom sts provider",
-			aks:    "1111",
-			sak:    "2222",
-			region: "xxxxx",
-			role:   "zzzzz",
-			sts: func(*session.Session) stscreds.AssumeRoler {
-				return &fakesess.AssumeRoler{
-					AssumeRoleFunc: func(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) {
-						assert.Equal(t, *input.RoleArn, "zzzzz")
-						return &sts.AssumeRoleOutput{
-							AssumedRoleUser: &sts.AssumedRoleUser{
-								Arn:           aws.String("1123132"),
-								AssumedRoleId: aws.String("xxxxx"),
-							},
-							Credentials: &sts.Credentials{
-								SecretAccessKey: aws.String("3333"),
-								AccessKeyId:     aws.String("4444"),
-								Expiration:      aws.Time(time.Now().Add(time.Hour)),
-								SessionToken:    aws.String("6666"),
-							},
-						}, nil
-					},
-				}
-			},
-			expectedSecretKey: "3333",
-			expectedKeyID:     "4444",
-		},
-	}
-	for i := range tbl {
-		row := tbl[i]
-		t.Run(row.test, func(t *testing.T) {
-			sess, err := New(row.sak, row.aks, row.region, row.role, row.sts)
-			assert.Nil(t, err)
-			creds, err := sess.Config.Credentials.Get()
-			assert.Nil(t, err)
-			assert.Equal(t, row.expectedKeyID, creds.AccessKeyID)
-			assert.Equal(t, row.expectedSecretKey, creds.SecretAccessKey)
-		})
-	}
-}

+ 6 - 9
pkg/provider/aws/util/errors.go

@@ -16,17 +16,14 @@ package util
 
 
 import (
 import (
 	"errors"
 	"errors"
-	"fmt"
-
-	"github.com/aws/aws-sdk-go/aws/awserr"
+	"regexp"
 )
 )
 
 
-// SanitizeErr removes sanitizes the error string
+var regexReqID = regexp.MustCompile(`request id: (\S+)`)
+
+// SanitizeErr sanitizes the error string
 // because the requestID must not be included in the error.
 // because the requestID must not be included in the error.
+// otherwise the secrets keeps syncing.
 func SanitizeErr(err error) error {
 func SanitizeErr(err error) error {
-	var bErr awserr.BatchedErrors
-	if errors.As(bErr, &bErr) {
-		return fmt.Errorf("%s: %s", bErr.Code(), bErr.Message())
-	}
-	return err
+	return errors.New(string(regexReqID.ReplaceAll([]byte(err.Error()), nil)))
 }
 }

+ 42 - 0
pkg/provider/aws/util/errors_test.go

@@ -0,0 +1,42 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package util
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSanitize(t *testing.T) {
+	tbl := []struct {
+		err      error
+		expected string
+	}{
+		{
+			err:      errors.New("some AccessDeniedException: User: arn:aws:sts::123123123123:assumed-role/foobar is not authorized to perform: secretsmanager:GetSecretValue on resource: example\n\tstatus code: 400, request id: df34-75f-0c5f-4b4c-a71a-f93d581d177c"),
+			expected: "some AccessDeniedException: User: arn:aws:sts::123123123123:assumed-role/foobar is not authorized to perform: secretsmanager:GetSecretValue on resource: example\n\tstatus code: 400, ",
+		},
+		{
+			err:      errors.New("some generic error"),
+			expected: "some generic error",
+		},
+	}
+
+	for _, c := range tbl {
+		out := SanitizeErr(c.err)
+		assert.Equal(t, c.expected, out.Error())
+	}
+}

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

@@ -0,0 +1,47 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package util
+
+import (
+	"fmt"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+)
+
+const (
+	errNilStore         = "found nil store"
+	errMissingStoreSpec = "store is missing spec"
+	errMissingProvider  = "storeSpec is missing provider"
+	errInvalidProvider  = "invalid provider spec. Missing AWS field in store %s"
+)
+
+// GetAWSProvider does the necessary nil checks on the generic store
+// it returns the aws provider or an error.
+func GetAWSProvider(store esv1alpha1.GenericStore) (*esv1alpha1.AWSProvider, error) {
+	if store == nil {
+		return nil, fmt.Errorf(errNilStore)
+	}
+	spc := store.GetSpec()
+	if spc == nil {
+		return nil, fmt.Errorf(errMissingStoreSpec)
+	}
+	if spc.Provider == nil {
+		return nil, fmt.Errorf(errMissingProvider)
+	}
+	prov := spc.Provider.AWS
+	if prov == nil {
+		return nil, fmt.Errorf(errInvalidProvider, store.GetObjectMeta().String())
+	}
+	return prov, nil
+}