1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

feat(vault): add option for JWT backend to authenticate with Kubernetes service account token (#768)

This commit is contained in:
Alfred Krohmer 2022-04-04 21:20:58 +02:00 committed by GitHub
parent 6f856db55a
commit d7022b1bef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 583 additions and 46 deletions

View file

@ -203,8 +203,29 @@ type VaultLdapAuth struct {
SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"`
}
// VaultKubernetesServiceAccountTokenAuth authenticates with Vault using a temporary
// Kubernetes service account token retrieved by the `TokenRequest` API.
type VaultKubernetesServiceAccountTokenAuth struct {
// Service account field containing the name of a kubernetes ServiceAccount.
ServiceAccountRef esmeta.ServiceAccountSelector `json:"serviceAccountRef"`
// Optional audiences field that will be used to request a temporary Kubernetes service
// account token for the service account referenced by `serviceAccountRef`.
// Defaults to a single audience `vault` it not specified.
// +optional
Audiences *[]string `json:"audiences,omitempty"`
// Optional expiration time in seconds that will be used to request a temporary
// Kubernetes service account token for the service account referenced by
// `serviceAccountRef`.
// Defaults to 10 minutes.
// +optional
ExpirationSeconds *int64 `json:"expirationSeconds,omitempty"`
}
// VaultJwtAuth authenticates with Vault using the JWT/OIDC authentication
// method, with the role name and token stored in a Kubernetes Secret resource.
// method, with the role name and a token stored in a Kubernetes Secret resource or
// a Kubernetes service account token retrieved via `TokenRequest`.
type VaultJwtAuth struct {
// Path where the JWT authentication backend is mounted
// in Vault, e.g: "jwt"
@ -216,9 +237,15 @@ type VaultJwtAuth struct {
// +optional
Role string `json:"role"`
// SecretRef to a key in a Secret resource containing JWT token to
// authenticate with Vault using the JWT/OIDC authentication method
SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"`
// Optional SecretRef that refers to a key in a Secret resource containing JWT token to
// authenticate with Vault using the JWT/OIDC authentication method.
// +optional
SecretRef *esmeta.SecretKeySelector `json:"secretRef,omitempty"`
// Optional ServiceAccountToken specifies the Kubernetes service account for which to request
// a token for with the `TokenRequest` API.
// +optional
KubernetesServiceAccountToken *VaultKubernetesServiceAccountTokenAuth `json:"kubernetesServiceAccountToken,omitempty"`
}
// VaultJwtAuth authenticates with Vault using the JWT/OIDC authentication

View file

@ -1379,7 +1379,16 @@ func (in *VaultCertAuth) DeepCopy() *VaultCertAuth {
// 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
in.SecretRef.DeepCopyInto(&out.SecretRef)
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(metav1.SecretKeySelector)
(*in).DeepCopyInto(*out)
}
if in.KubernetesServiceAccountToken != nil {
in, out := &in.KubernetesServiceAccountToken, &out.KubernetesServiceAccountToken
*out = new(VaultKubernetesServiceAccountTokenAuth)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultJwtAuth.
@ -1417,6 +1426,36 @@ func (in *VaultKubernetesAuth) DeepCopy() *VaultKubernetesAuth {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultKubernetesServiceAccountTokenAuth) DeepCopyInto(out *VaultKubernetesServiceAccountTokenAuth) {
*out = *in
in.ServiceAccountRef.DeepCopyInto(&out.ServiceAccountRef)
if in.Audiences != nil {
in, out := &in.Audiences, &out.Audiences
*out = new([]string)
if **in != nil {
in, out := *in, *out
*out = make([]string, len(*in))
copy(*out, *in)
}
}
if in.ExpirationSeconds != nil {
in, out := &in.ExpirationSeconds, &out.ExpirationSeconds
*out = new(int64)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultKubernetesServiceAccountTokenAuth.
func (in *VaultKubernetesServiceAccountTokenAuth) DeepCopy() *VaultKubernetesServiceAccountTokenAuth {
if in == nil {
return nil
}
out := new(VaultKubernetesServiceAccountTokenAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultLdapAuth) DeepCopyInto(out *VaultLdapAuth) {
*out = *in

View file

@ -203,8 +203,29 @@ type VaultLdapAuth struct {
SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"`
}
// VaultKubernetesServiceAccountTokenAuth authenticates with Vault using a temporary
// Kubernetes service account token retrieved by the `TokenRequest` API.
type VaultKubernetesServiceAccountTokenAuth struct {
// Service account field containing the name of a kubernetes ServiceAccount.
ServiceAccountRef esmeta.ServiceAccountSelector `json:"serviceAccountRef"`
// Optional audiences field that will be used to request a temporary Kubernetes service
// account token for the service account referenced by `serviceAccountRef`.
// Defaults to a single audience `vault` it not specified.
// +optional
Audiences *[]string `json:"audiences,omitempty"`
// Optional expiration time in seconds that will be used to request a temporary
// Kubernetes service account token for the service account referenced by
// `serviceAccountRef`.
// Defaults to 10 minutes.
// +optional
ExpirationSeconds *int64 `json:"expirationSeconds,omitempty"`
}
// VaultJwtAuth authenticates with Vault using the JWT/OIDC authentication
// method, with the role name and token stored in a Kubernetes Secret resource.
// method, with the role name and a token stored in a Kubernetes Secret resource or
// a Kubernetes service account token retrieved via `TokenRequest`.
type VaultJwtAuth struct {
// Path where the JWT authentication backend is mounted
// in Vault, e.g: "jwt"
@ -216,9 +237,15 @@ type VaultJwtAuth struct {
// +optional
Role string `json:"role"`
// SecretRef to a key in a Secret resource containing JWT token to
// authenticate with Vault using the JWT/OIDC authentication method
SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"`
// Optional SecretRef that refers to a key in a Secret resource containing JWT token to
// authenticate with Vault using the JWT/OIDC authentication method.
// +optional
SecretRef *esmeta.SecretKeySelector `json:"secretRef,omitempty"`
// Optional ServiceAccountToken specifies the Kubernetes service account for which to request
// a token for with the `TokenRequest` API.
// +optional
KubernetesServiceAccountToken *VaultKubernetesServiceAccountTokenAuth `json:"kubernetesServiceAccountToken,omitempty"`
}
// VaultJwtAuth authenticates with Vault using the JWT/OIDC authentication

View file

@ -1609,7 +1609,16 @@ func (in *VaultCertAuth) DeepCopy() *VaultCertAuth {
// 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
in.SecretRef.DeepCopyInto(&out.SecretRef)
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(metav1.SecretKeySelector)
(*in).DeepCopyInto(*out)
}
if in.KubernetesServiceAccountToken != nil {
in, out := &in.KubernetesServiceAccountToken, &out.KubernetesServiceAccountToken
*out = new(VaultKubernetesServiceAccountTokenAuth)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultJwtAuth.
@ -1647,6 +1656,36 @@ func (in *VaultKubernetesAuth) DeepCopy() *VaultKubernetesAuth {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultKubernetesServiceAccountTokenAuth) DeepCopyInto(out *VaultKubernetesServiceAccountTokenAuth) {
*out = *in
in.ServiceAccountRef.DeepCopyInto(&out.ServiceAccountRef)
if in.Audiences != nil {
in, out := &in.Audiences, &out.Audiences
*out = new([]string)
if **in != nil {
in, out := *in, *out
*out = make([]string, len(*in))
copy(*out, *in)
}
}
if in.ExpirationSeconds != nil {
in, out := &in.ExpirationSeconds, &out.ExpirationSeconds
*out = new(int64)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultKubernetesServiceAccountTokenAuth.
func (in *VaultKubernetesServiceAccountTokenAuth) DeepCopy() *VaultKubernetesServiceAccountTokenAuth {
if in == nil {
return nil
}
out := new(VaultKubernetesServiceAccountTokenAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultLdapAuth) DeepCopyInto(out *VaultLdapAuth) {
*out = *in

View file

@ -908,6 +908,48 @@ spec:
description: Jwt authenticates with Vault by passing role
and JWT token using the JWT/OIDC authentication method
properties:
kubernetesServiceAccountToken:
description: Optional ServiceAccountToken specifies
the Kubernetes service account for which to request
a token for with the `TokenRequest` API.
properties:
audiences:
description: Optional audiences field that will
be used to request a temporary Kubernetes service
account token for the service account referenced
by `serviceAccountRef`. Defaults to a single
audience `vault` it not specified.
items:
type: string
type: array
expirationSeconds:
description: Optional expiration time in seconds
that will be used to request a temporary Kubernetes
service account token for the service account
referenced by `serviceAccountRef`. Defaults
to 10 minutes.
format: int64
type: integer
serviceAccountRef:
description: Service account field containing
the name of a kubernetes ServiceAccount.
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
required:
- serviceAccountRef
type: object
path:
default: jwt
description: 'Path where the JWT authentication backend
@ -918,9 +960,9 @@ spec:
the JWT/OIDC Vault authentication method
type: string
secretRef:
description: SecretRef to a key in a Secret resource
containing JWT token to authenticate with Vault
using the JWT/OIDC authentication method
description: Optional SecretRef that refers to a key
in a Secret resource containing JWT token to authenticate
with Vault using the JWT/OIDC authentication method.
properties:
key:
description: The key of the entry in the Secret
@ -2227,6 +2269,48 @@ spec:
description: Jwt authenticates with Vault by passing role
and JWT token using the JWT/OIDC authentication method
properties:
kubernetesServiceAccountToken:
description: Optional ServiceAccountToken specifies
the Kubernetes service account for which to request
a token for with the `TokenRequest` API.
properties:
audiences:
description: Optional audiences field that will
be used to request a temporary Kubernetes service
account token for the service account referenced
by `serviceAccountRef`. Defaults to a single
audience `vault` it not specified.
items:
type: string
type: array
expirationSeconds:
description: Optional expiration time in seconds
that will be used to request a temporary Kubernetes
service account token for the service account
referenced by `serviceAccountRef`. Defaults
to 10 minutes.
format: int64
type: integer
serviceAccountRef:
description: Service account field containing
the name of a kubernetes ServiceAccount.
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
required:
- serviceAccountRef
type: object
path:
default: jwt
description: 'Path where the JWT authentication backend
@ -2237,9 +2321,9 @@ spec:
the JWT/OIDC Vault authentication method
type: string
secretRef:
description: SecretRef to a key in a Secret resource
containing JWT token to authenticate with Vault
using the JWT/OIDC authentication method
description: Optional SecretRef that refers to a key
in a Secret resource containing JWT token to authenticate
with Vault using the JWT/OIDC authentication method.
properties:
key:
description: The key of the entry in the Secret

View file

@ -908,6 +908,48 @@ spec:
description: Jwt authenticates with Vault by passing role
and JWT token using the JWT/OIDC authentication method
properties:
kubernetesServiceAccountToken:
description: Optional ServiceAccountToken specifies
the Kubernetes service account for which to request
a token for with the `TokenRequest` API.
properties:
audiences:
description: Optional audiences field that will
be used to request a temporary Kubernetes service
account token for the service account referenced
by `serviceAccountRef`. Defaults to a single
audience `vault` it not specified.
items:
type: string
type: array
expirationSeconds:
description: Optional expiration time in seconds
that will be used to request a temporary Kubernetes
service account token for the service account
referenced by `serviceAccountRef`. Defaults
to 10 minutes.
format: int64
type: integer
serviceAccountRef:
description: Service account field containing
the name of a kubernetes ServiceAccount.
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
required:
- serviceAccountRef
type: object
path:
default: jwt
description: 'Path where the JWT authentication backend
@ -918,9 +960,9 @@ spec:
the JWT/OIDC Vault authentication method
type: string
secretRef:
description: SecretRef to a key in a Secret resource
containing JWT token to authenticate with Vault
using the JWT/OIDC authentication method
description: Optional SecretRef that refers to a key
in a Secret resource containing JWT token to authenticate
with Vault using the JWT/OIDC authentication method.
properties:
key:
description: The key of the entry in the Secret
@ -2230,6 +2272,48 @@ spec:
description: Jwt authenticates with Vault by passing role
and JWT token using the JWT/OIDC authentication method
properties:
kubernetesServiceAccountToken:
description: Optional ServiceAccountToken specifies
the Kubernetes service account for which to request
a token for with the `TokenRequest` API.
properties:
audiences:
description: Optional audiences field that will
be used to request a temporary Kubernetes service
account token for the service account referenced
by `serviceAccountRef`. Defaults to a single
audience `vault` it not specified.
items:
type: string
type: array
expirationSeconds:
description: Optional expiration time in seconds
that will be used to request a temporary Kubernetes
service account token for the service account
referenced by `serviceAccountRef`. Defaults
to 10 minutes.
format: int64
type: integer
serviceAccountRef:
description: Service account field containing
the name of a kubernetes ServiceAccount.
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
required:
- serviceAccountRef
type: object
path:
default: jwt
description: 'Path where the JWT authentication backend
@ -2240,9 +2324,9 @@ spec:
the JWT/OIDC Vault authentication method
type: string
secretRef:
description: SecretRef to a key in a Secret resource
containing JWT token to authenticate with Vault
using the JWT/OIDC authentication method
description: Optional SecretRef that refers to a key
in a Secret resource containing JWT token to authenticate
with Vault using the JWT/OIDC authentication method.
properties:
key:
description: The key of the entry in the Secret

View file

@ -999,6 +999,33 @@ spec:
jwt:
description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method
properties:
kubernetesServiceAccountToken:
description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API.
properties:
audiences:
description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified.
items:
type: string
type: array
expirationSeconds:
description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes.
format: int64
type: integer
serviceAccountRef:
description: Service account field containing the name of a kubernetes ServiceAccount.
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
required:
- serviceAccountRef
type: object
path:
default: jwt
description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"'
@ -1007,7 +1034,7 @@ spec:
description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method
type: string
secretRef:
description: SecretRef to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method
description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method.
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.
@ -1972,6 +1999,33 @@ spec:
jwt:
description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method
properties:
kubernetesServiceAccountToken:
description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API.
properties:
audiences:
description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified.
items:
type: string
type: array
expirationSeconds:
description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes.
format: int64
type: integer
serviceAccountRef:
description: Service account field containing the name of a kubernetes ServiceAccount.
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
required:
- serviceAccountRef
type: object
path:
default: jwt
description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"'
@ -1980,7 +2034,7 @@ spec:
description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method
type: string
secretRef:
description: SecretRef to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method
description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method.
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.
@ -3487,6 +3541,33 @@ spec:
jwt:
description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method
properties:
kubernetesServiceAccountToken:
description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API.
properties:
audiences:
description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified.
items:
type: string
type: array
expirationSeconds:
description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes.
format: int64
type: integer
serviceAccountRef:
description: Service account field containing the name of a kubernetes ServiceAccount.
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
required:
- serviceAccountRef
type: object
path:
default: jwt
description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"'
@ -3495,7 +3576,7 @@ spec:
description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method
type: string
secretRef:
description: SecretRef to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method
description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method.
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.
@ -4463,6 +4544,33 @@ spec:
jwt:
description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method
properties:
kubernetesServiceAccountToken:
description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API.
properties:
audiences:
description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified.
items:
type: string
type: array
expirationSeconds:
description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes.
format: int64
type: integer
serviceAccountRef:
description: Service account field containing the name of a kubernetes ServiceAccount.
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
required:
- serviceAccountRef
type: object
path:
default: jwt
description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"'
@ -4471,7 +4579,7 @@ spec:
description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method
type: string
secretRef:
description: SecretRef to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method
description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method.
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.

View file

@ -295,9 +295,9 @@ in a `Kind=Secret` referenced by the `secretRef`.
#### JWT/OIDC authentication
[JWT/OIDC](https://www.vaultproject.io/docs/auth/jwt) uses a
[JWT/OIDC](https://www.vaultproject.io/docs/auth/jwt) uses either a
[JWT](https://jwt.io/) token stored in a `Kind=Secret` and referenced by the
`secretRef`. Optionally a `role` field can be defined in a `Kind=SecretStore`
`secretRef` or a temporary Kubernetes service account token retrieved via the `TokenRequest` API. Optionally a `role` field can be defined in a `Kind=SecretStore`
or `Kind=ClusterSecretStore` resource.
```yaml

View file

@ -17,6 +17,18 @@ spec:
path: "jwt"
# JWT role configured in a Vault server, optional.
role: "vault-jwt-role"
# Retrieve JWT token from a Kubernetes secret
secretRef:
name: "my-secret"
key: "jwt-token"
# ... or retrieve a Kubernetes service account token via the `TokenRequest` API
kubernetesServiceAccountToken:
serviceAccountRef:
name: "my-sa"
# `audiences` defaults to `["vault"]` it not supplied
audiences:
- vault
# `expirationSeconds` defaults to 10 minutes if not supplied
expirationSeconds: 600

View file

@ -58,6 +58,7 @@ type Vault struct {
JWTToken string
JWTRole string
JWTPath string
JWTK8sPath string
KubernetesAuthPath string
KubernetesAuthRole string
@ -162,6 +163,7 @@ func (l *Vault) initVault() error {
l.JWTPubkey = jwtPubkey
l.JWTToken = jwtToken
l.JWTPath = "myjwt" // see configure-vault.sh
l.JWTK8sPath = "myjwtk8s" // see configure-vault.sh
l.JWTRole = "external-secrets-operator" // see configure-vault.sh
l.KubernetesAuthPath = "mykubernetes" // see configure-vault.sh
l.KubernetesAuthRole = "external-secrets-operator" // see configure-vault.sh

View file

@ -69,6 +69,21 @@ vault write auth/myjwt/role/external-secrets-operator \
policies=external-secrets-operator \
ttl=1h
vault auth enable -path=myjwtk8s jwt
vault write auth/myjwtk8s/config \
oidc_discovery_url=https://kubernetes.default.svc.cluster.local \
oidc_discovery_ca_pem=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
bound_issuer="https://kubernetes.default.svc.cluster.local" \
default_role="external-secrets-operator"
vault write auth/myjwtk8s/role/external-secrets-operator \
role_type="jwt" \
bound_audiences="vault.client" \
user_claim="sub" \
policies=external-secrets-operator \
ttl=1h
# ------------------
# Kubernetes AUTH
# https://www.vaultproject.io/docs/auth/kubernetes

View file

@ -1,14 +1,5 @@
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
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"
nodes:
- role: control-plane
- role: worker

View file

@ -34,6 +34,11 @@ kubectl create clusterrolebinding permissive-binding \
--user=kubelet \
--serviceaccount=default:external-secrets-e2e || true
echo -e "Granting anonymous access to service account issuer discovery"
kubectl create clusterrolebinding service-account-issuer-discovery-binding \
--clusterrole=system:service-account-issuer-discovery \
--group=system:unauthenticated || true
echo -e "Waiting service account..."; \
until kubectl get secret | grep -q -e ^external-secrets-e2e-token; do \
echo -e "waiting for api token"; \

View file

@ -45,6 +45,7 @@ const (
appRoleAuthProviderName = "app-role-provider"
kvv1ProviderName = "kv-v1-provider"
jwtProviderName = "jwt-provider"
jwtK8sProviderName = "jwt-k8s-provider"
kubernetesProviderName = "kubernetes-provider"
)
@ -95,6 +96,7 @@ func (s *vaultProvider) BeforeEach() {
s.CreateAppRoleStore(v, ns)
s.CreateV1Store(v, ns)
s.CreateJWTStore(v, ns)
s.CreateJWTK8sStore(v, ns)
s.CreateKubernetesAuthStore(v, ns)
}
@ -249,7 +251,7 @@ func (s vaultProvider) CreateJWTStore(v *addon.Vault, ns string) {
Jwt: &esv1beta1.VaultJwtAuth{
Path: v.JWTPath,
Role: v.JWTRole,
SecretRef: esmeta.SecretKeySelector{
SecretRef: &esmeta.SecretKeySelector{
Name: "jwt-provider",
Key: "jwt",
},
@ -259,6 +261,26 @@ func (s vaultProvider) CreateJWTStore(v *addon.Vault, ns string) {
Expect(err).ToNot(HaveOccurred())
}
func (s vaultProvider) CreateJWTK8sStore(v *addon.Vault, ns string) {
secretStore := makeStore(jwtK8sProviderName, ns, v)
secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{
Jwt: &esv1beta1.VaultJwtAuth{
Path: v.JWTK8sPath,
Role: v.JWTRole,
KubernetesServiceAccountToken: &esv1beta1.VaultKubernetesServiceAccountTokenAuth{
ServiceAccountRef: esmeta.ServiceAccountSelector{
Name: "default",
},
Audiences: &[]string{
"vault.client",
},
},
},
}
err := s.framework.CRClient.Create(context.Background(), secretStore)
Expect(err).ToNot(HaveOccurred())
}
func (s vaultProvider) CreateKubernetesAuthStore(v *addon.Vault, ns string) {
secretStore := makeStore(kubernetesProviderName, ns, v)
secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{

View file

@ -30,6 +30,7 @@ const (
withApprole = "with approle auth"
withV1 = "with v1 provider"
withJWT = "with jwt provider"
withJWTK8s = "with jwt k8s provider"
withK8s = "with kubernetes provider"
)
@ -74,6 +75,12 @@ var _ = Describe("[vault]", Label("vault"), func() {
framework.Compose(withJWT, f, common.JSONDataWithTemplate, useJWTProvider),
framework.Compose(withJWT, f, common.DataPropertyDockerconfigJSON, useJWTProvider),
framework.Compose(withJWT, f, common.JSONDataWithoutTargetName, useJWTProvider),
// use jwt k8s provider
framework.Compose(withJWTK8s, f, common.JSONDataFromSync, useJWTK8sProvider),
framework.Compose(withJWTK8s, f, common.JSONDataWithProperty, useJWTK8sProvider),
framework.Compose(withJWTK8s, f, common.JSONDataWithTemplate, useJWTK8sProvider),
framework.Compose(withJWTK8s, f, common.DataPropertyDockerconfigJSON, useJWTK8sProvider),
framework.Compose(withJWTK8s, f, common.JSONDataWithoutTargetName, useJWTK8sProvider),
// use kubernetes provider
framework.Compose(withK8s, f, common.FindByName, useKubernetesProvider),
framework.Compose(withK8s, f, common.JSONDataFromSync, useKubernetesProvider),
@ -109,6 +116,10 @@ func useJWTProvider(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Name = jwtProviderName
}
func useJWTK8sProvider(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Name = jwtK8sProviderName
}
func useKubernetesProvider(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Name = kubernetesProviderName
}

View file

@ -29,10 +29,15 @@ import (
"github.com/go-logr/logr"
vault "github.com/hashicorp/vault/api"
"github.com/tidwall/gjson"
authenticationv1 "k8s.io/api/authentication/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
typedcorev1 "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"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
@ -64,10 +69,13 @@ const (
errVaultRequest = "error from Vault request: %w"
errVaultResponse = "cannot parse Vault response: %w"
errServiceAccount = "cannot read Kubernetes service account token from file system: %w"
errJwtNoTokenSource = "neither `secretRef` nor `kubernetesServiceAccountToken` was supplied as token source for jwt authentication"
errUnsupportedKvVersion = "cannot perform find operations with kv version v1"
errGetKubeSA = "cannot get Kubernetes service account %q: %w"
errGetKubeSASecrets = "cannot find secrets bound to service account: %q"
errGetKubeSANoToken = "cannot find token in secrets bound to service account: %q"
errGetKubeSATokenRequest = "cannot request Kubernetes service account token for service account %q: %w"
errGetKubeSecret = "cannot get Kubernetes secret %q: %w"
errSecretKeyFmt = "cannot find secret data for key: %q"
@ -106,6 +114,7 @@ type Client interface {
type client struct {
kube kclient.Client
corev1 typedcorev1.CoreV1Interface
store *esv1beta1.VaultProvider
log logr.Logger
client Client
@ -130,6 +139,21 @@ type connector struct {
}
func (c *connector) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
// 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
// (for Kubernetes service account token auth)
restCfg, err := ctrlcfg.GetConfig()
if err != nil {
return nil, err
}
clientset, err := kubernetes.NewForConfig(restCfg)
if err != nil {
return nil, err
}
return c.newClient(ctx, store, kube, clientset.CoreV1(), namespace)
}
func (c *connector) newClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, corev1 typedcorev1.CoreV1Interface, namespace string) (esv1beta1.SecretsClient, error) {
storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Vault == nil {
return nil, errors.New(errVaultStore)
@ -138,6 +162,7 @@ func (c *connector) NewClient(ctx context.Context, store esv1beta1.GenericStore,
vStore := &client{
kube: kube,
corev1: corev1,
store: vaultSpec,
log: ctrl.Log.WithName("provider").WithName("vault"),
namespace: namespace,
@ -200,9 +225,17 @@ func (c *connector) ValidateStore(store esv1beta1.GenericStore) error {
}
}
if p.Auth.Jwt != nil {
if err := utils.ValidateSecretSelector(store, p.Auth.Jwt.SecretRef); err != nil {
if p.Auth.Jwt.SecretRef != nil {
if err := utils.ValidateSecretSelector(store, *p.Auth.Jwt.SecretRef); err != nil {
return fmt.Errorf(errInvalidJwtSec, err)
}
} else if p.Auth.Jwt.KubernetesServiceAccountToken != nil {
if err := utils.ValidateServiceAccountSelector(store, p.Auth.Jwt.KubernetesServiceAccountToken.ServiceAccountRef); err != nil {
return fmt.Errorf(errInvalidJwtSec, err)
}
} else {
return fmt.Errorf(errJwtNoTokenSource)
}
}
if p.Auth.Kubernetes != nil {
if p.Auth.Kubernetes.ServiceAccountRef != nil {
@ -822,6 +855,27 @@ func (v *client) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySe
return valueStr, nil
}
func (v *client) serviceAccountToken(ctx context.Context, serviceAccountRef esmeta.ServiceAccountSelector, audiences []string, expirationSeconds int64) (string, error) {
tokenRequest := &authenticationv1.TokenRequest{
ObjectMeta: metav1.ObjectMeta{
Namespace: v.namespace,
},
Spec: authenticationv1.TokenRequestSpec{
Audiences: audiences,
ExpirationSeconds: &expirationSeconds,
},
}
if (v.storeKind == esv1beta1.ClusterSecretStoreKind) &&
(serviceAccountRef.Namespace != nil) {
tokenRequest.Namespace = *serviceAccountRef.Namespace
}
tokenResponse, err := v.corev1.ServiceAccounts(tokenRequest.Namespace).CreateToken(ctx, serviceAccountRef.Name, tokenRequest, metav1.CreateOptions{})
if err != nil {
return "", fmt.Errorf(errGetKubeSATokenRequest, serviceAccountRef.Name, err)
}
return tokenResponse.Status.Token, nil
}
// checkToken does a lookup and checks if the provided token exists.
func checkToken(ctx context.Context, vStore *client) error {
// https://www.vaultproject.io/api-docs/auth/token#lookup-a-token-self
@ -995,7 +1049,24 @@ func (v *client) requestTokenWithLdapAuth(ctx context.Context, client Client, ld
func (v *client) requestTokenWithJwtAuth(ctx context.Context, client Client, jwtAuth *esv1beta1.VaultJwtAuth) (string, error) {
role := strings.TrimSpace(jwtAuth.Role)
jwt, err := v.secretKeyRef(ctx, &jwtAuth.SecretRef)
var jwt string
var err error
if jwtAuth.SecretRef != nil {
jwt, err = v.secretKeyRef(ctx, jwtAuth.SecretRef)
} else if k8sServiceAccountToken := jwtAuth.KubernetesServiceAccountToken; k8sServiceAccountToken != nil {
audiences := k8sServiceAccountToken.Audiences
if audiences == nil {
audiences = &[]string{"vault"}
}
expirationSeconds := k8sServiceAccountToken.ExpirationSeconds
if expirationSeconds == nil {
tmp := int64(600)
expirationSeconds = &tmp
}
jwt, err = v.serviceAccountToken(ctx, k8sServiceAccountToken.ServiceAccountRef, *audiences, *expirationSeconds)
} else {
err = fmt.Errorf(errJwtNoTokenSource)
}
if err != nil {
return "", err
}

View file

@ -564,7 +564,7 @@ func vaultTest(t *testing.T, name string, tc testCase) {
if tc.args.newClientFunc == nil {
conn.newVaultClient = newVaultClient
}
_, err := conn.NewClient(context.Background(), tc.args.store, tc.args.kube, tc.args.ns)
_, err := conn.newClient(context.Background(), tc.args.store, tc.args.kube, nil, tc.args.ns)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nvault.New(...): -want error, +got error:\n%s", tc.reason, diff)
}
@ -1361,7 +1361,7 @@ func TestValidateStore(t *testing.T) {
args: args{
auth: esv1beta1.VaultAuth{
Jwt: &esv1beta1.VaultJwtAuth{
SecretRef: esmeta.SecretKeySelector{
SecretRef: &esmeta.SecretKeySelector{
Namespace: pointer.StringPtr("invalid"),
},
},