From 7b8fef2c182702c018b8ed078ae310d52ecd8ad5 Mon Sep 17 00:00:00 2001
From: Gaurav Dasson
Date: Thu, 11 May 2023 04:10:07 -0500
Subject: [PATCH] :sparkles: Enabling Vault IAM auth (#2208)
* Enabling Vault IAM auth
Signed-off-by: Gaurav Dasson
* Adding spec
Signed-off-by: Gaurav Dasson
* Adding test cases and decoupling vault provider from aws for iam auth
Signed-off-by: Gaurav Dasson
* Fixing comments
Signed-off-by: Gaurav Dasson
* Fixing linter issues
Signed-off-by: Gaurav Dasson
* Fixing the check-diff errors
Signed-off-by: Gaurav Dasson
* Adding support for assumeRole operations when using static creds
Signed-off-by: Gaurav Dasson
* Bumping the dependencies to fix the go.mod/go.sum conflicts
Signed-off-by: Gaurav Dasson
* Bumping up e2e go mod files
Signed-off-by: Gaurav Dasson
---------
Signed-off-by: Gaurav Dasson
---
.../v1beta1/secretstore_vault_types.go | 59 ++++
.../v1beta1/zz_generated.deepcopy.go | 97 ++++++
...ternal-secrets.io_clustersecretstores.yaml | 129 ++++++++
.../external-secrets.io_secretstores.yaml | 129 ++++++++
...ternal-secrets.io_vaultdynamicsecrets.yaml | 128 ++++++++
deploy/crds/bundle.yaml | 264 +++++++++++++++
docs/api/spec.md | 265 +++++++++++++++
docs/provider/hashicorp-vault.md | 53 ++-
...ult-iam-store-controller-pod-identity.yaml | 21 ++
docs/snippets/vault-iam-store-sa.yaml | 7 +
.../vault-iam-store-static-creds.yaml | 33 ++
docs/snippets/vault-iam-store.yaml | 24 ++
e2e/go.mod | 1 -
e2e/go.sum | 1 -
go.mod | 5 +
go.sum | 17 +-
pkg/provider/vault/iamauth/iamauth.go | 309 ++++++++++++++++++
pkg/provider/vault/iamauth/iamauth_test.go | 60 ++++
pkg/provider/vault/util/vault.go | 3 +
pkg/provider/vault/vault.go | 193 ++++++++++-
pkg/provider/vault/vault_test.go | 62 ++++
21 files changed, 1854 insertions(+), 6 deletions(-)
create mode 100644 docs/snippets/vault-iam-store-controller-pod-identity.yaml
create mode 100644 docs/snippets/vault-iam-store-sa.yaml
create mode 100644 docs/snippets/vault-iam-store-static-creds.yaml
create mode 100644 docs/snippets/vault-iam-store.yaml
create mode 100644 pkg/provider/vault/iamauth/iamauth.go
create mode 100644 pkg/provider/vault/iamauth/iamauth_test.go
diff --git a/apis/externalsecrets/v1beta1/secretstore_vault_types.go b/apis/externalsecrets/v1beta1/secretstore_vault_types.go
index a6b095be5..6230e1502 100644
--- a/apis/externalsecrets/v1beta1/secretstore_vault_types.go
+++ b/apis/externalsecrets/v1beta1/secretstore_vault_types.go
@@ -112,6 +112,11 @@ type VaultAuth struct {
// Cert authentication method
// +optional
Cert *VaultCertAuth `json:"cert,omitempty"`
+
+ // Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials
+ // AWS IAM authentication method
+ // +optional
+ Iam *VaultIamAuth `json:"iam,omitempty"`
}
// VaultAppRole authenticates with Vault using the App Role auth mechanism,
@@ -178,6 +183,37 @@ type VaultLdapAuth struct {
SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"`
}
+// VaultAwsAuth 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 try to load credentials from its own service account assuming it is IRSA enabled.
+type VaultAwsAuth struct {
+ // +optional
+ SecretRef *VaultAwsAuthSecretRef `json:"secretRef,omitempty"`
+ // +optional
+ JWTAuth *VaultAwsJWTAuth `json:"jwt,omitempty"`
+}
+
+// VaultAWSAuthSecretRef holds secret references for AWS credentials
+// both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
+type VaultAwsAuthSecretRef struct {
+ // The AccessKeyID is used for authentication
+ AccessKeyID esmeta.SecretKeySelector `json:"accessKeyIDSecretRef,omitempty"`
+
+ // The SecretAccessKey is used for authentication
+ SecretAccessKey esmeta.SecretKeySelector `json:"secretAccessKeySecretRef,omitempty"`
+
+ // The SessionToken used for authentication
+ // This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
+ // see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
+ // +Optional
+ SessionToken *esmeta.SecretKeySelector `json:"sessionTokenSecretRef,omitempty"`
+}
+
+// Authenticate against AWS using service account tokens.
+type VaultAwsJWTAuth struct {
+ ServiceAccountRef *esmeta.ServiceAccountSelector `json:"serviceAccountRef,omitempty"`
+}
+
// VaultKubernetesServiceAccountTokenAuth authenticates with Vault using a temporary
// Kubernetes service account token retrieved by the `TokenRequest` API.
type VaultKubernetesServiceAccountTokenAuth struct {
@@ -237,3 +273,26 @@ type VaultCertAuth struct {
// authenticate with Vault using the Cert authentication method
SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"`
}
+
+// VaultIamAuth authenticates with Vault using the Vault's AWS IAM authentication method. Refer: https://developer.hashicorp.com/vault/docs/auth/aws
+type VaultIamAuth struct {
+
+ // Path where the AWS auth method is enabled in Vault, e.g: "aws"
+ Path string `json:"path,omitempty"`
+ // AWS region
+ Region string `json:"region,omitempty"`
+ // This is the AWS role to be assumed before talking to vault
+ AWSIAMRole string `json:"role,omitempty"`
+ // Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine
+ Role string `json:"vaultRole"`
+ // AWS External ID set on assumed IAM roles
+ ExternalID string `json:"externalID,omitempty"`
+ // X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws
+ VaultAWSIAMServerID string `json:"vaultAwsIamServerID,omitempty"`
+ // Specify credentials in a Secret object
+ // +optional
+ SecretRef *VaultAwsAuthSecretRef `json:"secretRef,omitempty"`
+ // Specify a service account with IRSA enabled
+ // +optional
+ JWTAuth *VaultAwsJWTAuth `json:"jwt,omitempty"`
+}
diff --git a/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go b/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go
index dfe5ce8eb..2a97b7ddc 100644
--- a/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go
+++ b/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go
@@ -2051,6 +2051,11 @@ func (in *VaultAuth) DeepCopyInto(out *VaultAuth) {
*out = new(VaultCertAuth)
(*in).DeepCopyInto(*out)
}
+ if in.Iam != nil {
+ in, out := &in.Iam, &out.Iam
+ *out = new(VaultIamAuth)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultAuth.
@@ -2063,6 +2068,73 @@ func (in *VaultAuth) DeepCopy() *VaultAuth {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VaultAwsAuth) DeepCopyInto(out *VaultAwsAuth) {
+ *out = *in
+ if in.SecretRef != nil {
+ in, out := &in.SecretRef, &out.SecretRef
+ *out = new(VaultAwsAuthSecretRef)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.JWTAuth != nil {
+ in, out := &in.JWTAuth, &out.JWTAuth
+ *out = new(VaultAwsJWTAuth)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultAwsAuth.
+func (in *VaultAwsAuth) DeepCopy() *VaultAwsAuth {
+ if in == nil {
+ return nil
+ }
+ out := new(VaultAwsAuth)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VaultAwsAuthSecretRef) DeepCopyInto(out *VaultAwsAuthSecretRef) {
+ *out = *in
+ in.AccessKeyID.DeepCopyInto(&out.AccessKeyID)
+ in.SecretAccessKey.DeepCopyInto(&out.SecretAccessKey)
+ if in.SessionToken != nil {
+ in, out := &in.SessionToken, &out.SessionToken
+ *out = new(metav1.SecretKeySelector)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultAwsAuthSecretRef.
+func (in *VaultAwsAuthSecretRef) DeepCopy() *VaultAwsAuthSecretRef {
+ if in == nil {
+ return nil
+ }
+ out := new(VaultAwsAuthSecretRef)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VaultAwsJWTAuth) DeepCopyInto(out *VaultAwsJWTAuth) {
+ *out = *in
+ if in.ServiceAccountRef != nil {
+ in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
+ *out = new(metav1.ServiceAccountSelector)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultAwsJWTAuth.
+func (in *VaultAwsJWTAuth) DeepCopy() *VaultAwsJWTAuth {
+ if in == nil {
+ return nil
+ }
+ out := new(VaultAwsJWTAuth)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultCertAuth) DeepCopyInto(out *VaultCertAuth) {
*out = *in
@@ -2080,6 +2152,31 @@ func (in *VaultCertAuth) DeepCopy() *VaultCertAuth {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VaultIamAuth) DeepCopyInto(out *VaultIamAuth) {
+ *out = *in
+ if in.SecretRef != nil {
+ in, out := &in.SecretRef, &out.SecretRef
+ *out = new(VaultAwsAuthSecretRef)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.JWTAuth != nil {
+ in, out := &in.JWTAuth, &out.JWTAuth
+ *out = new(VaultAwsJWTAuth)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultIamAuth.
+func (in *VaultIamAuth) DeepCopy() *VaultIamAuth {
+ if in == nil {
+ return nil
+ }
+ out := new(VaultIamAuth)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultJwtAuth) DeepCopyInto(out *VaultJwtAuth) {
*out = *in
diff --git a/config/crds/bases/external-secrets.io_clustersecretstores.yaml b/config/crds/bases/external-secrets.io_clustersecretstores.yaml
index 7aaedbc23..55b8a77b3 100644
--- a/config/crds/bases/external-secrets.io_clustersecretstores.yaml
+++ b/config/crds/bases/external-secrets.io_clustersecretstores.yaml
@@ -3003,6 +3003,135 @@ spec:
type: string
type: object
type: object
+ iam:
+ description: Iam authenticates with vault by passing a
+ special AWS request signed with AWS IAM credentials
+ AWS IAM authentication method
+ properties:
+ externalID:
+ description: AWS External ID set on assumed IAM roles
+ type: string
+ jwt:
+ description: Specify a service account with IRSA enabled
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: Audience specifies the `aud`
+ claim for the service account token If the
+ service account uses a well-known annotation
+ for e.g. IRSA or GCP Workload Identity then
+ this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ 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
+ path:
+ description: 'Path where the AWS auth method is enabled
+ in Vault, e.g: "aws"'
+ type: string
+ region:
+ description: AWS region
+ type: string
+ role:
+ description: This is the AWS role to be assumed before
+ talking to vault
+ type: string
+ secretRef:
+ description: Specify credentials in a Secret object
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret
+ resource's `data` field to be used. Some
+ instances of this field may be defaulted,
+ in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret
+ resource's `data` field to be used. Some
+ instances of this field may be defaulted,
+ in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ sessionTokenSecretRef:
+ description: 'The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey
+ are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html'
+ properties:
+ key:
+ description: The key of the entry in the Secret
+ resource's `data` field to be used. Some
+ instances of this field may be defaulted,
+ in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ type: object
+ vaultAwsIamServerID:
+ description: 'X-Vault-AWS-IAM-Server-ID is an additional
+ header used by Vault IAM auth method to mitigate
+ against different types of replay attacks. More
+ details here: https://developer.hashicorp.com/vault/docs/auth/aws'
+ type: string
+ vaultRole:
+ description: Vault Role. In vault, a role describes
+ an identity with a set of permissions, groups, or
+ policies you want to attach a user of the secrets
+ engine
+ type: string
+ required:
+ - vaultRole
+ type: object
jwt:
description: Jwt authenticates with Vault by passing role
and JWT token using the JWT/OIDC authentication method
diff --git a/config/crds/bases/external-secrets.io_secretstores.yaml b/config/crds/bases/external-secrets.io_secretstores.yaml
index 91cab3799..875fe420e 100644
--- a/config/crds/bases/external-secrets.io_secretstores.yaml
+++ b/config/crds/bases/external-secrets.io_secretstores.yaml
@@ -3003,6 +3003,135 @@ spec:
type: string
type: object
type: object
+ iam:
+ description: Iam authenticates with vault by passing a
+ special AWS request signed with AWS IAM credentials
+ AWS IAM authentication method
+ properties:
+ externalID:
+ description: AWS External ID set on assumed IAM roles
+ type: string
+ jwt:
+ description: Specify a service account with IRSA enabled
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: Audience specifies the `aud`
+ claim for the service account token If the
+ service account uses a well-known annotation
+ for e.g. IRSA or GCP Workload Identity then
+ this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ 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
+ path:
+ description: 'Path where the AWS auth method is enabled
+ in Vault, e.g: "aws"'
+ type: string
+ region:
+ description: AWS region
+ type: string
+ role:
+ description: This is the AWS role to be assumed before
+ talking to vault
+ type: string
+ secretRef:
+ description: Specify credentials in a Secret object
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret
+ resource's `data` field to be used. Some
+ instances of this field may be defaulted,
+ in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret
+ resource's `data` field to be used. Some
+ instances of this field may be defaulted,
+ in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ sessionTokenSecretRef:
+ description: 'The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey
+ are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html'
+ properties:
+ key:
+ description: The key of the entry in the Secret
+ resource's `data` field to be used. Some
+ instances of this field may be defaulted,
+ in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ type: object
+ vaultAwsIamServerID:
+ description: 'X-Vault-AWS-IAM-Server-ID is an additional
+ header used by Vault IAM auth method to mitigate
+ against different types of replay attacks. More
+ details here: https://developer.hashicorp.com/vault/docs/auth/aws'
+ type: string
+ vaultRole:
+ description: Vault Role. In vault, a role describes
+ an identity with a set of permissions, groups, or
+ policies you want to attach a user of the secrets
+ engine
+ type: string
+ required:
+ - vaultRole
+ type: object
jwt:
description: Jwt authenticates with Vault by passing role
and JWT token using the JWT/OIDC authentication method
diff --git a/config/crds/bases/generators.external-secrets.io_vaultdynamicsecrets.yaml b/config/crds/bases/generators.external-secrets.io_vaultdynamicsecrets.yaml
index 3fb8a2e18..b53d5f442 100644
--- a/config/crds/bases/generators.external-secrets.io_vaultdynamicsecrets.yaml
+++ b/config/crds/bases/generators.external-secrets.io_vaultdynamicsecrets.yaml
@@ -139,6 +139,134 @@ spec:
type: string
type: object
type: object
+ iam:
+ description: Iam authenticates with vault by passing a special
+ AWS request signed with AWS IAM credentials AWS IAM authentication
+ method
+ properties:
+ externalID:
+ description: AWS External ID set on assumed IAM roles
+ type: string
+ jwt:
+ description: Specify a service account with IRSA enabled
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: Audience specifies the `aud` claim
+ for the service account token If the service
+ account uses a well-known annotation for e.g.
+ IRSA or GCP Workload Identity then this audiences
+ will be appended to the list
+ items:
+ type: string
+ type: array
+ 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
+ path:
+ description: 'Path where the AWS auth method is enabled
+ in Vault, e.g: "aws"'
+ type: string
+ region:
+ description: AWS region
+ type: string
+ role:
+ description: This is the AWS role to be assumed before
+ talking to vault
+ type: string
+ secretRef:
+ description: Specify credentials in a Secret object
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret
+ resource's `data` field to be used. Some instances
+ of this field may be defaulted, in others it
+ may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret
+ resource's `data` field to be used. Some instances
+ of this field may be defaulted, in others it
+ may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ sessionTokenSecretRef:
+ description: 'The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey
+ are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html'
+ properties:
+ key:
+ description: The key of the entry in the Secret
+ resource's `data` field to be used. Some instances
+ of this field may be defaulted, in others it
+ may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ type: object
+ vaultAwsIamServerID:
+ description: 'X-Vault-AWS-IAM-Server-ID is an additional
+ header used by Vault IAM auth method to mitigate against
+ different types of replay attacks. More details here:
+ https://developer.hashicorp.com/vault/docs/auth/aws'
+ type: string
+ vaultRole:
+ description: Vault Role. In vault, a role describes an
+ identity with a set of permissions, groups, or policies
+ you want to attach a user of the secrets engine
+ type: string
+ required:
+ - vaultRole
+ type: object
jwt:
description: Jwt authenticates with Vault by passing role
and JWT token using the JWT/OIDC authentication method
diff --git a/deploy/crds/bundle.yaml b/deploy/crds/bundle.yaml
index 7b697c7f0..54c1533cd 100644
--- a/deploy/crds/bundle.yaml
+++ b/deploy/crds/bundle.yaml
@@ -2656,6 +2656,94 @@ spec:
type: string
type: object
type: object
+ iam:
+ description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method
+ properties:
+ externalID:
+ description: AWS External ID set on assumed IAM roles
+ type: string
+ jwt:
+ description: Specify a service account with IRSA enabled
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ 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
+ path:
+ description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"'
+ type: string
+ region:
+ description: AWS region
+ type: string
+ role:
+ description: This is the AWS role to be assumed before talking to vault
+ type: string
+ secretRef:
+ description: Specify credentials in a Secret object
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ sessionTokenSecretRef:
+ description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html'
+ properties:
+ key:
+ description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ type: object
+ vaultAwsIamServerID:
+ description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws'
+ type: string
+ vaultRole:
+ description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine
+ type: string
+ required:
+ - vaultRole
+ type: object
jwt:
description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method
properties:
@@ -6113,6 +6201,94 @@ spec:
type: string
type: object
type: object
+ iam:
+ description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method
+ properties:
+ externalID:
+ description: AWS External ID set on assumed IAM roles
+ type: string
+ jwt:
+ description: Specify a service account with IRSA enabled
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ 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
+ path:
+ description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"'
+ type: string
+ region:
+ description: AWS region
+ type: string
+ role:
+ description: This is the AWS role to be assumed before talking to vault
+ type: string
+ secretRef:
+ description: Specify credentials in a Secret object
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ sessionTokenSecretRef:
+ description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html'
+ properties:
+ key:
+ description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ type: object
+ vaultAwsIamServerID:
+ description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws'
+ type: string
+ vaultRole:
+ description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine
+ type: string
+ required:
+ - vaultRole
+ type: object
jwt:
description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method
properties:
@@ -7154,6 +7330,94 @@ spec:
type: string
type: object
type: object
+ iam:
+ description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method
+ properties:
+ externalID:
+ description: AWS External ID set on assumed IAM roles
+ type: string
+ jwt:
+ description: Specify a service account with IRSA enabled
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ 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
+ path:
+ description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"'
+ type: string
+ region:
+ description: AWS region
+ type: string
+ role:
+ description: This is the AWS role to be assumed before talking to vault
+ type: string
+ secretRef:
+ description: Specify credentials in a Secret object
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ sessionTokenSecretRef:
+ description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html'
+ properties:
+ key:
+ description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required.
+ type: string
+ name:
+ description: The name of the Secret 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
+ type: object
+ type: object
+ vaultAwsIamServerID:
+ description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws'
+ type: string
+ vaultRole:
+ description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine
+ type: string
+ required:
+ - vaultRole
+ type: object
jwt:
description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method
properties:
diff --git a/docs/api/spec.md b/docs/api/spec.md
index a03ccb898..428ddd506 100644
--- a/docs/api/spec.md
+++ b/docs/api/spec.md
@@ -5543,6 +5543,158 @@ VaultCertAuth
Cert authentication method
+
+
+iam
+
+
+VaultIamAuth
+
+
+ |
+
+(Optional)
+ Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials
+AWS IAM authentication method
+ |
+
+
+
+VaultAwsAuth
+
+
+
VaultAwsAuth 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 try to load credentials from its own service account assuming it is IRSA enabled.
+
+
+VaultAwsAuthSecretRef
+
+
+(Appears on:
+VaultAwsAuth,
+VaultIamAuth)
+
+
+
VaultAWSAuthSecretRef holds secret references for AWS credentials
+both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
+
+
+VaultAwsJWTAuth
+
+
+(Appears on:
+VaultAwsAuth,
+VaultIamAuth)
+
+
+
Authenticate against AWS using service account tokens.
+
+
VaultCertAuth
@@ -5594,6 +5746,119 @@ authenticate with Vault using the Cert authentication method
+VaultIamAuth
+
+
+(Appears on:
+VaultAuth)
+
+
+
VaultIamAuth authenticates with Vault using the Vault’s AWS IAM authentication method. Refer: https://developer.hashicorp.com/vault/docs/auth/aws
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+path
+
+string
+
+ |
+
+ Path where the AWS auth method is enabled in Vault, e.g: “aws”
+ |
+
+
+
+region
+
+string
+
+ |
+
+ AWS region
+ |
+
+
+
+role
+
+string
+
+ |
+
+ This is the AWS role to be assumed before talking to vault
+ |
+
+
+
+vaultRole
+
+string
+
+ |
+
+ Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine
+ |
+
+
+
+externalID
+
+string
+
+ |
+
+ AWS External ID set on assumed IAM roles
+ |
+
+
+
+vaultAwsIamServerID
+
+string
+
+ |
+
+ X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws
+ |
+
+
+
+secretRef
+
+
+VaultAwsAuthSecretRef
+
+
+ |
+
+(Optional)
+ Specify credentials in a Secret object
+ |
+
+
+
+jwt
+
+
+VaultAwsJWTAuth
+
+
+ |
+
+(Optional)
+ Specify a service account with IRSA enabled
+ |
+
+
+
VaultJwtAuth
diff --git a/docs/provider/hashicorp-vault.md b/docs/provider/hashicorp-vault.md
index 891666a64..20ced7d04 100644
--- a/docs/provider/hashicorp-vault.md
+++ b/docs/provider/hashicorp-vault.md
@@ -271,8 +271,9 @@ We support five different modes for authentication:
[token-based](https://www.vaultproject.io/docs/auth/token),
[appRole](https://www.vaultproject.io/docs/auth/approle),
[kubernetes-native](https://www.vaultproject.io/docs/auth/kubernetes),
-[ldap](https://www.vaultproject.io/docs/auth/ldap) and
-[jwt/oidc](https://www.vaultproject.io/docs/auth/jwt), each one comes with it's own
+[ldap](https://www.vaultproject.io/docs/auth/ldap),
+[jwt/oidc](https://www.vaultproject.io/docs/auth/jwt) and
+[awsAuth](https://developer.hashicorp.com/vault/docs/auth/aws), each one comes with it's own
trade-offs. Depending on the authentication method you need to adapt your environment.
#### Token-based authentication
@@ -333,6 +334,54 @@ or `Kind=ClusterSecretStore` resource.
```
**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `secretRef` with the namespace where the secret resides.
+#### AWS IAM authentication
+
+[AWS IAM](https://developer.hashicorp.com/vault/docs/auth/aws) uses either a
+set of AWS Programmatic access credentials stored in a `Kind=Secret` and referenced by the
+`secretRef` or by getting the authentication token from an [IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) enabled service account
+
+### Access Key ID & Secret Access Key
+You can store Access Key ID & Secret Access Key in a `Kind=Secret` and reference it from a SecretStore.
+
+```yaml
+{% include 'vault-iam-store-static-creds.yaml' %}
+```
+
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `accessKeyIDSecretRef`, `secretAccessKeySecretRef` with the namespaces where the secrets reside.
+
+### EKS Service Account credentials
+
+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
+{% include 'vault-iam-store-sa.yaml' %}
+```
+
+Reference the service account from above in the Secret Store:
+
+```yaml
+{% include 'vault-iam-store.yaml' %}
+```
+### Controller's Pod Identity
+
+This is basicially a zero-configuration authentication approach that inherits the credentials from the controller's pod identity
+
+This approach assumes that appropriate IRSA setup is done controller's pod (i.e. IRSA enabled IAM role is created appropriately and controller's service account is annotated appropriately with the annotation "eks.amazonaws.com/role-arn" to enable IRSA)
+
+```yaml
+{% include 'vault-iam-store-controller-pod-identity.yaml' %}
+```
+
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` for `serviceAccountRef` with the namespace where the service account resides.
+
+```yaml
+{% include 'vault-jwt-store.yaml' %}
+```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `secretRef` with the namespace where the secret resides.
+
### PushSecret
Vault supports PushSecret features which allow you to sync a given kubernetes secret key into a hashicorp vault secret. In order to do so, it is expected that the secret key is a valid JSON object.
diff --git a/docs/snippets/vault-iam-store-controller-pod-identity.yaml b/docs/snippets/vault-iam-store-controller-pod-identity.yaml
new file mode 100644
index 000000000..55f99695c
--- /dev/null
+++ b/docs/snippets/vault-iam-store-controller-pod-identity.yaml
@@ -0,0 +1,21 @@
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: vault-backend-aws-iam
+spec:
+ provider:
+ vault:
+ server: "http://my.vault.server:8200"
+ path: secret
+ version: v2
+ namespace:
+ auth:
+ iam:
+ # Path where the AWS auth method is enabled in Vault, e.g: "aws/". Defaults to aws
+ path: aws
+ # AWS Region. Defaults to us-east-1
+ region: us-east-1
+ # Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine
+ vaultRole: vault-role-for-aws-iam-auth
+ # Optional. Placeholder to supply header X-Vault-AWS-IAM-Server-ID. It is an additional (optional) header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws
+ vaultAwsIamServerID: example-vaultAwsIamServerID
\ No newline at end of file
diff --git a/docs/snippets/vault-iam-store-sa.yaml b/docs/snippets/vault-iam-store-sa.yaml
new file mode 100644
index 000000000..2ceb3cc28
--- /dev/null
+++ b/docs/snippets/vault-iam-store-sa.yaml
@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ annotations:
+ eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-irsa-enabled-role
+ name: my-serviceaccount
+ namespace: default
\ No newline at end of file
diff --git a/docs/snippets/vault-iam-store-static-creds.yaml b/docs/snippets/vault-iam-store-static-creds.yaml
new file mode 100644
index 000000000..2a929b93a
--- /dev/null
+++ b/docs/snippets/vault-iam-store-static-creds.yaml
@@ -0,0 +1,33 @@
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: vault-backend-aws-iam
+spec:
+ provider:
+ vault:
+ server: "http://my.vault.server:8200"
+ path: secret
+ version: v2
+ namespace:
+ auth:
+ iam:
+ # Path where the AWS auth method is enabled in Vault, e.g: "aws/". Defaults to aws
+ path: aws
+ # AWS Region. Defaults to us-east-1
+ region: us-east-1
+ # optional: assume role before fetching secrets
+ role: arn:aws:iam::1234567890:role/role-a
+ # Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine
+ vaultRole: vault-role-for-aws-iam-auth
+ # Optional. Placeholder to supply header X-Vault-AWS-IAM-Server-ID. It is an additional (optional) header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws
+ vaultAwsIamServerID: example-vaultAwsIamServerID
+ secretRef: #Use this method when you have static AWS creds.
+ accessKeyIDSecretRef:
+ name: vault-iam-creds-secret
+ key: access-key
+ secretAccessKeySecretRef:
+ name: vault-iam-creds-secret
+ key: secret-access-key
+ sessionTokenSecretRef:
+ name: vault-iam-creds-secret
+ key: secret-session-token
\ No newline at end of file
diff --git a/docs/snippets/vault-iam-store.yaml b/docs/snippets/vault-iam-store.yaml
new file mode 100644
index 000000000..c78610f68
--- /dev/null
+++ b/docs/snippets/vault-iam-store.yaml
@@ -0,0 +1,24 @@
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: vault-backend-aws-iam
+spec:
+ provider:
+ vault:
+ server: "http://my.vault.server:8200"
+ path: secret
+ version: v2
+ namespace:
+ auth:
+ iam:
+ # Path where the AWS auth method is enabled in Vault, e.g: "aws/". Defaults to aws
+ path: aws
+ # AWS Region. Defaults to us-east-1
+ region: us-east-1
+ # Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine
+ vaultRole: vault-role-for-aws-iam-auth
+ # Optional. Placeholder to supply header X-Vault-AWS-IAM-Server-ID. It is an additional (optional) header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws
+ vaultAwsIamServerID: example-vaultAwsIamServerID
+ jwt:
+ serviceAccountRef:
+ name: my-serviceaccount #Provide service account with IRSA enabled
\ No newline at end of file
diff --git a/e2e/go.mod b/e2e/go.mod
index 5f5a801af..aec41a34e 100644
--- a/e2e/go.mod
+++ b/e2e/go.mod
@@ -179,7 +179,6 @@ require (
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/robfig/cron v1.2.0 // indirect
- github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/russross/blackfriday v1.5.2 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
diff --git a/e2e/go.sum b/e2e/go.sum
index 394bb29d1..efa93383a 100644
--- a/e2e/go.sum
+++ b/e2e/go.sum
@@ -853,7 +853,6 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM=
github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
diff --git a/go.mod b/go.mod
index 25c29ad99..497a9c5ab 100644
--- a/go.mod
+++ b/go.mod
@@ -71,7 +71,9 @@ require (
github.com/alibabacloud-go/tea-utils/v2 v2.0.1
github.com/aliyun/credentials-go v1.2.7
github.com/avast/retry-go/v4 v4.3.4
+ github.com/golang-jwt/jwt/v5 v5.0.0-rc.2
github.com/hashicorp/golang-lru v0.5.4
+ github.com/hashicorp/vault/api/auth/aws v0.4.0
github.com/keeper-security/secrets-manager-go/core v1.5.0
github.com/maxbrunsfeld/counterfeiter/v6 v6.6.1
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.16
@@ -90,6 +92,9 @@ require (
github.com/clbanning/mxj/v2 v2.5.7 // indirect
github.com/go-playground/validator/v10 v10.13.0 // indirect
github.com/google/s2a-go v0.1.3 // indirect
+ github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 // indirect
+ github.com/hashicorp/go-uuid v1.0.2 // indirect
+ github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
)
diff --git a/go.sum b/go.sum
index b66b25c6d..e910c0205 100644
--- a/go.sum
+++ b/go.sum
@@ -154,6 +154,7 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/avast/retry-go/v4 v4.3.4 h1:pHLkL7jvCvP317I8Ge+Km2Yhntv3SdkJm7uekkqbKhM=
github.com/avast/retry-go/v4 v4.3.4/go.mod h1:rv+Nla6Vk3/ilU0H51VHddWHiwimzX66yZ0JT6T+UvE=
+github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.41.13/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go v1.44.254 h1:8baW4yal2xGiM/Wm5/ZU10drS8sd+BVjMjPFjJx2ooc=
github.com/aws/aws-sdk-go v1.44.254/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
@@ -255,6 +256,7 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ=
github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
@@ -272,6 +274,8 @@ github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v5 v5.0.0-rc.2 h1:hXPcSazn8wKOfSb9y2m1bdgUMlDxVDarxh3lJVbC6JE=
+github.com/golang-jwt/jwt/v5 v5.0.0-rc.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -381,6 +385,8 @@ github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUD
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
+github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 h1:W9WN8p6moV1fjKLkeqEgkAMu5rauy9QeYDAmIaPuuiA=
+github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
@@ -389,6 +395,8 @@ github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
+github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
+github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
@@ -401,6 +409,8 @@ github.com/hashicorp/vault/api v1.9.1 h1:LtY/I16+5jVGU8rufyyAkwopgq/HpUnxFBg+QLO
github.com/hashicorp/vault/api v1.9.1/go.mod h1:78kktNcQYbBGSrOjQfHjXN32OhhxXnbYl3zxpd2uPUs=
github.com/hashicorp/vault/api/auth/approle v0.4.0 h1:tjJHoUkPx8zRoFlFy86uvgg/1gpTnDPp0t0BYWTKjjw=
github.com/hashicorp/vault/api/auth/approle v0.4.0/go.mod h1:D2gEpR0aS/F/MEcSjmhUlOsuK1RMVZojsnIQAEf0EV0=
+github.com/hashicorp/vault/api/auth/aws v0.4.0 h1:2Myo+XU3X5gQTtr3S+WGXcrLUa6iO4w97VzFFaaBOm8=
+github.com/hashicorp/vault/api/auth/aws v0.4.0/go.mod h1:CGm5PAXEREuYpszyA2ERQPFBSIUD+QTqXfKvdI2Gw/Q=
github.com/hashicorp/vault/api/auth/kubernetes v0.4.0 h1:f6OIOF9012JIdqYvOeeewxhtQdJosnog2CHzh33j41s=
github.com/hashicorp/vault/api/auth/kubernetes v0.4.0/go.mod h1:tMewM2hPyFNKP1EXdWbc0dUHHoS5V/0qS04BEaxuy78=
github.com/hashicorp/vault/api/auth/ldap v0.4.0 h1:/P2HCNmcDY6s22JBXxVhr9noaFqPEQS2qwSnWIYezkc=
@@ -416,6 +426,7 @@ github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+h
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -437,6 +448,7 @@ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -541,7 +553,9 @@ github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJf
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -812,6 +826,7 @@ golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/pkg/provider/vault/iamauth/iamauth.go b/pkg/provider/vault/iamauth/iamauth.go
new file mode 100644
index 000000000..e26631834
--- /dev/null
+++ b/pkg/provider/vault/iamauth/iamauth.go
@@ -0,0 +1,309 @@
+/*
+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.
+*/
+
+// Mostly sourced from ~/external-secrets/pkg/provider/aws/auth
+package iamauth
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ "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/endpoints"
+ "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"
+ authv1 "k8s.io/api/authentication/v1"
+ v1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/client-go/kubernetes"
+ k8scorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
+ ctrl "sigs.k8s.io/controller-runtime"
+ kclient "sigs.k8s.io/controller-runtime/pkg/client"
+ ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/provider/vault/util"
+)
+
+var (
+ logger = ctrl.Log.WithName("provider").WithName("vault")
+)
+
+const (
+ roleARNAnnotation = "eks.amazonaws.com/role-arn"
+ audienceAnnotation = "eks.amazonaws.com/audience"
+ defaultTokenAudience = "sts.amazonaws.com"
+
+ STSEndpointEnv = "AWS_STS_ENDPOINT"
+ AWSWebIdentityTokenFileEnvVar = "AWS_WEB_IDENTITY_TOKEN_FILE"
+
+ 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"
+ errFetchSTSecret = "could not fetch SessionToken secret: %w"
+ errMissingSAK = "missing SecretAccessKey"
+ errMissingAKID = "missing AccessKeyID"
+)
+
+// 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 string, aud []string, 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,
+ Audiences: aud,
+ ServiceAccount: name,
+ k8sClient: clientset.CoreV1(),
+ }
+
+ return stscreds.NewWebIdentityRoleProviderWithOptions(
+ sts.New(sess), roleArn, "external-secrets-provider-vault", tokenFetcher), nil
+}
+
+// ResolveEndpoint returns a ResolverFunc with
+// customizable endpoints.
+func ResolveEndpoint() endpoints.ResolverFunc {
+ customEndpoints := make(map[string]string)
+ 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...)
+ }
+}
+
+// mostly taken from:
+// https://github.com/aws/secrets-store-csi-driver-provider-aws/blob/main/auth/auth.go#L140-L145
+
+type authTokenFetcher struct {
+ Namespace string
+ // Audience is the token aud claim
+ // which is verified by the aws oidc provider
+ // see: https://github.com/external-secrets/external-secrets/issues/1251#issuecomment-1161745849
+ Audiences []string
+ ServiceAccount string
+ k8sClient k8scorev1.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) {
+ logger.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: p.Audiences,
+ },
+ }, metav1.CreateOptions{})
+ if err != nil {
+ return nil, fmt.Errorf("error creating service account token: %w", err)
+ }
+ return []byte(tokRsp.Status.Token), nil
+}
+
+// CredsFromServiceAccount uses a Kubernetes Service Account to acquire temporary
+// credentials using aws.AssumeRoleWithWebIdentity. It will assume the role defined
+// in the ServiceAccount annotation.
+// If the ClusterSecretStore does not define a namespace it will use the namespace from the ExternalSecret (referentAuth).
+// If the ClusterSecretStore defines the namespace it will take precedence.
+func CredsFromServiceAccount(ctx context.Context, auth esv1beta1.VaultIamAuth, region string, isClusterKind bool, kube kclient.Client, namespace string, jwtProvider util.JwtProviderFactory) (*credentials.Credentials, error) {
+ name := auth.JWTAuth.ServiceAccountRef.Name
+ if isClusterKind && auth.JWTAuth.ServiceAccountRef.Namespace != nil {
+ namespace = *auth.JWTAuth.ServiceAccountRef.Namespace
+ }
+ 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)
+ }
+
+ tokenAud := sa.Annotations[audienceAnnotation]
+ if tokenAud == "" {
+ tokenAud = defaultTokenAudience
+ }
+ audiences := []string{tokenAud}
+ if len(auth.JWTAuth.ServiceAccountRef.Audiences) > 0 {
+ audiences = append(audiences, auth.JWTAuth.ServiceAccountRef.Audiences...)
+ }
+
+ jwtProv, err := jwtProvider(name, namespace, roleArn, audiences, region)
+ if err != nil {
+ return nil, err
+ }
+
+ logger.V(1).Info("using credentials via service account", "role", roleArn, "region", region)
+ return credentials.NewCredentials(jwtProv), nil
+}
+
+func CredsFromControllerServiceAccount(ctx context.Context, saname, ns, region string, kube kclient.Client, jwtProvider util.JwtProviderFactory) (*credentials.Credentials, error) {
+ name := saname
+ nmspc := ns
+
+ sa := v1.ServiceAccount{}
+ err := kube.Get(ctx, types.NamespacedName{
+ Name: name,
+ Namespace: nmspc,
+ }, &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, nmspc)
+ }
+
+ tokenAud := sa.Annotations[audienceAnnotation]
+ if tokenAud == "" {
+ tokenAud = defaultTokenAudience
+ }
+ audiences := []string{tokenAud}
+
+ jwtProv, err := jwtProvider(name, nmspc, roleArn, audiences, region)
+ if err != nil {
+ return nil, err
+ }
+
+ logger.V(1).Info("using credentials via service account", "role", roleArn, "region", region)
+ return credentials.NewCredentials(jwtProv), nil
+}
+
+// CredsFromSecretRef pulls access-key / secret-access-key from a secretRef to
+// construct a aws.Credentials object
+// The namespace of the external secret is used if the ClusterSecretStore does not specify a namespace (referentAuth)
+// If the ClusterSecretStore defines a namespace it will take precedence.
+func CredsFromSecretRef(ctx context.Context, auth esv1beta1.VaultIamAuth, isClusterKind bool, kube kclient.Client, namespace string) (*credentials.Credentials, error) {
+ ke := kclient.ObjectKey{
+ Name: auth.SecretRef.AccessKeyID.Name,
+ Namespace: namespace,
+ }
+ if isClusterKind && auth.SecretRef.AccessKeyID.Namespace != nil {
+ ke.Namespace = *auth.SecretRef.AccessKeyID.Namespace
+ }
+ akSecret := v1.Secret{}
+ err := kube.Get(ctx, ke, &akSecret)
+ if err != nil {
+ return nil, fmt.Errorf(errFetchAKIDSecret, err)
+ }
+ ke = kclient.ObjectKey{
+ Name: auth.SecretRef.SecretAccessKey.Name,
+ Namespace: namespace,
+ }
+ if isClusterKind && auth.SecretRef.SecretAccessKey.Namespace != nil {
+ ke.Namespace = *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[auth.SecretRef.SecretAccessKey.Key])
+ aks := string(akSecret.Data[auth.SecretRef.AccessKeyID.Key])
+ if sak == "" {
+ return nil, fmt.Errorf(errMissingSAK)
+ }
+ if aks == "" {
+ return nil, fmt.Errorf(errMissingAKID)
+ }
+
+ var sessionToken string
+ if auth.SecretRef.SessionToken != nil {
+ ke = kclient.ObjectKey{
+ Name: auth.SecretRef.SessionToken.Name,
+ Namespace: namespace,
+ }
+ if isClusterKind && auth.SecretRef.SessionToken.Namespace != nil {
+ ke.Namespace = *auth.SecretRef.SessionToken.Namespace
+ }
+ stSecret := v1.Secret{}
+ err = kube.Get(ctx, ke, &stSecret)
+ if err != nil {
+ return nil, fmt.Errorf(errFetchSTSecret, err)
+ }
+ sessionToken = string(stSecret.Data[auth.SecretRef.SessionToken.Key])
+ }
+
+ return credentials.NewStaticCredentials(aks, sak, sessionToken), err
+}
+
+type STSProvider func(*session.Session) stsiface.STSAPI
+
+func DefaultSTSProvider(sess *session.Session) stsiface.STSAPI {
+ return sts.New(sess)
+}
+
+// getAWSSession returns the aws session or an error.
+func GetAWSSession(config *aws.Config) (*session.Session, error) {
+ 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
+ }
+
+ return sess, nil
+}
diff --git a/pkg/provider/vault/iamauth/iamauth_test.go b/pkg/provider/vault/iamauth/iamauth_test.go
new file mode 100644
index 000000000..ecc488614
--- /dev/null
+++ b/pkg/provider/vault/iamauth/iamauth_test.go
@@ -0,0 +1,60 @@
+/*
+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 iamauth
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "github.com/external-secrets/external-secrets/pkg/provider/util/fake"
+)
+
+func TestTokenFetcher(t *testing.T) {
+ tf := &authTokenFetcher{
+ ServiceAccount: "foobar",
+ Namespace: "example",
+ k8sClient: fake.NewCreateTokenMock().WithToken("FAKETOKEN"),
+ }
+ token, err := tf.FetchToken(context.Background())
+ assert.Nil(t, err)
+ assert.Equal(t, []byte("FAKETOKEN"), token)
+}
+
+func TestResolver(t *testing.T) {
+ tbl := []struct {
+ env string
+ service string
+ url string
+ }{
+ {
+ env: STSEndpointEnv,
+ service: "sts",
+ url: "http://sts.foo",
+ },
+ }
+
+ for _, item := range tbl {
+ t.Setenv(item.env, item.url)
+ }
+
+ f := ResolveEndpoint()
+
+ for _, item := range tbl {
+ ep, err := f.EndpointFor(item.service, "")
+ assert.Nil(t, err)
+ assert.Equal(t, item.url, ep.URL)
+ }
+}
diff --git a/pkg/provider/vault/util/vault.go b/pkg/provider/vault/util/vault.go
index 47fa93b49..5ec81241d 100644
--- a/pkg/provider/vault/util/vault.go
+++ b/pkg/provider/vault/util/vault.go
@@ -17,9 +17,12 @@ package util
import (
"context"
+ "github.com/aws/aws-sdk-go/aws/credentials"
vault "github.com/hashicorp/vault/api"
)
+type JwtProviderFactory func(name, namespace, roleArn string, aud []string, region string) (credentials.Provider, error)
+
type Auth interface {
Login(ctx context.Context, authMethod vault.AuthMethod) (*vault.Secret, error)
}
diff --git a/pkg/provider/vault/vault.go b/pkg/provider/vault/vault.go
index 9cdb05963..382c539e9 100644
--- a/pkg/provider/vault/vault.go
+++ b/pkg/provider/vault/vault.go
@@ -28,9 +28,14 @@ import (
"strconv"
"strings"
+ "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/go-logr/logr"
+ "github.com/golang-jwt/jwt/v5"
vault "github.com/hashicorp/vault/api"
approle "github.com/hashicorp/vault/api/auth/approle"
+ authaws "github.com/hashicorp/vault/api/auth/aws"
authkubernetes "github.com/hashicorp/vault/api/auth/kubernetes"
authldap "github.com/hashicorp/vault/api/auth/ldap"
"github.com/spf13/pflag"
@@ -51,6 +56,7 @@ import (
"github.com/external-secrets/external-secrets/pkg/feature"
"github.com/external-secrets/external-secrets/pkg/find"
"github.com/external-secrets/external-secrets/pkg/provider/metrics"
+ vaultiamauth "github.com/external-secrets/external-secrets/pkg/provider/vault/iamauth"
"github.com/external-secrets/external-secrets/pkg/provider/vault/util"
"github.com/external-secrets/external-secrets/pkg/utils"
)
@@ -64,7 +70,9 @@ var (
)
const (
- serviceAccTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"
+ serviceAccTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"
+ defaultAWSRegion = "us-east-1"
+ defaultAWSAuthMountPath = "aws"
errVaultStore = "received invalid Vault SecretStore resource: %w"
errVaultCacheCreate = "cannot create Vault client cache: %s"
@@ -87,6 +95,11 @@ const (
errUnsupportedKvVersion = "cannot perform find operations with kv version v1"
errUnsupportedMetadataKvVersion = "cannot perform metadata fetch operations with kv version v1"
errNotFound = "secret not found"
+ errIrsaTokenEnvVarNotFoundOnPod = "expected env variable: %s not found on controller's pod"
+ errIrsaTokenFileNotFoundOnPod = "web ddentity token file not found at %s location: %w"
+ errIrsaTokenFileNotReadable = "could not read the web identity token from the file %s: %w"
+ errIrsaTokenNotValidJWT = "could not parse web identity token available at %s. not a valid jwt?: %w"
+ errPodInfoNotFoundOnToken = "could not find pod identity info on token %s: %w"
errGetKubeSA = "cannot get Kubernetes service account %q: %w"
errGetKubeSASecrets = "cannot find secrets bound to service account: %q"
@@ -352,6 +365,29 @@ func (c *Connector) ValidateStore(store esv1beta1.GenericStore) error {
return fmt.Errorf(errInvalidTokenRef, err)
}
}
+ if p.Auth.Iam != nil {
+ if p.Auth.Iam.JWTAuth != nil {
+ if p.Auth.Iam.JWTAuth.ServiceAccountRef != nil {
+ if err := utils.ValidateReferentServiceAccountSelector(store, *p.Auth.Iam.JWTAuth.ServiceAccountRef); err != nil {
+ return fmt.Errorf(errInvalidTokenRef, err)
+ }
+ }
+ }
+
+ if p.Auth.Iam.SecretRef != nil {
+ if err := utils.ValidateReferentSecretSelector(store, p.Auth.Iam.SecretRef.AccessKeyID); err != nil {
+ return fmt.Errorf(errInvalidTokenRef, err)
+ }
+ if err := utils.ValidateReferentSecretSelector(store, p.Auth.Iam.SecretRef.SecretAccessKey); err != nil {
+ return fmt.Errorf(errInvalidTokenRef, err)
+ }
+ if p.Auth.Iam.SecretRef.SessionToken != nil {
+ if err := utils.ValidateReferentSecretSelector(store, *p.Auth.Iam.SecretRef.SessionToken); err != nil {
+ return fmt.Errorf(errInvalidTokenRef, err)
+ }
+ }
+ }
+ }
return nil
}
@@ -740,6 +776,15 @@ func isReferentSpec(prov *esv1beta1.VaultProvider) bool {
if prov.Auth.Cert != nil && prov.Auth.Cert.SecretRef.Namespace == nil {
return true
}
+ if prov.Auth.Iam != nil && prov.Auth.Iam.JWTAuth != nil && prov.Auth.Iam.JWTAuth.ServiceAccountRef != nil && prov.Auth.Iam.JWTAuth.ServiceAccountRef.Namespace == nil {
+ return true
+ }
+ if prov.Auth.Iam != nil && prov.Auth.Iam.SecretRef != nil &&
+ (prov.Auth.Iam.SecretRef.AccessKeyID.Namespace == nil ||
+ prov.Auth.Iam.SecretRef.SecretAccessKey.Namespace == nil ||
+ (prov.Auth.Iam.SecretRef.SessionToken != nil && prov.Auth.Iam.SecretRef.SessionToken.Namespace == nil)) {
+ return true
+ }
return false
}
@@ -1034,6 +1079,12 @@ func (v *client) setAuth(ctx context.Context, cfg *vault.Config) error {
return err
}
+ tokenExists, err = setIamAuthToken(ctx, v, vaultiamauth.DefaultJWTProvider, vaultiamauth.DefaultSTSProvider)
+ if tokenExists {
+ v.log.V(1).Info("Retrieved new token using IAM auth")
+ return err
+ }
+
return errors.New(errAuthFormat)
}
@@ -1110,6 +1161,19 @@ func setCertAuthToken(ctx context.Context, v *client, cfg *vault.Config) (bool,
return false, nil
}
+func setIamAuthToken(ctx context.Context, v *client, jwtProvider util.JwtProviderFactory, assumeRoler vaultiamauth.STSProvider) (bool, error) {
+ iamAuth := v.store.Auth.Iam
+ isClusterKind := v.storeKind == esv1beta1.ClusterSecretStoreKind
+ if iamAuth != nil {
+ err := v.requestTokenWithIamAuth(ctx, iamAuth, isClusterKind, v.kube, v.namespace, jwtProvider, assumeRoler)
+ if err != nil {
+ return true, err
+ }
+ return true, nil
+ }
+ return false, nil
+}
+
func (v *client) secretKeyRefForServiceAccount(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) (string, error) {
serviceAccount := &corev1.ServiceAccount{}
ref := types.NamespacedName{
@@ -1407,6 +1471,133 @@ func (v *client) requestTokenWithCertAuth(ctx context.Context, certAuth *esv1bet
return nil
}
+func (v *client) requestTokenWithIamAuth(ctx context.Context, iamAuth *esv1beta1.VaultIamAuth, ick bool, k kclient.Client, n string, jwtProvider util.JwtProviderFactory, assumeRoler vaultiamauth.STSProvider) error {
+ jwtAuth := iamAuth.JWTAuth
+ secretRefAuth := iamAuth.SecretRef
+ regionAWS := defaultAWSRegion
+ awsAuthMountPath := defaultAWSAuthMountPath
+ if iamAuth.Region != "" {
+ regionAWS = iamAuth.Region
+ }
+ if iamAuth.Path != "" {
+ awsAuthMountPath = iamAuth.Path
+ }
+ var creds *credentials.Credentials
+ var err error
+ if jwtAuth != nil { // use credentials from a sa explicitly defined and referenced. Highest preference is given to this method/configuration.
+ creds, err = vaultiamauth.CredsFromServiceAccount(ctx, *iamAuth, regionAWS, ick, k, n, jwtProvider)
+ if err != nil {
+ return err
+ }
+ } else if secretRefAuth != nil { // if jwtAuth is not defined, check if secretRef is defined. Second preference.
+ logger.V(1).Info("using credentials from secretRef")
+ creds, err = vaultiamauth.CredsFromSecretRef(ctx, *iamAuth, ick, k, n)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Neither of jwtAuth or secretRefAuth defined. Last preference.
+ // Default to controller pod's identity
+ if jwtAuth == nil && secretRefAuth == nil {
+ // Checking if controller pod's service account is IRSA enabled and Web Identity token is available on pod
+ tknFile, tknFileEnvVarPresent := os.LookupEnv(vaultiamauth.AWSWebIdentityTokenFileEnvVar)
+ if !tknFileEnvVarPresent {
+ return fmt.Errorf(errIrsaTokenEnvVarNotFoundOnPod, vaultiamauth.AWSWebIdentityTokenFileEnvVar) // No Web Identity(IRSA) token found on pod
+ }
+
+ // IRSA enabled service account, let's check that the jwt token filemount and file exists
+ if _, err := os.Stat(tknFile); err != nil {
+ return fmt.Errorf(errIrsaTokenFileNotFoundOnPod, tknFile, err)
+ }
+
+ // everything looks good so far, let's fetch the jwt token from AWS_WEB_IDENTITY_TOKEN_FILE
+ jwtByte, err := os.ReadFile(tknFile)
+ if err != nil {
+ return fmt.Errorf(errIrsaTokenFileNotReadable, tknFile, err)
+ }
+
+ // let's parse the jwt token
+ parser := jwt.NewParser(jwt.WithoutClaimsValidation())
+
+ token, _, err := parser.ParseUnverified(string(jwtByte), jwt.MapClaims{})
+ if err != nil {
+ return fmt.Errorf(errIrsaTokenNotValidJWT, tknFile, err) // JWT token parser error
+ }
+
+ var ns string
+ var sa string
+
+ // let's fetch the namespace and serviceaccount from parsed jwt token
+ if claims, ok := token.Claims.(jwt.MapClaims); ok {
+ ns = claims["kubernetes.io"].(map[string]interface{})["namespace"].(string)
+ sa = claims["kubernetes.io"].(map[string]interface{})["serviceaccount"].(map[string]interface{})["name"].(string)
+ } else {
+ return fmt.Errorf(errPodInfoNotFoundOnToken, tknFile, err)
+ }
+
+ creds, err = vaultiamauth.CredsFromControllerServiceAccount(ctx, sa, ns, regionAWS, k, jwtProvider)
+ if err != nil {
+ return err
+ }
+ }
+
+ config := aws.NewConfig().WithEndpointResolver(vaultiamauth.ResolveEndpoint())
+ if creds != nil {
+ config.WithCredentials(creds)
+ }
+
+ if regionAWS != "" {
+ config.WithRegion(regionAWS)
+ }
+
+ sess, err := vaultiamauth.GetAWSSession(config)
+ if err != nil {
+ return err
+ }
+ if iamAuth.AWSIAMRole != "" {
+ stsclient := assumeRoler(sess)
+ if iamAuth.ExternalID != "" {
+ var setExternalID = func(p *stscreds.AssumeRoleProvider) {
+ p.ExternalID = aws.String(iamAuth.ExternalID)
+ }
+ sess.Config.WithCredentials(stscreds.NewCredentialsWithClient(stsclient, iamAuth.AWSIAMRole, setExternalID))
+ } else {
+ sess.Config.WithCredentials(stscreds.NewCredentialsWithClient(stsclient, iamAuth.AWSIAMRole))
+ }
+ }
+
+ getCreds, err := sess.Config.Credentials.Get()
+ if err != nil {
+ return err
+ }
+ // Set environment variables. These would be fetched by Login
+ os.Setenv("AWS_ACCESS_KEY_ID", getCreds.AccessKeyID)
+ os.Setenv("AWS_SECRET_ACCESS_KEY", getCreds.SecretAccessKey)
+ os.Setenv("AWS_SESSION_TOKEN", getCreds.SessionToken)
+
+ var awsAuthClient *authaws.AWSAuth
+
+ if iamAuth.VaultAWSIAMServerID != "" {
+ awsAuthClient, err = authaws.NewAWSAuth(authaws.WithRegion(regionAWS), authaws.WithIAMAuth(), authaws.WithRole(iamAuth.Role), authaws.WithMountPath(awsAuthMountPath), authaws.WithIAMServerIDHeader(iamAuth.VaultAWSIAMServerID))
+ if err != nil {
+ return err
+ }
+ } else {
+ awsAuthClient, err = authaws.NewAWSAuth(authaws.WithRegion(regionAWS), authaws.WithIAMAuth(), authaws.WithRole(iamAuth.Role), authaws.WithMountPath(awsAuthMountPath))
+ if err != nil {
+ return err
+ }
+ }
+
+ _, err = v.auth.Login(ctx, awsAuthClient)
+ metrics.ObserveAPICall(metrics.ProviderHCVault, metrics.CallHCVaultLogin, err)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
func init() {
var vaultTokenCacheSize int
fs := pflag.NewFlagSet("vault", pflag.ExitOnError)
diff --git a/pkg/provider/vault/vault_test.go b/pkg/provider/vault/vault_test.go
index a56c6d8d0..c0a85675d 100644
--- a/pkg/provider/vault/vault_test.go
+++ b/pkg/provider/vault/vault_test.go
@@ -161,6 +161,45 @@ func makeInvalidClusterSecretStoreWithK8sCerts() *esv1beta1.ClusterSecretStore {
}
}
+func makeValidSecretStoreWithIamAuthSecret() *esv1beta1.SecretStore {
+ return &esv1beta1.SecretStore{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "vault-store",
+ Namespace: "default",
+ },
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Vault: &esv1beta1.VaultProvider{
+ Server: "https://vault.example.com:8200",
+ Path: &secretStorePath,
+ Version: esv1beta1.VaultKVStoreV2,
+ Auth: esv1beta1.VaultAuth{
+ Iam: &esv1beta1.VaultIamAuth{
+ Path: "aws",
+ Region: "us-east-1",
+ Role: "vault-role",
+ SecretRef: &esv1beta1.VaultAwsAuthSecretRef{
+ AccessKeyID: esmeta.SecretKeySelector{
+ Name: "vault-iam-creds-secret",
+ Key: "access-key",
+ },
+ SecretAccessKey: esmeta.SecretKeySelector{
+ Name: "vault-iam-creds-secret",
+ Key: "secret-access-key",
+ },
+ SessionToken: &esmeta.SecretKeySelector{
+ Name: "vault-iam-creds-secret",
+ Key: "secret-session-token",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
type secretStoreTweakFn func(s *esv1beta1.SecretStore)
func makeSecretStore(tweaks ...secretStoreTweakFn) *esv1beta1.SecretStore {
@@ -335,6 +374,29 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
err: fmt.Errorf(errVaultCert, errors.New(`cannot find secret data for key: "cert"`)),
},
},
+ "SuccessfulVaultStoreWithIamAuthSecret": {
+ reason: "Should return a Vault provider successfully",
+ args: args{
+ store: makeValidSecretStoreWithIamAuthSecret(),
+ ns: "default",
+ kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "vault-iam-creds-secret",
+ Namespace: "default",
+ },
+ Data: map[string][]byte{
+ "access-key": []byte("TESTING"),
+ "secret-access-key": []byte("ABCDEF"),
+ "secret-session-token": []byte("c2VjcmV0LXNlc3Npb24tdG9rZW4K"),
+ },
+ }).Build(),
+ corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
+ newClientFunc: fake.ClientWithLoginMock,
+ },
+ want: want{
+ err: nil,
+ },
+ },
"SuccessfulVaultStoreWithK8sCertConfigMap": {
reason: "Should return a Vault prodvider with the cert from k8s",
args: args{