Merge pull request #7036 from fidelity-contributions/feature/5514-venafi-issuer-ca-ref-support

Feature/5514 - Add SecretRef support for venafi TPP issuer CA Bundle
This commit is contained in:
cert-manager-prow[bot] 2024-06-24 14:18:20 +00:00 committed by GitHub
commit 837c6a1e06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 537 additions and 4 deletions

View File

@ -2341,6 +2341,28 @@ spec:
is used to validate the chain.
type: string
format: byte
caBundleSecretRef:
description: |-
Reference to a Secret containing a base64-encoded bundle of PEM CAs
which will be used to validate the certificate chain presented by the TPP server.
Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle.
If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in
the cert-manager controller container is used to validate the TLS connection.
type: object
required:
- name
properties:
key:
description: |-
The key of the entry in the Secret resource's `data` field to be used.
Some instances of this field may be defaulted, in others it may be
required.
type: string
name:
description: |-
Name of the resource being referred to.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
credentialsRef:
description: |-
CredentialsRef is a reference to a Secret containing the username and

View File

@ -2341,6 +2341,28 @@ spec:
is used to validate the chain.
type: string
format: byte
caBundleSecretRef:
description: |-
Reference to a Secret containing a base64-encoded bundle of PEM CAs
which will be used to validate the certificate chain presented by the TPP server.
Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle.
If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in
the cert-manager controller container is used to validate the TLS connection.
type: object
required:
- name
properties:
key:
description: |-
The key of the entry in the Secret resource's `data` field to be used.
Some instances of this field may be defaulted, in others it may be
required.
type: string
name:
description: |-
Name of the resource being referred to.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
credentialsRef:
description: |-
CredentialsRef is a reference to a Secret containing the username and

View File

@ -142,6 +142,13 @@ type VenafiTPP struct {
// If undefined, the certificate bundle in the cert-manager controller container
// is used to validate the chain.
CABundle []byte
// Reference to a Secret containing a base64-encoded bundle of PEM CAs
// which will be used to validate the certificate chain presented by the TPP server.
// Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle.
// If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in
// the cert-manager controller container is used to validate the TLS connection.
CABundleSecretRef *cmmeta.SecretKeySelector `json:"caBundleSecretRef,omitempty"`
}
// VenafiCloud defines connection configuration details for Venafi Cloud

View File

@ -1771,6 +1771,15 @@ func autoConvert_v1_VenafiTPP_To_certmanager_VenafiTPP(in *v1.VenafiTPP, out *ce
return err
}
out.CABundle = *(*[]byte)(unsafe.Pointer(&in.CABundle))
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(meta.SecretKeySelector)
if err := internalapismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.CABundleSecretRef = nil
}
return nil
}
@ -1785,6 +1794,15 @@ func autoConvert_certmanager_VenafiTPP_To_v1_VenafiTPP(in *certmanager.VenafiTPP
return err
}
out.CABundle = *(*[]byte)(unsafe.Pointer(&in.CABundle))
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(apismetav1.SecretKeySelector)
if err := internalapismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.CABundleSecretRef = nil
}
return nil
}

View File

@ -156,6 +156,14 @@ type VenafiTPP struct {
// is used to validate the chain.
// +optional
CABundle []byte `json:"caBundle,omitempty"`
// Reference to a Secret containing a base64-encoded bundle of PEM CAs
// which will be used to validate the certificate chain presented by the TPP server.
// Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle.
// If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in
// the cert-manager controller container is used to validate the TLS connection.
// +optional
CABundleSecretRef *cmmeta.SecretKeySelector `json:"caBundleSecretRef,omitempty"`
}
// VenafiCloud defines connection configuration details for Venafi Cloud

View File

@ -1772,6 +1772,15 @@ func autoConvert_v1alpha2_VenafiTPP_To_certmanager_VenafiTPP(in *VenafiTPP, out
return err
}
out.CABundle = *(*[]byte)(unsafe.Pointer(&in.CABundle))
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(meta.SecretKeySelector)
if err := apismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.CABundleSecretRef = nil
}
return nil
}
@ -1786,6 +1795,15 @@ func autoConvert_certmanager_VenafiTPP_To_v1alpha2_VenafiTPP(in *certmanager.Ven
return err
}
out.CABundle = *(*[]byte)(unsafe.Pointer(&in.CABundle))
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(metav1.SecretKeySelector)
if err := apismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.CABundleSecretRef = nil
}
return nil
}

