From 0af5dbe334cb5cda75b9a1b1f7808f0069f48ce2 Mon Sep 17 00:00:00 2001 From: cornfeedhobo Date: Wed, 21 Dec 2022 22:07:40 -0600 Subject: [PATCH] Add attribute support for certificate subject Fixes #128 --- pkg/apis/v1alpha1/types.go | 12 +++++- pkg/apis/validation/validation_test.go | 14 ++++++ pkg/requestgen/generator.go | 60 +++++++++++++++++++------- pkg/requestgen/generator_test.go | 35 +++++++++++++++ 4 files changed, 104 insertions(+), 17 deletions(-) diff --git a/pkg/apis/v1alpha1/types.go b/pkg/apis/v1alpha1/types.go index 492786ba..81013380 100644 --- a/pkg/apis/v1alpha1/types.go +++ b/pkg/apis/v1alpha1/types.go @@ -21,7 +21,17 @@ const ( IssuerKindKey = "csi.cert-manager.io/issuer-kind" IssuerGroupKey = "csi.cert-manager.io/issuer-group" - CommonNameKey = "csi.cert-manager.io/common-name" + LiteralSubjectKey = "csi.cert-manager.io/literal-subject" + CommonNameKey = "csi.cert-manager.io/common-name" + OrganizationsKey = "csi.cert-manager.io/organizations" + OrganizationalUnitsKey = "csi.cert-manager.io/organizationalunits" + CountriesKey = "csi.cert-manager.io/countries" + ProvincesKey = "csi.cert-manager.io/provinces" + LocalitiesKey = "csi.cert-manager.io/localities" + StreetAddressesKey = "csi.cert-manager.io/streetaddresses" + PostalCodesKey = "csi.cert-manager.io/postalcodes" + SerialNumberKey = "csi.cert-manager.io/serialnumber" + DNSNamesKey = "csi.cert-manager.io/dns-names" IPSANsKey = "csi.cert-manager.io/ip-sans" URISANsKey = "csi.cert-manager.io/uri-sans" diff --git a/pkg/apis/validation/validation_test.go b/pkg/apis/validation/validation_test.go index ba705f91..bbb0f688 100644 --- a/pkg/apis/validation/validation_test.go +++ b/pkg/apis/validation/validation_test.go @@ -32,6 +32,8 @@ func Test_ValidateAttributes(t *testing.T) { expError error } + var literalSubject = "CN=${POD_NAME}.${POD_NAMESPACE}.svc.cluster.local,OU=0:${POD_NAME}\\;1:${POD_NAMESPACE}\\;2:my-region\\;4:unittest,O=foo.bar.com" + tests := map[string]struct { attr map[string]string expErr field.ErrorList @@ -206,6 +208,18 @@ func Test_ValidateAttributes(t *testing.T) { field.Invalid(field.NewPath("volumeAttributes", "csi.cert-manager.io/privatekey-file"), "/foobar", "filename must not include '/'"), }, }, + "correct literal-subject should not error": { + attr: map[string]string{ + csiapi.IssuerNameKey: "test-issuer", + csiapi.LiteralSubjectKey: literalSubject, + csiapi.CAFileKey: "ca.crt", + csiapi.CertFileKey: "crt.tls", + csiapi.KeyFileKey: "key.tls", + csiapi.DNSNamesKey: "foo.bar.com", + csiapi.KeyEncodingKey: "PKCS8", + }, + expErr: nil, + }, } for name, test := range tests { diff --git a/pkg/requestgen/generator.go b/pkg/requestgen/generator.go index 8ae241ac..9c17219c 100644 --- a/pkg/requestgen/generator.go +++ b/pkg/requestgen/generator.go @@ -29,6 +29,8 @@ import ( cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" + cmpki "github.com/cert-manager/cert-manager/pkg/util/pki" + "github.com/cert-manager/csi-lib/manager" "github.com/cert-manager/csi-lib/metadata" @@ -56,32 +58,58 @@ func RequestForMetadata(meta metadata.Metadata) (*manager.CertificateRequestBund } } - commonName, err := expand(meta, attrs[csiapi.CommonNameKey]) - if err != nil { - return nil, fmt.Errorf("%q: %w", csiapi.CommonNameKey, err) + var request = &x509.CertificateRequest{} + if lSubjStr, ok := attrs[csiapi.LiteralSubjectKey]; ok && len(lSubjStr) > 0 { + lSubjStr, err = expand(meta, lSubjStr) + if err != nil { + return nil, fmt.Errorf("%q: %w", csiapi.LiteralSubjectKey, err) + } + request.RawSubject, err = cmpki.ParseSubjectStringToRawDerBytes(lSubjStr) + if err != nil { + return nil, fmt.Errorf("%q: %w", csiapi.LiteralSubjectKey, err) + } + } else { + request.Subject = pkix.Name{} + request.Subject.CommonName, err = expand(meta, attrs[csiapi.CommonNameKey]) + if err != nil { + return nil, fmt.Errorf("%q: %w", csiapi.CommonNameKey, err) + } + if len(attrs[csiapi.SerialNumberKey]) > 0 { + request.Subject.SerialNumber = attrs[csiapi.SerialNumberKey] + } + for k, v := range map[*[]string]string{ + &request.Subject.Organization: csiapi.OrganizationsKey, + &request.Subject.OrganizationalUnit: csiapi.OrganizationalUnitsKey, + &request.Subject.Country: csiapi.CountriesKey, + &request.Subject.Province: csiapi.ProvincesKey, + &request.Subject.Locality: csiapi.LocalitiesKey, + &request.Subject.StreetAddress: csiapi.StreetAddressesKey, + &request.Subject.PostalCode: csiapi.PostalCodesKey, + } { + if len(attrs[v]) > 0 { + var e, err = expand(meta, attrs[v]) + if err != nil { + return nil, fmt.Errorf("%q: %w", v, err) + } + *k = strings.Split(e, ",") + } + } } - dns, err := parseDNSNames(meta, attrs[csiapi.DNSNamesKey]) + request.DNSNames, err = parseDNSNames(meta, attrs[csiapi.DNSNamesKey]) if err != nil { return nil, fmt.Errorf("%q: %w", csiapi.DNSNamesKey, err) } - uris, err := parseURIs(meta, attrs[csiapi.URISANsKey]) + request.IPAddresses, err = parseIPAddresses(attrs[csiapi.IPSANsKey]) if err != nil { - return nil, fmt.Errorf("%q: %w", csiapi.URISANsKey, err) + return nil, fmt.Errorf("%q: %w", csiapi.IPSANsKey, err) } - ips, err := parseIPAddresses(attrs[csiapi.IPSANsKey]) + request.URIs, err = parseURIs(meta, attrs[csiapi.URISANsKey]) if err != nil { - return nil, fmt.Errorf("%q: %w", csiapi.IPSANsKey, err) + return nil, fmt.Errorf("%q: %w", csiapi.URISANsKey, err) } return &manager.CertificateRequestBundle{ - Request: &x509.CertificateRequest{ - Subject: pkix.Name{ - CommonName: commonName, - }, - DNSNames: dns, - IPAddresses: ips, - URIs: uris, - }, + Request: request, IsCA: strings.ToLower(attrs[csiapi.IsCAKey]) == "true", Namespace: attrs[csiapi.K8sVolumeContextKeyPodNamespace], Duration: duration, diff --git a/pkg/requestgen/generator_test.go b/pkg/requestgen/generator_test.go index 5b3463bf..c61f8443 100644 --- a/pkg/requestgen/generator_test.go +++ b/pkg/requestgen/generator_test.go @@ -22,6 +22,7 @@ import ( "errors" "net" "net/url" + "strings" "testing" "time" @@ -31,6 +32,9 @@ import ( cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" + cmpki "github.com/cert-manager/cert-manager/pkg/util/pki" + + csiapi "github.com/cert-manager/csi-driver/pkg/apis/v1alpha1" ) func Test_RequestForMetadata(t *testing.T) { @@ -52,6 +56,12 @@ func Test_RequestForMetadata(t *testing.T) { return puri } + var literalSubject = "CN=my-pod.my-namespace.svc.cluster.local,OU=0:my-pod\\;1:my-namespace\\;2:my-region\\;4:unittest,O=foo.bar.com" + var rawLiteralSubject, err = cmpki.ParseSubjectStringToRawDerBytes(literalSubject) + if err != nil { + assert.NoError(t, err) + } + tests := map[string]struct { meta metadata.Metadata expRequest *manager.CertificateRequestBundle @@ -174,6 +184,31 @@ func Test_RequestForMetadata(t *testing.T) { }, expErr: false, }, + "a metadata with literal subject set should be returned": { + meta: baseMetadataWith(metadata.Metadata{VolumeContext: map[string]string{ + csiapi.IssuerNameKey: "my-issuer", + csiapi.LiteralSubjectKey: literalSubject, + }}), + expRequest: &manager.CertificateRequestBundle{ + Request: &x509.CertificateRequest{RawSubject: rawLiteralSubject}, + Usages: cmapi.DefaultKeyUsages(), + Namespace: "my-namespace", + IssuerRef: cmmeta.ObjectReference{ + Name: "my-issuer", + Kind: "Issuer", + Group: "cert-manager.io", + }, + Duration: cmapi.DefaultCertificateDuration, + }, + expErr: false, + }, + "a metadata with incorrect literal subject set should error": { + meta: baseMetadataWith(metadata.Metadata{VolumeContext: map[string]string{ + csiapi.IssuerNameKey: "my-issuer", + csiapi.LiteralSubjectKey: strings.Replace(literalSubject, ";", "&", -1), + }}), + expErr: true, + }, } for name, test := range tests {