serviceAccountRef: auto-generate "aud" and hardcode "exp"

Signed-off-by: Maël Valais <mael@vls.dev>
This commit is contained in:
Maël Valais 2023-01-27 18:00:55 +01:00
parent bfce543640
commit aed8a2ec85
17 changed files with 94 additions and 112 deletions

View File

@ -245,7 +245,7 @@ type VaultKubernetesAuth struct {
// for authenticating with Vault. Use of 'ambient credentials' is not
// supported. This field should not be set if serviceAccountRef is set.
// +optional
SecretRef *cmmeta.SecretKeySelector
SecretRef cmmeta.SecretKeySelector
// A reference to a service account that will be used to request a bound
// token (also known as "projected token"). Compared to using "secretRef",

View File

@ -1459,14 +1459,8 @@ func Convert_certmanager_VaultIssuer_To_v1_VaultIssuer(in *certmanager.VaultIssu
func autoConvert_v1_VaultKubernetesAuth_To_certmanager_VaultKubernetesAuth(in *v1.VaultKubernetesAuth, out *certmanager.VaultKubernetesAuth, s conversion.Scope) error {
out.Path = in.Path
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(meta.SecretKeySelector)
if err := internalapismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.SecretRef = nil
if err := internalapismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(&in.SecretRef, &out.SecretRef, s); err != nil {
return err
}
out.ServiceAccountRef = (*certmanager.ServiceAccountRef)(unsafe.Pointer(in.ServiceAccountRef))
out.Role = in.Role
@ -1480,14 +1474,8 @@ func Convert_v1_VaultKubernetesAuth_To_certmanager_VaultKubernetesAuth(in *v1.Va
func autoConvert_certmanager_VaultKubernetesAuth_To_v1_VaultKubernetesAuth(in *certmanager.VaultKubernetesAuth, out *v1.VaultKubernetesAuth, s conversion.Scope) error {
out.Path = in.Path
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(apismetav1.SecretKeySelector)
if err := internalapismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.SecretRef = nil
if err := internalapismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(&in.SecretRef, &out.SecretRef, s); err != nil {
return err
}
out.ServiceAccountRef = (*v1.ServiceAccountRef)(unsafe.Pointer(in.ServiceAccountRef))
out.Role = in.Role

View File

@ -266,7 +266,7 @@ type VaultKubernetesAuth struct {
// for authenticating with Vault. Use of 'ambient credentials' is not
// supported.
// +optional
SecretRef *cmmeta.SecretKeySelector `json:"secretRef,omitempty"`
SecretRef cmmeta.SecretKeySelector `json:"secretRef,omitempty"`
// A reference to a service account that will be used to request a bound
// token (also known as "projected token"). Compared to using "secretRef",

View File

@ -1475,14 +1475,8 @@ func Convert_certmanager_VaultIssuer_To_v1alpha2_VaultIssuer(in *certmanager.Vau
func autoConvert_v1alpha2_VaultKubernetesAuth_To_certmanager_VaultKubernetesAuth(in *VaultKubernetesAuth, out *certmanager.VaultKubernetesAuth, s conversion.Scope) error {
out.Path = in.Path
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(meta.SecretKeySelector)
if err := apismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.SecretRef = nil
if err := apismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(&in.SecretRef, &out.SecretRef, s); err != nil {
return err
}
out.ServiceAccountRef = (*certmanager.ServiceAccountRef)(unsafe.Pointer(in.ServiceAccountRef))
out.Role = in.Role
@ -1496,14 +1490,8 @@ func Convert_v1alpha2_VaultKubernetesAuth_To_certmanager_VaultKubernetesAuth(in
func autoConvert_certmanager_VaultKubernetesAuth_To_v1alpha2_VaultKubernetesAuth(in *certmanager.VaultKubernetesAuth, out *VaultKubernetesAuth, s conversion.Scope) error {
out.Path = in.Path
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(metav1.SecretKeySelector)
if err := apismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.SecretRef = nil
if err := apismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(&in.SecretRef, &out.SecretRef, s); err != nil {
return err
}
out.ServiceAccountRef = (*ServiceAccountRef)(unsafe.Pointer(in.ServiceAccountRef))
out.Role = in.Role

View File

@ -916,11 +916,7 @@ func (in *VaultIssuer) DeepCopy() *VaultIssuer {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultKubernetesAuth) DeepCopyInto(out *VaultKubernetesAuth) {
*out = *in
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(metav1.SecretKeySelector)
**out = **in
}
out.SecretRef = in.SecretRef
if in.ServiceAccountRef != nil {
in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
*out = new(ServiceAccountRef)

View File

@ -266,7 +266,7 @@ type VaultKubernetesAuth struct {
// for authenticating with Vault. Use of 'ambient credentials' is not
// supported.
// +optional
SecretRef *cmmeta.SecretKeySelector `json:"secretRef,omitempty"`
SecretRef cmmeta.SecretKeySelector `json:"secretRef,omitempty"`
// A reference to a service account that will be used to request a bound
// token (also known as "projected token"). Compared to using "secretRef",

View File

@ -1474,14 +1474,8 @@ func Convert_certmanager_VaultIssuer_To_v1alpha3_VaultIssuer(in *certmanager.Vau
func autoConvert_v1alpha3_VaultKubernetesAuth_To_certmanager_VaultKubernetesAuth(in *VaultKubernetesAuth, out *certmanager.VaultKubernetesAuth, s conversion.Scope) error {
out.Path = in.Path
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(meta.SecretKeySelector)
if err := apismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.SecretRef = nil
if err := apismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(&in.SecretRef, &out.SecretRef, s); err != nil {
return err
}
out.ServiceAccountRef = (*certmanager.ServiceAccountRef)(unsafe.Pointer(in.ServiceAccountRef))
out.Role = in.Role
@ -1495,14 +1489,8 @@ func Convert_v1alpha3_VaultKubernetesAuth_To_certmanager_VaultKubernetesAuth(in
func autoConvert_certmanager_VaultKubernetesAuth_To_v1alpha3_VaultKubernetesAuth(in *certmanager.VaultKubernetesAuth, out *VaultKubernetesAuth, s conversion.Scope) error {
out.Path = in.Path
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(metav1.SecretKeySelector)
if err := apismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.SecretRef = nil
if err := apismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(&in.SecretRef, &out.SecretRef, s); err != nil {
return err
}
out.ServiceAccountRef = (*ServiceAccountRef)(unsafe.Pointer(in.ServiceAccountRef))
out.Role = in.Role

View File

@ -911,11 +911,7 @@ func (in *VaultIssuer) DeepCopy() *VaultIssuer {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultKubernetesAuth) DeepCopyInto(out *VaultKubernetesAuth) {
*out = *in
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(metav1.SecretKeySelector)
**out = **in
}
out.SecretRef = in.SecretRef
if in.ServiceAccountRef != nil {
in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
*out = new(ServiceAccountRef)

View File

@ -268,7 +268,7 @@ type VaultKubernetesAuth struct {
// for authenticating with Vault. Use of 'ambient credentials' is not
// supported.
// +optional
SecretRef *cmmeta.SecretKeySelector `json:"secretRef,omitempty"`
SecretRef cmmeta.SecretKeySelector `json:"secretRef,omitempty"`
// A reference to a service account that will be used to request a bound
// token (also known as "projected token"). Compared to using "secretRef",

View File

@ -1467,14 +1467,8 @@ func Convert_certmanager_VaultIssuer_To_v1beta1_VaultIssuer(in *certmanager.Vaul
func autoConvert_v1beta1_VaultKubernetesAuth_To_certmanager_VaultKubernetesAuth(in *VaultKubernetesAuth, out *certmanager.VaultKubernetesAuth, s conversion.Scope) error {
out.Path = in.Path
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(meta.SecretKeySelector)
if err := apismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.SecretRef = nil
if err := apismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(&in.SecretRef, &out.SecretRef, s); err != nil {
return err
}
out.ServiceAccountRef = (*certmanager.ServiceAccountRef)(unsafe.Pointer(in.ServiceAccountRef))
out.Role = in.Role
@ -1488,14 +1482,8 @@ func Convert_v1beta1_VaultKubernetesAuth_To_certmanager_VaultKubernetesAuth(in *
func autoConvert_certmanager_VaultKubernetesAuth_To_v1beta1_VaultKubernetesAuth(in *certmanager.VaultKubernetesAuth, out *VaultKubernetesAuth, s conversion.Scope) error {
out.Path = in.Path
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(metav1.SecretKeySelector)
if err := apismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.SecretRef = nil
if err := apismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(&in.SecretRef, &out.SecretRef, s); err != nil {
return err
}
out.ServiceAccountRef = (*ServiceAccountRef)(unsafe.Pointer(in.ServiceAccountRef))
out.Role = in.Role

View File

@ -911,11 +911,7 @@ func (in *VaultIssuer) DeepCopy() *VaultIssuer {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultKubernetesAuth) DeepCopyInto(out *VaultKubernetesAuth) {
*out = *in
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(metav1.SecretKeySelector)
**out = **in
}
out.SecretRef = in.SecretRef
if in.ServiceAccountRef != nil {
in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
*out = new(ServiceAccountRef)

View File

@ -911,11 +911,7 @@ func (in *VaultIssuer) DeepCopy() *VaultIssuer {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultKubernetesAuth) DeepCopyInto(out *VaultKubernetesAuth) {
*out = *in
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(meta.SecretKeySelector)
**out = **in
}
out.SecretRef = in.SecretRef
if in.ServiceAccountRef != nil {
in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
*out = new(ServiceAccountRef)

View File

@ -32,6 +32,7 @@ import (
authv1 "k8s.io/api/authentication/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corelisters "k8s.io/client-go/listers/core/v1"
"k8s.io/utils/pointer"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
@ -389,10 +390,25 @@ func (v *Vault) requestTokenWithKubernetesAuth(client Client, kubernetesAuth *v1
jwt = string(keyBytes)
case kubernetesAuth.ServiceAccountRef.Name != "":
aud := "vault://"
if v.issuer.GetNamespace() != "" {
aud += v.issuer.GetNamespace() + "/"
}
aud += v.issuer.GetName()
tokenrequest, err := v.createToken(context.Background(), kubernetesAuth.ServiceAccountRef.Name, &authv1.TokenRequest{
Spec: authv1.TokenRequestSpec{
Audiences: []string{kubernetesAuth.ServiceAccountRef.Audience},
ExpirationSeconds: &kubernetesAuth.ServiceAccountRef.ExpirationSeconds,
// The audience is generated by cert-manager and can't be
// configured by the user for security reasons. The format is:
// "vault://<namespace>/<issuer-name>" (for an Issuer)
// "vault://<issuer-name>" (for a ClusterIssuer)
Audiences: []string{aud},
// Since the JWT is only used to authenticate with Vault and is
// immediately discarded, 1 minute is more than enough. Note
// that all Kubernetes API servers won't accept that duration,
// they may return a JWT with a longer lifetime.
ExpirationSeconds: pointer.Int64(60),
},
}, metav1.CreateOptions{})
if err != nil {

View File

@ -388,7 +388,7 @@ func TestSetToken(t *testing.T) {
expectedToken string
expectedErr error
issuer *cmapi.Issuer
issuer cmapi.GenericIssuer
fakeLister *listers.FakeSecretLister
mockCreateToken func(t *testing.T) CreateToken
@ -641,17 +641,15 @@ func TestSetToken(t *testing.T) {
expectedErr: nil,
},
"if kubernetes.serviceAccountRef set, request token and exchange it for a vault token": {
"if kubernetes.serviceAccountRef set, request token and exchange it for a vault token (Issuer)": {
issuer: gen.Issuer("vault-issuer",
gen.SetIssuerVault(cmapi.VaultIssuer{
CABundle: []byte(testLeafCertificate),
Auth: cmapi.VaultAuth{
Kubernetes: &cmapi.VaultKubernetesAuth{
Role: "kube-vault-role",
ServiceAccountRef: v1.ServiceAccountRef{
Name: "my-service-account",
Audience: "my-audience",
ExpirationSeconds: 100,
ServiceAccountRef: &v1.ServiceAccountRef{
Name: "my-service-account",
},
Path: "my-path",
},
@ -661,8 +659,45 @@ func TestSetToken(t *testing.T) {
mockCreateToken: func(t *testing.T) CreateToken {
return func(_ context.Context, saName string, req *authv1.TokenRequest, _ metav1.CreateOptions) (*authv1.TokenRequest, error) {
assert.Equal(t, "my-service-account", saName)
assert.Equal(t, "my-audience", req.Spec.Audiences[0])
assert.Equal(t, int64(100), *req.Spec.ExpirationSeconds)
assert.Equal(t, "vault://default-unit-test-ns/vault-issuer", req.Spec.Audiences[0])
assert.Equal(t, int64(60), *req.Spec.ExpirationSeconds)
return &authv1.TokenRequest{Status: authv1.TokenRequestStatus{
Token: "kube-sa-token",
}}, nil
}
},
fakeClient: vaultfake.NewFakeClient().WithRawRequestFn(func(t *testing.T, req *vault.Request) (*vault.Response, error) {
// Vault exhanges the Kubernetes token with a Vault token.
assert.Equal(t, "kube-sa-token", req.Obj.(map[string]string)["jwt"])
assert.Equal(t, "kube-vault-role", req.Obj.(map[string]string)["role"])
return &vault.Response{Response: &http.Response{Body: io.NopCloser(strings.NewReader(
`{"request_id":"","lease_id":"","lease_duration":0,"renewable":false,"data":null,"warnings":null,"data":{"id":"vault-token"}}`,
))}}, nil
}),
expectedToken: "vault-token",
expectedErr: nil,
},
"if kubernetes.serviceAccountRef set, request token and exchange it for a vault token (ClusterIssuer)": {
issuer: gen.ClusterIssuer("vault-issuer",
gen.SetIssuerVault(cmapi.VaultIssuer{
CABundle: []byte(testLeafCertificate),
Auth: cmapi.VaultAuth{
Kubernetes: &cmapi.VaultKubernetesAuth{
Role: "kube-vault-role",
ServiceAccountRef: &v1.ServiceAccountRef{
Name: "my-service-account",
},
Path: "my-path",
},
},
}),
),
mockCreateToken: func(t *testing.T) CreateToken {
return func(_ context.Context, saName string, req *authv1.TokenRequest, _ metav1.CreateOptions) (*authv1.TokenRequest, error) {
assert.Equal(t, "my-service-account", saName)
assert.Equal(t, "vault://vault-issuer", req.Spec.Audiences[0])
assert.Equal(t, int64(60), *req.Spec.ExpirationSeconds)
return &authv1.TokenRequest{Status: authv1.TokenRequestStatus{
Token: "kube-sa-token",
}}, nil
@ -692,6 +727,7 @@ func TestSetToken(t *testing.T) {
if test.mockCreateToken != nil {
mockCreateToken = test.mockCreateToken(t)
}
v := &Vault{
namespace: "test-namespace",
secretsLister: test.fakeLister,
@ -1076,10 +1112,8 @@ func TestNewConfig(t *testing.T) {
Auth: cmapi.VaultAuth{
Kubernetes: &cmapi.VaultKubernetesAuth{
Role: "my-role",
ServiceAccountRef: v1.ServiceAccountRef{
Name: "my-sa",
Audience: "my-audience",
ExpirationSeconds: 100,
ServiceAccountRef: &v1.ServiceAccountRef{
Name: "my-sa",
},
},
}})),

View File

@ -270,7 +270,9 @@ type VaultKubernetesAuth struct {
// for authenticating with Vault. Use of 'ambient credentials' is not
// supported.
// +optional
SecretRef *cmmeta.SecretKeySelector `json:"secretRef,omitempty"`
SecretRef cmmeta.SecretKeySelector `json:"secretRef,omitempty"`
// Note: it should be a pointer because it is optional. However, for
// backward compatibility, we cannot change it to a pointer.
// A reference to a service account that will be used to request a bound
// token (also known as "projected token"). Compared to using "secretRef",

View File

@ -911,11 +911,7 @@ func (in *VaultIssuer) DeepCopy() *VaultIssuer {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultKubernetesAuth) DeepCopyInto(out *VaultKubernetesAuth) {
*out = *in
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(apismetav1.SecretKeySelector)
**out = **in
}
out.SecretRef = in.SecretRef
if in.ServiceAccountRef != nil {
in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
*out = new(ServiceAccountRef)

View File

@ -354,10 +354,8 @@ func SetIssuerVaultKubernetesAuthServiceAccount(serviceAccount, role, path strin
spec.Vault.Auth.Kubernetes = &v1.VaultKubernetesAuth{
Path: path,
Role: role,
ServiceAccountRef: v1.ServiceAccountRef{
Name: serviceAccount,
Audience: "vault",
ExpirationSeconds: 600,
ServiceAccountRef: &v1.ServiceAccountRef{
Name: serviceAccount,
},
}