View File

@ -1121,6 +1121,11 @@ func (in *VenafiTPP) DeepCopyInto(out *VenafiTPP) {
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(metav1.SecretKeySelector)
**out = **in
}
return
}

View File

@ -156,6 +156,14 @@ type VenafiTPP struct {
// is used to validate the chain.
// +optional
CABundle []byte `json:"caBundle,omitempty"`
// Reference to a Secret containing a base64-encoded bundle of PEM CAs
// which will be used to validate the certificate chain presented by the TPP server.
// Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle.
// If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in
// the cert-manager controller container is used to validate the TLS connection.
// +optional
CABundleSecretRef *cmmeta.SecretKeySelector `json:"caBundleSecretRef,omitempty"`
}
// VenafiCloud defines connection configuration details for Venafi Cloud

View File

@ -1771,6 +1771,15 @@ func autoConvert_v1alpha3_VenafiTPP_To_certmanager_VenafiTPP(in *VenafiTPP, out
return err
}
out.CABundle = *(*[]byte)(unsafe.Pointer(&in.CABundle))
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(meta.SecretKeySelector)
if err := apismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.CABundleSecretRef = nil
}
return nil
}
@ -1785,6 +1794,15 @@ func autoConvert_certmanager_VenafiTPP_To_v1alpha3_VenafiTPP(in *certmanager.Ven
return err
}
out.CABundle = *(*[]byte)(unsafe.Pointer(&in.CABundle))
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(metav1.SecretKeySelector)
if err := apismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.CABundleSecretRef = nil
}
return nil
}

View File

@ -1116,6 +1116,11 @@ func (in *VenafiTPP) DeepCopyInto(out *VenafiTPP) {
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(metav1.SecretKeySelector)
**out = **in
}
return
}

View File

@ -158,6 +158,14 @@ type VenafiTPP struct {
// is used to validate the chain.
// +optional
CABundle []byte `json:"caBundle,omitempty"`
// Reference to a Secret containing a base64-encoded bundle of PEM CAs
// which will be used to validate the certificate chain presented by the TPP server.
// Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle.
// If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in
// the cert-manager controller container is used to validate the TLS connection.
// +optional
CABundleSecretRef *cmmeta.SecretKeySelector `json:"caBundleSecretRef,omitempty"`
}
// VenafiCloud defines connection configuration details for Venafi Cloud

View File

@ -1759,6 +1759,15 @@ func autoConvert_v1beta1_VenafiTPP_To_certmanager_VenafiTPP(in *VenafiTPP, out *
return err
}
out.CABundle = *(*[]byte)(unsafe.Pointer(&in.CABundle))
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(meta.SecretKeySelector)
if err := apismetav1.Convert_v1_SecretKeySelector_To_meta_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.CABundleSecretRef = nil
}
return nil
}
@ -1773,6 +1782,15 @@ func autoConvert_certmanager_VenafiTPP_To_v1beta1_VenafiTPP(in *certmanager.Vena
return err
}
out.CABundle = *(*[]byte)(unsafe.Pointer(&in.CABundle))
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(metav1.SecretKeySelector)
if err := apismetav1.Convert_meta_SecretKeySelector_To_v1_SecretKeySelector(*in, *out, s); err != nil {
return err
}
} else {
out.CABundleSecretRef = nil
}
return nil
}

View File

@ -1116,6 +1116,11 @@ func (in *VenafiTPP) DeepCopyInto(out *VenafiTPP) {
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(metav1.SecretKeySelector)
**out = **in
}
return
}

View File

@ -363,6 +363,25 @@ func ValidateVenafiTPP(tpp *certmanager.VenafiTPP, fldPath *field.Path) (el fiel
// TODO: validate CABundle using validateCABundleNotEmpty
// Validate only one of CABundle/CABundleSecretRef is passed
el = append(el, validateVenafiTPPCABundleUnique(tpp, fldPath)...)
return el
}
func validateVenafiTPPCABundleUnique(tpp *certmanager.VenafiTPP, fldPath *field.Path) (el field.ErrorList) {
numCAs := 0
if len(tpp.CABundle) > 0 {
numCAs++
}
if tpp.CABundleSecretRef != nil {
numCAs++
}
if numCAs > 1 {
el = append(el, field.Forbidden(fldPath, "may not specify more than one of caBundle/caBundleSecretRef as TPP CA Bundle"))
}
return el
}

