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"` 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 // 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 { type VaultJwtAuth struct {
// Path where the JWT authentication backend is mounted // Path where the JWT authentication backend is mounted
// in Vault, e.g: "jwt" // in Vault, e.g: "jwt"
@ -216,9 +237,15 @@ type VaultJwtAuth struct {
// +optional // +optional
Role string `json:"role"` Role string `json:"role"`
// SecretRef to a key in a Secret resource containing JWT token to // Optional SecretRef that refers to a key in a Secret resource containing JWT token to
// authenticate with Vault using the JWT/OIDC authentication method // authenticate with Vault using the JWT/OIDC authentication method.
SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"` // +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 // 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. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultJwtAuth) DeepCopyInto(out *VaultJwtAuth) { func (in *VaultJwtAuth) DeepCopyInto(out *VaultJwtAuth) {
*out = *in *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. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultJwtAuth.
@ -1417,6 +1426,36 @@ func (in *VaultKubernetesAuth) DeepCopy() *VaultKubernetesAuth {
return out 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. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultLdapAuth) DeepCopyInto(out *VaultLdapAuth) { func (in *VaultLdapAuth) DeepCopyInto(out *VaultLdapAuth) {
*out = *in *out = *in

View file

@ -203,8 +203,29 @@ type VaultLdapAuth struct {
SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"` 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 // 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 { type VaultJwtAuth struct {
// Path where the JWT authentication backend is mounted // Path where the JWT authentication backend is mounted
// in Vault, e.g: "jwt" // in Vault, e.g: "jwt"
@ -216,9 +237,15 @@ type VaultJwtAuth struct {
// +optional // +optional
Role string `json:"role"` Role string `json:"role"`
// SecretRef to a key in a Secret resource containing JWT token to // Optional SecretRef that refers to a key in a Secret resource containing JWT token to
// authenticate with Vault using the JWT/OIDC authentication method // authenticate with Vault using the JWT/OIDC authentication method.
SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"` // +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 // 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. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultJwtAuth) DeepCopyInto(out *VaultJwtAuth) { func (in *VaultJwtAuth) DeepCopyInto(out *VaultJwtAuth) {
*out = *in *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. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultJwtAuth.
@ -1647,6 +1656,36 @@ func (in *VaultKubernetesAuth) DeepCopy() *VaultKubernetesAuth {
return out 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. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultLdapAuth) DeepCopyInto(out *VaultLdapAuth) { func (in *VaultLdapAuth) DeepCopyInto(out *VaultLdapAuth) {
*out = *in *out = *in

View file

@ -908,6 +908,48 @@ spec:
description: Jwt authenticates with Vault by passing role description: Jwt authenticates with Vault by passing role
and JWT token using the JWT/OIDC authentication method and JWT token using the JWT/OIDC authentication method
properties: 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: path:
default: jwt default: jwt
description: 'Path where the JWT authentication backend description: 'Path where the JWT authentication backend
@ -918,9 +960,9 @@ spec:
the JWT/OIDC Vault authentication method the JWT/OIDC Vault authentication method
type: string type: string
secretRef: secretRef:
description: SecretRef to a key in a Secret resource description: Optional SecretRef that refers to a key
containing JWT token to authenticate with Vault in a Secret resource containing JWT token to authenticate
using the JWT/OIDC authentication method with Vault using the JWT/OIDC authentication method.
properties: properties:
key: key:
description: The key of the entry in the Secret description: The key of the entry in the Secret
@ -2227,6 +2269,48 @@ spec:
description: Jwt authenticates with Vault by passing role description: Jwt authenticates with Vault by passing role
and JWT token using the JWT/OIDC authentication method and JWT token using the JWT/OIDC authentication method
properties: 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: path:
default: jwt default: jwt
description: 'Path where the JWT authentication backend description: 'Path where the JWT authentication backend
@ -2237,9 +2321,9 @@ spec:
the JWT/OIDC Vault authentication method the JWT/OIDC Vault authentication method
type: string type: string
secretRef: secretRef:
description: SecretRef to a key in a Secret resource description: Optional SecretRef that refers to a key
containing JWT token to authenticate with Vault in a Secret resource containing JWT token to authenticate
using the JWT/OIDC authentication method with Vault using the JWT/OIDC authentication method.
properties: properties:
key: key:
description: The key of the entry in the Secret description: The key of the entry in the Secret

View file

@ -908,6 +908,48 @@ spec:
description: Jwt authenticates with Vault by passing role description: Jwt authenticates with Vault by passing role
and JWT token using the JWT/OIDC authentication method and JWT token using the JWT/OIDC authentication method
properties: 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: path:
default: jwt default: jwt
description: 'Path where the JWT authentication backend description: 'Path where the JWT authentication backend
@ -918,9 +960,9 @@ spec:
the JWT/OIDC Vault authentication method the JWT/OIDC Vault authentication method
type: string type: string
secretRef: secretRef:
description: SecretRef to a key in a Secret resource description: Optional SecretRef that refers to a key
containing JWT token to authenticate with Vault in a Secret resource containing JWT token to authenticate
using the JWT/OIDC authentication method with Vault using the JWT/OIDC authentication method.
properties: properties:
key: key:
description: The key of the entry in the Secret description: The key of the entry in the Secret
@ -2230,6 +2272,48 @@ spec:
description: Jwt authenticates with Vault by passing role description: Jwt authenticates with Vault by passing role
and JWT token using the JWT/OIDC authentication method and JWT token using the JWT/OIDC authentication method
properties: 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: path:
default: jwt default: jwt
description: 'Path where the JWT authentication backend description: 'Path where the JWT authentication backend
@ -2240,9 +2324,9 @@ spec:
the JWT/OIDC Vault authentication method the JWT/OIDC Vault authentication method
type: string type: string
secretRef: secretRef:
description: SecretRef to a key in a Secret resource description: Optional SecretRef that refers to a key
containing JWT token to authenticate with Vault in a Secret resource containing JWT token to authenticate
using the JWT/OIDC authentication method with Vault using the JWT/OIDC authentication method.
properties: properties:
key: key:
description: The key of the entry in the Secret description: The key of the entry in the Secret

View file

@ -999,6 +999,33 @@ spec:
jwt: jwt:
description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method
properties: 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: path:
default: jwt default: jwt
description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "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 description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method
type: string type: string
secretRef: 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: properties:
key: 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. 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: jwt:
description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method
properties: 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: path:
default: jwt default: jwt
description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "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 description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method
type: string type: string
secretRef: 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: properties:
key: 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. 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: jwt:
description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method
properties: 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: path:
default: jwt default: jwt
description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "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 description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method
type: string type: string
secretRef: 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: properties:
key: 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. 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: jwt:
description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method
properties: 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: path:
default: jwt default: jwt
description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "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 description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method
type: string type: string
secretRef: 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: properties:
key: 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. 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 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 [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. or `Kind=ClusterSecretStore` resource.
```yaml ```yaml

View file

@ -17,6 +17,18 @@ spec:
path: "jwt" path: "jwt"
# JWT role configured in a Vault server, optional. # JWT role configured in a Vault server, optional.
role: "vault-jwt-role" role: "vault-jwt-role"
# Retrieve JWT token from a Kubernetes secret
secretRef: secretRef:
name: "my-secret" name: "my-secret"
key: "jwt-token" 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 JWTToken string
JWTRole string JWTRole string
JWTPath string JWTPath string
JWTK8sPath string
KubernetesAuthPath string KubernetesAuthPath string
KubernetesAuthRole string KubernetesAuthRole string
@ -162,6 +163,7 @@ func (l *Vault) initVault() error {
l.JWTPubkey = jwtPubkey l.JWTPubkey = jwtPubkey
l.JWTToken = jwtToken l.JWTToken = jwtToken
l.JWTPath = "myjwt" // see configure-vault.sh l.JWTPath = "myjwt" // see configure-vault.sh
l.JWTK8sPath = "myjwtk8s" // see configure-vault.sh
l.JWTRole = "external-secrets-operator" // see configure-vault.sh l.JWTRole = "external-secrets-operator" // see configure-vault.sh
l.KubernetesAuthPath = "mykubernetes" // see configure-vault.sh l.KubernetesAuthPath = "mykubernetes" // see configure-vault.sh
l.KubernetesAuthRole = "external-secrets-operator" // 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 \ policies=external-secrets-operator \
ttl=1h 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 # Kubernetes AUTH
# https://www.vaultproject.io/docs/auth/kubernetes # https://www.vaultproject.io/docs/auth/kubernetes

View file

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

View file

@ -34,6 +34,11 @@ kubectl create clusterrolebinding permissive-binding \
--user=kubelet \ --user=kubelet \
--serviceaccount=default:external-secrets-e2e || true --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..."; \ echo -e "Waiting service account..."; \
until kubectl get secret | grep -q -e ^external-secrets-e2e-token; do \ until kubectl get secret | grep -q -e ^external-secrets-e2e-token; do \
echo -e "waiting for api token"; \ echo -e "waiting for api token"; \

View file

@ -45,6 +45,7 @@ const (
appRoleAuthProviderName = "app-role-provider" appRoleAuthProviderName = "app-role-provider"
kvv1ProviderName = "kv-v1-provider" kvv1ProviderName = "kv-v1-provider"
jwtProviderName = "jwt-provider" jwtProviderName = "jwt-provider"
jwtK8sProviderName = "jwt-k8s-provider"
kubernetesProviderName = "kubernetes-provider" kubernetesProviderName = "kubernetes-provider"
) )
@ -95,6 +96,7 @@ func (s *vaultProvider) BeforeEach() {
s.CreateAppRoleStore(v, ns) s.CreateAppRoleStore(v, ns)
s.CreateV1Store(v, ns) s.CreateV1Store(v, ns)
s.CreateJWTStore(v, ns) s.CreateJWTStore(v, ns)
s.CreateJWTK8sStore(v, ns)
s.CreateKubernetesAuthStore(v, ns) s.CreateKubernetesAuthStore(v, ns)
} }
@ -249,7 +251,7 @@ func (s vaultProvider) CreateJWTStore(v *addon.Vault, ns string) {
Jwt: &esv1beta1.VaultJwtAuth{ Jwt: &esv1beta1.VaultJwtAuth{
Path: v.JWTPath, Path: v.JWTPath,
Role: v.JWTRole, Role: v.JWTRole,
SecretRef: esmeta.SecretKeySelector{ SecretRef: &esmeta.SecretKeySelector{
Name: "jwt-provider", Name: "jwt-provider",
Key: "jwt", Key: "jwt",
}, },
@ -259,6 +261,26 @@ func (s vaultProvider) CreateJWTStore(v *addon.Vault, ns string) {
Expect(err).ToNot(HaveOccurred()) 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) { func (s vaultProvider) CreateKubernetesAuthStore(v *addon.Vault, ns string) {
secretStore := makeStore(kubernetesProviderName, ns, v) secretStore := makeStore(kubernetesProviderName, ns, v)
secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{ secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{

View file

@ -30,6 +30,7 @@ const (
withApprole = "with approle auth" withApprole = "with approle auth"
withV1 = "with v1 provider" withV1 = "with v1 provider"
withJWT = "with jwt provider" withJWT = "with jwt provider"
withJWTK8s = "with jwt k8s provider"
withK8s = "with kubernetes 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.JSONDataWithTemplate, useJWTProvider),
framework.Compose(withJWT, f, common.DataPropertyDockerconfigJSON, useJWTProvider), framework.Compose(withJWT, f, common.DataPropertyDockerconfigJSON, useJWTProvider),
framework.Compose(withJWT, f, common.JSONDataWithoutTargetName, 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 // use kubernetes provider
framework.Compose(withK8s, f, common.FindByName, useKubernetesProvider), framework.Compose(withK8s, f, common.FindByName, useKubernetesProvider),
framework.Compose(withK8s, f, common.JSONDataFromSync, useKubernetesProvider), framework.Compose(withK8s, f, common.JSONDataFromSync, useKubernetesProvider),
@ -109,6 +116,10 @@ func useJWTProvider(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Name = jwtProviderName tc.ExternalSecret.Spec.SecretStoreRef.Name = jwtProviderName
} }
func useJWTK8sProvider(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Name = jwtK8sProviderName
}
func useKubernetesProvider(tc *framework.TestCase) { func useKubernetesProvider(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Name = kubernetesProviderName tc.ExternalSecret.Spec.SecretStoreRef.Name = kubernetesProviderName
} }

View file

@ -29,10 +29,15 @@ import (
"github.com/go-logr/logr" "github.com/go-logr/logr"
vault "github.com/hashicorp/vault/api" vault "github.com/hashicorp/vault/api"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
authenticationv1 "k8s.io/api/authentication/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "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" ctrl "sigs.k8s.io/controller-runtime"
kclient "sigs.k8s.io/controller-runtime/pkg/client" 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" esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1" esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
@ -64,10 +69,13 @@ const (
errVaultRequest = "error from Vault request: %w" errVaultRequest = "error from Vault request: %w"
errVaultResponse = "cannot parse Vault response: %w" errVaultResponse = "cannot parse Vault response: %w"
errServiceAccount = "cannot read Kubernetes service account token from file system: %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" errUnsupportedKvVersion = "cannot perform find operations with kv version v1"
errGetKubeSA = "cannot get Kubernetes service account %q: %w" errGetKubeSA = "cannot get Kubernetes service account %q: %w"
errGetKubeSASecrets = "cannot find secrets bound to service account: %q" errGetKubeSASecrets = "cannot find secrets bound to service account: %q"
errGetKubeSANoToken = "cannot find token in 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" errGetKubeSecret = "cannot get Kubernetes secret %q: %w"
errSecretKeyFmt = "cannot find secret data for key: %q" errSecretKeyFmt = "cannot find secret data for key: %q"
@ -106,6 +114,7 @@ type Client interface {
type client struct { type client struct {
kube kclient.Client kube kclient.Client
corev1 typedcorev1.CoreV1Interface
store *esv1beta1.VaultProvider store *esv1beta1.VaultProvider
log logr.Logger log logr.Logger
client Client 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) { 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() storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Vault == nil { if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Vault == nil {
return nil, errors.New(errVaultStore) return nil, errors.New(errVaultStore)
@ -138,6 +162,7 @@ func (c *connector) NewClient(ctx context.Context, store esv1beta1.GenericStore,
vStore := &client{ vStore := &client{
kube: kube, kube: kube,
corev1: corev1,
store: vaultSpec, store: vaultSpec,
log: ctrl.Log.WithName("provider").WithName("vault"), log: ctrl.Log.WithName("provider").WithName("vault"),
namespace: namespace, namespace: namespace,
@ -200,9 +225,17 @@ func (c *connector) ValidateStore(store esv1beta1.GenericStore) error {
} }
} }
if p.Auth.Jwt != nil { 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) 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 != nil {
if p.Auth.Kubernetes.ServiceAccountRef != nil { if p.Auth.Kubernetes.ServiceAccountRef != nil {
@ -822,6 +855,27 @@ func (v *client) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySe
return valueStr, nil 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. // checkToken does a lookup and checks if the provided token exists.
func checkToken(ctx context.Context, vStore *client) error { func checkToken(ctx context.Context, vStore *client) error {
// https://www.vaultproject.io/api-docs/auth/token#lookup-a-token-self // 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) { func (v *client) requestTokenWithJwtAuth(ctx context.Context, client Client, jwtAuth *esv1beta1.VaultJwtAuth) (string, error) {
role := strings.TrimSpace(jwtAuth.Role) 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 { if err != nil {
return "", err return "", err
} }

View file

@ -564,7 +564,7 @@ func vaultTest(t *testing.T, name string, tc testCase) {
if tc.args.newClientFunc == nil { if tc.args.newClientFunc == nil {
conn.newVaultClient = newVaultClient 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 != "" { 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) 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{ args: args{
auth: esv1beta1.VaultAuth{ auth: esv1beta1.VaultAuth{
Jwt: &esv1beta1.VaultJwtAuth{ Jwt: &esv1beta1.VaultJwtAuth{
SecretRef: esmeta.SecretKeySelector{ SecretRef: &esmeta.SecretKeySelector{
Namespace: pointer.StringPtr("invalid"), Namespace: pointer.StringPtr("invalid"),
}, },
}, },