View File

@ -1651,6 +1651,10 @@ func TestValidateVenafiIssuerConfig(t *testing.T) {
}
func TestValidateVenafiTPP(t *testing.T) {
caBundle := unitcrypto.MustCreateCryptoBundle(t,
&pubcmapi.Certificate{Spec: pubcmapi.CertificateSpec{CommonName: "test"}},
clock.RealClock{},
).CertBytes
fldPath := field.NewPath("test")
scenarios := map[string]struct {
cfg *cmapi.VenafiTPP
@ -1667,6 +1671,21 @@ func TestValidateVenafiTPP(t *testing.T) {
field.Required(fldPath.Child("url"), ""),
},
},
"venafi TPP issuer defines both caBundle and caBundleSecretRef": {
cfg: &cmapi.VenafiTPP{
URL: "https://tpp.example.com/vedsdk",
CABundle: caBundle,
CABundleSecretRef: &cmmeta.SecretKeySelector{
Key: "ca.crt",
LocalObjectReference: cmmeta.LocalObjectReference{
Name: "test-secret",
},
},
},
errs: []*field.Error{
field.Forbidden(fldPath, "may not specify more than one of caBundle/caBundleSecretRef as TPP CA Bundle"),
},
},
}
for n, s := range scenarios {

View File

@ -1116,6 +1116,11 @@ func (in *VenafiTPP) DeepCopyInto(out *VenafiTPP) {
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(meta.SecretKeySelector)
**out = **in
}
return
}

View File

@ -160,6 +160,14 @@ type VenafiTPP struct {
// is used to validate the chain.
// +optional
CABundle []byte `json:"caBundle,omitempty"`
// Reference to a Secret containing a base64-encoded bundle of PEM CAs
// which will be used to validate the certificate chain presented by the TPP server.
// Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle.
// If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in
// the cert-manager controller container is used to validate the TLS connection.
// +optional
CABundleSecretRef *cmmeta.SecretKeySelector `json:"caBundleSecretRef,omitempty"`
}
// VenafiCloud defines connection configuration details for Venafi Cloud

View File

@ -1116,6 +1116,11 @@ func (in *VenafiTPP) DeepCopyInto(out *VenafiTPP) {
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(apismetav1.SecretKeySelector)
**out = **in
}
return
}

View File

@ -29,7 +29,7 @@ func (c *controller) issuersForSecret(secret *corev1.Secret) ([]*v1.ClusterIssue
issuers, err := c.clusterIssuerLister.List(labels.NewSelector())
if err != nil {
return nil, fmt.Errorf("error listing certificates: %s", err.Error())
return nil, fmt.Errorf("error listing issuers: %s", err.Error())
}
var affected []*v1.ClusterIssuer
@ -61,6 +61,12 @@ func (c *controller) issuersForSecret(secret *corev1.Secret) ([]*v1.ClusterIssue
continue
}
}
if iss.Spec.Venafi.TPP.CABundleSecretRef != nil {
if iss.Spec.Venafi.TPP.CABundleSecretRef.Name == secret.Name {
affected = append(affected, iss)
continue
}
}
if iss.Spec.Venafi.Cloud != nil {
if iss.Spec.Venafi.Cloud.APITokenSecretRef.Name == secret.Name {
affected = append(affected, iss)

View File

@ -29,7 +29,7 @@ func (c *controller) issuersForSecret(secret *corev1.Secret) ([]*v1.Issuer, erro
issuers, err := c.issuerLister.List(labels.NewSelector())
if err != nil {
return nil, fmt.Errorf("error listing certificates: %s", err.Error())
return nil, fmt.Errorf("error listing issuers: %s", err.Error())
}
var affected []*v1.Issuer
@ -63,6 +63,12 @@ func (c *controller) issuersForSecret(secret *corev1.Secret) ([]*v1.Issuer, erro
continue
}
}
if iss.Spec.Venafi.TPP.CABundleSecretRef != nil {
if iss.Spec.Venafi.TPP.CABundleSecretRef.Name == secret.Name {
affected = append(affected, iss)
continue
}
}
if iss.Spec.Venafi.Cloud != nil {
if iss.Spec.Venafi.Cloud.APITokenSecretRef.Name == secret.Name {
affected = append(affected, iss)

View File

@ -34,6 +34,7 @@ import (
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
"github.com/cert-manager/cert-manager/pkg/issuer/venafi/client/api"
"github.com/cert-manager/cert-manager/pkg/metrics"
"github.com/cert-manager/cert-manager/pkg/util"
@ -139,6 +140,11 @@ func configForIssuer(iss cmapi.GenericIssuer, secretsLister internalinformers.Se
return nil, err
}
caBundle, err := caBundleForVcertTPP(tpp, secretsLister, namespace)
if err != nil {
return nil, err
}
username := string(tppSecret.Data[tppUsernameKey])
password := string(tppSecret.Data[tppPasswordKey])
accessToken := string(tppSecret.Data[tppAccessTokenKey])
@ -156,7 +162,7 @@ func configForIssuer(iss cmapi.GenericIssuer, secretsLister internalinformers.Se
// below. But we want to retain the CA bundle validation errors that
// were returned in previous versions of this code.
// https://github.com/Venafi/vcert/blob/89645a7710a7b529765274cb60dc5e28066217a1/client.go#L55-L61
ConnectionTrust: string(tpp.CABundle),
ConnectionTrust: string(caBundle),
Credentials: &endpoint.Authentication{
User: username,
Password: password,
@ -164,7 +170,7 @@ func configForIssuer(iss cmapi.GenericIssuer, secretsLister internalinformers.Se
},
Client: httpClientForVcert(&httpClientForVcertOptions{
UserAgent: ptr.To(userAgent),
CABundle: tpp.CABundle,
CABundle: caBundle,
TLSRenegotiationSupport: ptr.To(tls.RenegotiateOnceAsClient),
}),
}, nil
@ -312,6 +318,48 @@ func httpClientForVcert(options *httpClientForVcertOptions) *http.Client {
}
}
// caBundleForVcertTPP is used to by ConnectionTrust and Client fields of vcert.Config.
// This function sets appropriate CA based on provided bundle or kubernetes secret
// If no custom CA bundle is configured, an empty byte slice is returned.
// Assumes exactly one of the in-line/Secret CA bundles are defined.
// If the `key` of the Secret CA bundle is not defined, its value defaults to
// `ca.crt`.
func caBundleForVcertTPP(tpp *cmapi.VenafiTPP, secretsLister internalinformers.SecretLister, namespace string) (caBundle []byte, err error) {
if len(tpp.CABundle) > 0 {
return tpp.CABundle, nil
}
secretRef := tpp.CABundleSecretRef
if secretRef == nil {
return nil, nil
}
var certBytes []byte
var ok bool
if secretRef != nil {
secret, err := secretsLister.Secrets(namespace).Get(secretRef.Name)
if err != nil {
return nil, fmt.Errorf("could not access secret '%s/%s': %s", namespace, secretRef.Name, err)
}
var key string
if secretRef.Key != "" {
key = secretRef.Key
} else {
key = cmmeta.TLSCAKey
}
certBytes, ok = secret.Data[key]
if !ok {
return nil, fmt.Errorf("no data for %q in secret '%s/%s'", key, namespace, secretRef.Name)
}
}
return certBytes, nil
}
func (v *Venafi) Ping() error {
return v.vcertClient.Ping()
}

View File

@ -31,6 +31,50 @@ import (
testlisters "github.com/cert-manager/cert-manager/test/unit/listers"
)
const (
zone = "test-zone"
username = "test-username"
password = "test-password"
accessToken = "KT2EEVTIjWM/37L78dqJAg=="
apiKey = "test-api-key"
customKey = "test-custom-key"
defaultCaKey = "ca.crt"
customCaKey = "custom-ca-key"
tppUrl = "https://tpp.example.com/vedsdk"
customCaSecretName = "custom-ca-secret"
testLeafCertificate = `-----BEGIN CERTIFICATE-----
MIIFFTCCAv2gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRjELMAkGA1UEBhMCVVMx
CzAJBgNVBAgMAkNBMRQwEgYDVQQKDAtDRVJUTUFOQUdFUjEUMBIGA1UEAwwLZm9v
LmJhci5pbnQwHhcNMjAxMDAyMTQ1NzMwWhcNMjExMDEyMTQ1NzMwWjBKMQswCQYD
VQQGEwJVUzELMAkGA1UECAwCQ0ExFDASBgNVBAoMC0NFUlRNQU5BR0VSMRgwFgYD
VQQDDA9leGFtcGxlLmZvby5iYXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC8yTGzYIX3OoRma11vewbNf8dgKHc9GgvJJ29SVjaNwRAJjKOXokGOwcyQ
7Ieb1puYQ5KdSPC1IxyUx77URovIvd3Wql+J1gIxyrdN3om3uQdJ2ck6xatBZ8BI
Y3Z+6WpUQ2067Wk4KpUGfMrbGg5zVcesh6zc8J9yEiItUENeR+6GyEf+B8IJ0xqe
5lps2LaxZp6I6vaKeMELjj17Nb9r81Rjyk8BN7yX74tFE1mUGX9o75tsODU9IrYW
nqSl5gr2PO9Zb/bd6zhoncLJr9kj2tk6cLRPht+JOPoA2LAP6D0aEdC3a2XWuj2E
EsUYJR9e5C/X49VQaak0VdNnhO6RAgMBAAGjggEHMIIBAzAJBgNVHRMEAjAAMBEG
CWCGSAGG+EIBAQQEAwIGQDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0
ZWQgU2VydmVyIENlcnRpZmljYXRlMB0GA1UdDgQWBBQ41U/GiA2rQtuMz6tNL55C
o4pnBDBqBgNVHSMEYzBhgBSfus9cb7UA/PCfHJAGtL6ot2EpLKFFpEMwQTEPMA0G
A1UEAwwGYmFyLmNhMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFDASBgNVBAoM
C0NFUlRNQU5BR0VSggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYB
BQUHAwEwDQYJKoZIhvcNAQELBQADggIBAFFTJNqKSkkJNWWt+R7WFNIEKoaPcFH5
yupCQRYX9LK2cXdBQpF458/PFxREyt5jKFUcDyzQhOglFYq0hfcoAc2EB3Vw8Ww9
c4QCiCU6ehJVMRt7MzZ9uUVGCRVOA+Fa1tIFfL3dKlI+4pTSbDhNHRqDtFhfWOZK
bgtruQEUOW1lQR61AsidOF1iwDBU6ckpVY9Lc2SHEAfQFs0MoXmJ8B4MqFptF4+H
al+IAeQ1bC/2EccFYg3tq9+YKHDCyghHf8qeKJR9tZslvkHrAzuX56e0MHxM3AD6
D0L8nG3DsrHcjK0MlVUWmq0QFnY5t+78iocLoQZzpILZYuZn3p+XNlUdW4lcqSBn
y5fUwQ3RIuvN66GBhTeDV4vzYPa7g3i9PoBFoG50Ayr6VtIVn08rnl03lgp57Edv
A5oRrSHcd8Hd8/lk0Y9BpFTnZEg7RLhFhh9nazVp1/pjwaGx449uHIGEoxREQoPq
9Q+KLGMJR2IqiNI6+U1z2j8BChTOPkuAvsnSuAXyotu4BXBL5zbDzfDoggEk1ps1
bfHWnmdelE0WP7h7B0PSA0EXn0pdg2VQIQsknV6y3MCzFQCCSAog/OSguokXG1PG
l6fctDJ3+AF07EjtgArOBkUn7Nt3/CgMN8I1rnBZ1Vmd8yrHEP0E3yRXBL7cDj5j
Fqmd89NQLlGs
-----END CERTIFICATE-----
`
)
func checkNoConfigReturned(t *testing.T, cnf *vcert.Config) {
if cnf != nil {
t.Errorf("expected no config to be returned, got=%+v", cnf)
@ -48,6 +92,28 @@ func checkZone(t *testing.T, zone string, cnf *vcert.Config) {
}
}
func checkTppUrl(t *testing.T, tppUrl string, cnf *vcert.Config) {
if cnf == nil {
t.Errorf("expected config but got: %+v", cnf)
}
if tppUrl != cnf.BaseUrl {
t.Errorf("got unexpected BaseUrl set, exp=%s got=%s",
tppUrl, cnf.BaseUrl)
}
}
func checkTppCa(t *testing.T, ca string, cnf *vcert.Config) {
if cnf == nil {
t.Errorf("expected config but got: %+v", cnf)
}
if ca != cnf.ConnectionTrust {
t.Errorf("got unexpected CA as trust, exp=%s got=%s",
ca, cnf.ConnectionTrust)
}
}
func generateSecretLister(s *corev1.Secret, err error) internalinformers.SecretLister {
return &testlisters.FakeSecretLister{
SecretsFn: func(string) corelisters.SecretNamespaceLister {
@ -79,6 +145,36 @@ func TestConfigForIssuerT(t *testing.T) {
}),
)
tppIssuerWithoutCA := gen.IssuerFrom(baseIssuer,
gen.SetIssuerVenafi(cmapi.VenafiIssuer{
Zone: zone,
TPP: &cmapi.VenafiTPP{
URL: tppUrl,
},
}),
)
tppIssuerWithCABundle := gen.IssuerFrom(tppIssuerWithoutCA,
gen.SetIssuerVenafi(cmapi.VenafiIssuer{
TPP: &cmapi.VenafiTPP{
CABundle: []byte(testLeafCertificate),
},
}),
)
tppIssuerWithCABundleSecretRef := gen.IssuerFrom(tppIssuer,
gen.SetIssuerVenafi(cmapi.VenafiIssuer{
TPP: &cmapi.VenafiTPP{
CABundleSecretRef: &cmmeta.SecretKeySelector{
Key: customCaKey,
LocalObjectReference: cmmeta.LocalObjectReference{
Name: customCaSecretName,
},
},
},
}),
)
cloudIssuer := gen.IssuerFrom(baseIssuer,
gen.SetIssuerVenafi(cmapi.VenafiIssuer{
Zone: zone,
@ -109,6 +205,22 @@ func TestConfigForIssuerT(t *testing.T) {
CheckFn: checkNoConfigReturned,
expectedErr: true,
},
"if TPP and neither caBundle nor caBundleSecretRef is specified, CA bundle is not set in vcert config": {
iss: tppIssuerWithoutCA,
secretsLister: generateSecretLister(&corev1.Secret{
Data: map[string][]byte{
tppUsernameKey: []byte(username),
tppPasswordKey: []byte(password),
},
}, nil),
CheckFn: func(t *testing.T, cnf *vcert.Config) {
if trust := cnf.ConnectionTrust; trust != "" {
t.Errorf("got unexpected CA bundle: %s", trust)
}
checkTppUrl(t, tppUrl, cnf)
},
expectedErr: false,
},
"if TPP and secret returns user/pass, should return config with those credentials": {
iss: tppIssuer,
secretsLister: generateSecretLister(&corev1.Secret{
@ -143,6 +255,32 @@ func TestConfigForIssuerT(t *testing.T) {
},
expectedErr: false,
},
// NOTE: Below scenarios assume valid TPP CAs, the scenarios with invalid TPP CAs are run part of TestCaBundleForVcertTPP test
"if TPP and a good caBundle specified, CA bundle should be added to ConnectionTrust and Client in vcert config": {
iss: tppIssuerWithCABundle,
secretsLister: generateSecretLister(&corev1.Secret{
Data: map[string][]byte{
tppAccessTokenKey: []byte(accessToken),
},
}, nil),
CheckFn: func(t *testing.T, cnf *vcert.Config) {
checkTppCa(t, testLeafCertificate, cnf)
},
expectedErr: false,
},
"if TPP and a good caBundleSecretRef specified, CA bundle should be added to ConnectionTrust and Client in vcert config": {
iss: tppIssuerWithCABundleSecretRef,
// tppAccessTokenKey secret lister is not passed as we only have single secretsLister in testConfigForIssuerT struck
secretsLister: generateSecretLister(&corev1.Secret{
Data: map[string][]byte{
customCaKey: []byte(testLeafCertificate),
},
}, nil),
CheckFn: func(t *testing.T, cnf *vcert.Config) {
checkTppCa(t, testLeafCertificate, cnf)
},
expectedErr: false,
},
"if Cloud but getting secret fails, should error": {
iss: cloudIssuer,
secretsLister: generateSecretLister(nil, errors.New("this is a network error")),
@ -213,9 +351,108 @@ func TestConfigForIssuerT(t *testing.T) {
}
}
func TestCaBundleForVcertTPP(t *testing.T) {
baseIssuer := gen.Issuer("non-venafi-issue",
gen.SetIssuerVenafi(cmapi.VenafiIssuer{}),
)
tppIssuer := gen.IssuerFrom(baseIssuer,
gen.SetIssuerVenafi(cmapi.VenafiIssuer{
Zone: zone,
TPP: &cmapi.VenafiTPP{},
}),
)
tppIssuerWithCABundle := gen.IssuerFrom(tppIssuer,
gen.SetIssuerVenafi(cmapi.VenafiIssuer{
TPP: &cmapi.VenafiTPP{
CABundle: []byte(testLeafCertificate),
},
}),
)
tppIssuerWithCABundleSecretRefNoKey := gen.IssuerFrom(tppIssuer,
gen.SetIssuerVenafi(cmapi.VenafiIssuer{
TPP: &cmapi.VenafiTPP{
CABundleSecretRef: &cmmeta.SecretKeySelector{
LocalObjectReference: cmmeta.LocalObjectReference{
Name: customCaSecretName,
},
},
},
}),
)
tppIssuerWithCABundleSecretRef := gen.IssuerFrom(tppIssuer,
gen.SetIssuerVenafi(cmapi.VenafiIssuer{
TPP: &cmapi.VenafiTPP{
CABundleSecretRef: &cmmeta.SecretKeySelector{
Key: customCaKey,
LocalObjectReference: cmmeta.LocalObjectReference{
Name: customCaSecretName,
},
},
},
}),
)
tests := map[string]testConfigForIssuerT{
"if TPP and neither of caBundle nor caBundleSecretRef is specified, CA bundle is not returned": {
iss: tppIssuer,
caBundle: "",
expectedErr: false,
},
"if TPP and caBundle is specified, correct CA bundle from CABundle should be returned": {
iss: tppIssuerWithCABundle,
caBundle: testLeafCertificate,
expectedErr: false,
},
"if TPP and caBundleSecretRef is specified, correct CA bundle from CABundleSecretRef should be returned": {
iss: tppIssuerWithCABundleSecretRef,
caBundle: testLeafCertificate,
secretsLister: generateSecretLister(&corev1.Secret{
Data: map[string][]byte{
customCaKey: []byte(testLeafCertificate),
},
}, nil),
expectedErr: false,
},
"if TPP and caBundleSecretRef is specified without `key`, correct CA bundle from CABundleSecretRef with default key should be returned": {
iss: tppIssuerWithCABundleSecretRefNoKey,
caBundle: testLeafCertificate,
secretsLister: generateSecretLister(&corev1.Secret{
Data: map[string][]byte{
defaultCaKey: []byte(testLeafCertificate),
},
}, nil),
expectedErr: false,
},
"if TPP and caBundleSecretRef is specified, but getting secret fails should error": {
iss: tppIssuerWithCABundleSecretRef,
caBundle: testLeafCertificate,
secretsLister: generateSecretLister(nil, errors.New("this is a network error")),
expectedErr: true,
},
// TODO: write test cases where bad CA is passed.
// above TODO can be ignored if the checks are added to issuer validations per below link
// https://github.com/cert-manager/cert-manager/blob/v1.14.4/internal/apis/certmanager/validation/issuer.go#L354
// eventhough we are not prevalidating, vcert http.Client would anyway fail when using invalid CA
// 2 scenarios with bad CAs:
// "if TPP and caBundle is specified, a bad bundle from CABundle should error"
// "if TPP and caBundleSecretRef is specified, a bad bundle from a CABundleSecretRef should error"
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
test.runTppCaTest(t)
})
}
}
type testConfigForIssuerT struct {
iss cmapi.GenericIssuer
secretsLister internalinformers.SecretLister
caBundle string
expectedErr bool
@ -235,3 +472,21 @@ func (c *testConfigForIssuerT) runTest(t *testing.T) {
c.CheckFn(t, resp)
}
}
func (c *testConfigForIssuerT) runTppCaTest(t *testing.T) {
caResp, err := caBundleForVcertTPP(c.iss.GetSpec().Venafi.TPP, c.secretsLister, "test-namespace")
if err != nil && !c.expectedErr {
t.Errorf("expected to not get an error, but got: %v", err)
}
if err == nil && c.expectedErr {
t.Errorf("expected to get an error but did not get one")
}
if !c.expectedErr {
if c.caBundle != string(caResp) {
t.Errorf("got unexpected CA bundle, exp=%s got=%s",
c.caBundle, caResp)
}
}
}