From e33e28c4fd05729701765c193a97b52b12c1d80c Mon Sep 17 00:00:00 2001 From: Joshua Mathianas Date: Wed, 15 Jan 2020 12:28:00 -0500 Subject: [PATCH] add backwards compatability for using existing common name or organization if x509name ones are not set add ability to specify more subject attributes for csr Signed-off-by: Joshua Mathianas --- .../cert-manager/crds/certificates.yaml | 40 +++++++++++++ deploy/manifests/00-crds.yaml | 40 +++++++++++++ .../certmanager/v1alpha2/types_certificate.go | 20 +++++++ .../v1alpha2/zz_generated.deepcopy.go | 56 +++++++++++++++++++ .../apis/certmanager/types_certificate.go | 20 +++++++ .../v1alpha2/zz_generated.conversion.go | 48 ++++++++++++++++ .../apis/certmanager/zz_generated.deepcopy.go | 56 +++++++++++++++++++ pkg/util/pki/csr.go | 44 ++++++++++++--- 8 files changed, 316 insertions(+), 8 deletions(-) diff --git a/deploy/charts/cert-manager/crds/certificates.yaml b/deploy/charts/cert-manager/crds/certificates.yaml index afb884b69..18e6b2b99 100644 --- a/deploy/charts/cert-manager/crds/certificates.yaml +++ b/deploy/charts/cert-manager/crds/certificates.yaml @@ -190,6 +190,46 @@ spec: - ocsp signing - microsoft sgc - netscape sgc + x509Name: + description: Full X509 name specification (https://golang.org/pkg/crypto/x509/pkix/#Name). + If specified, overrides any other name elements below. + type: object + properties: + commonName: + type: string + country: + description: Country/Region + type: array + items: + type: string + locality: + description: City + type: array + items: + type: string + organization: + type: array + items: + type: string + organizationalUnit: + type: array + items: + type: string + postalCode: + type: array + items: + type: string + province: + description: State/Province + type: array + items: + type: string + serialNumber: + type: string + streetAddress: + type: array + items: + type: string status: description: CertificateStatus defines the observed state of Certificate type: object diff --git a/deploy/manifests/00-crds.yaml b/deploy/manifests/00-crds.yaml index 21c5565c7..94fac9527 100644 --- a/deploy/manifests/00-crds.yaml +++ b/deploy/manifests/00-crds.yaml @@ -381,6 +381,46 @@ spec: - ocsp signing - microsoft sgc - netscape sgc + x509Name: + description: Full X509 name specification (https://golang.org/pkg/crypto/x509/pkix/#Name). + If specified, overrides any other name elements below. + type: object + properties: + commonName: + type: string + country: + description: Country/Region + type: array + items: + type: string + locality: + description: City + type: array + items: + type: string + organization: + type: array + items: + type: string + organizationalUnit: + type: array + items: + type: string + postalCode: + type: array + items: + type: string + province: + description: State/Province + type: array + items: + type: string + serialNumber: + type: string + streetAddress: + type: array + items: + type: string status: description: CertificateStatus defines the observed state of Certificate type: object diff --git a/pkg/apis/certmanager/v1alpha2/types_certificate.go b/pkg/apis/certmanager/v1alpha2/types_certificate.go index b453dd504..3d3a0519c 100644 --- a/pkg/apis/certmanager/v1alpha2/types_certificate.go +++ b/pkg/apis/certmanager/v1alpha2/types_certificate.go @@ -72,6 +72,11 @@ const ( // A valid Certificate requires at least one of a CommonName, DNSName, or // URISAN to be valid. type CertificateSpec struct { + // Full X509 name specification (https://golang.org/pkg/crypto/x509/pkix/#Name). + // If specified, overrides any other name elements below. + // +optional + X509Name *X509DistinguishedName `json:"x509Name,omitempty"` + // CommonName is a common name to be used on the Certificate. // The CommonName should have a length of 64 characters or fewer to avoid // generating invalid CSRs. @@ -145,6 +150,21 @@ type CertificateSpec struct { KeyEncoding KeyEncoding `json:"keyEncoding,omitempty"` } +type X509DistinguishedName struct { + // Country/Region + Country []string `json:"country,omitempty"` + Organization []string `json:"organization,omitempty"` + OrganizationalUnit []string `json:"organizationalUnit,omitempty"` + // City + Locality []string `json:"locality,omitempty"` + // State/Province + Province []string `json:"province,omitempty"` + StreetAddress []string `json:"streetAddress,omitempty"` + PostalCode []string `json:"postalCode,omitempty"` + SerialNumber string `json:"serialNumber,omitempty"` + CommonName string `json:"commonName,omitempty"` +} + // CertificateStatus defines the observed state of Certificate type CertificateStatus struct { // +optional diff --git a/pkg/apis/certmanager/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/certmanager/v1alpha2/zz_generated.deepcopy.go index 8f58c82d5..0419225ef 100644 --- a/pkg/apis/certmanager/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/apis/certmanager/v1alpha2/zz_generated.deepcopy.go @@ -277,6 +277,11 @@ func (in *CertificateRequestStatus) DeepCopy() *CertificateRequestStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CertificateSpec) DeepCopyInto(out *CertificateSpec) { *out = *in + if in.X509Name != nil { + in, out := &in.X509Name, &out.X509Name + *out = new(X509DistinguishedName) + (*in).DeepCopyInto(*out) + } if in.Organization != nil { in, out := &in.Organization, &out.Organization *out = make([]string, len(*in)) @@ -752,3 +757,54 @@ func (in *VenafiTPP) DeepCopy() *VenafiTPP { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *X509DistinguishedName) DeepCopyInto(out *X509DistinguishedName) { + *out = *in + if in.Country != nil { + in, out := &in.Country, &out.Country + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Organization != nil { + in, out := &in.Organization, &out.Organization + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.OrganizationalUnit != nil { + in, out := &in.OrganizationalUnit, &out.OrganizationalUnit + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Locality != nil { + in, out := &in.Locality, &out.Locality + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Province != nil { + in, out := &in.Province, &out.Province + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.StreetAddress != nil { + in, out := &in.StreetAddress, &out.StreetAddress + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PostalCode != nil { + in, out := &in.PostalCode, &out.PostalCode + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new X509DistinguishedName. +func (in *X509DistinguishedName) DeepCopy() *X509DistinguishedName { + if in == nil { + return nil + } + out := new(X509DistinguishedName) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/internal/apis/certmanager/types_certificate.go b/pkg/internal/apis/certmanager/types_certificate.go index 32f010ee9..0ebfaec8f 100644 --- a/pkg/internal/apis/certmanager/types_certificate.go +++ b/pkg/internal/apis/certmanager/types_certificate.go @@ -59,6 +59,11 @@ const ( // CertificateSpec defines the desired state of Certificate type CertificateSpec struct { + // Full X509 name specification (https://golang.org/pkg/crypto/x509/pkix/#Name). + // If specified, overrides any other name elements below. + // +optional + X509Name *X509DistinguishedName `json:"x509Name,omitempty"` + // A valid Certificate requires at least one of a CommonName, DNSName, or // URISAN to be valid. @@ -135,6 +140,21 @@ type CertificateSpec struct { KeyEncoding KeyEncoding `json:"keyEncoding,omitempty"` } +type X509DistinguishedName struct { + // Country/Region + Country []string `json:"country,omitempty"` + Organization []string `json:"organization,omitempty"` + OrganizationalUnit []string `json:"organizationalUnit,omitempty"` + // City + Locality []string `json:"locality,omitempty"` + // State/Province + Province []string `json:"province,omitempty"` + StreetAddress []string `json:"streetAddress,omitempty"` + PostalCode []string `json:"postalCode,omitempty"` + SerialNumber string `json:"serialNumber,omitempty"` + CommonName string `json:"commonName,omitempty"` +} + // CertificateStatus defines the observed state of Certificate type CertificateStatus struct { // +optional diff --git a/pkg/internal/apis/certmanager/v1alpha2/zz_generated.conversion.go b/pkg/internal/apis/certmanager/v1alpha2/zz_generated.conversion.go index 9f8ce7d17..2072bb9e9 100644 --- a/pkg/internal/apis/certmanager/v1alpha2/zz_generated.conversion.go +++ b/pkg/internal/apis/certmanager/v1alpha2/zz_generated.conversion.go @@ -311,6 +311,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*v1alpha2.X509DistinguishedName)(nil), (*certmanager.X509DistinguishedName)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_X509DistinguishedName_To_certmanager_X509DistinguishedName(a.(*v1alpha2.X509DistinguishedName), b.(*certmanager.X509DistinguishedName), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*certmanager.X509DistinguishedName)(nil), (*v1alpha2.X509DistinguishedName)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_certmanager_X509DistinguishedName_To_v1alpha2_X509DistinguishedName(a.(*certmanager.X509DistinguishedName), b.(*v1alpha2.X509DistinguishedName), scope) + }); err != nil { + return err + } return nil } @@ -559,6 +569,7 @@ func Convert_certmanager_CertificateRequestStatus_To_v1alpha2_CertificateRequest } func autoConvert_v1alpha2_CertificateSpec_To_certmanager_CertificateSpec(in *v1alpha2.CertificateSpec, out *certmanager.CertificateSpec, s conversion.Scope) error { + out.X509Name = (*certmanager.X509DistinguishedName)(unsafe.Pointer(in.X509Name)) out.CommonName = in.CommonName out.Organization = *(*[]string)(unsafe.Pointer(&in.Organization)) out.Duration = (*v1.Duration)(unsafe.Pointer(in.Duration)) @@ -585,6 +596,7 @@ func Convert_v1alpha2_CertificateSpec_To_certmanager_CertificateSpec(in *v1alpha } func autoConvert_certmanager_CertificateSpec_To_v1alpha2_CertificateSpec(in *certmanager.CertificateSpec, out *v1alpha2.CertificateSpec, s conversion.Scope) error { + out.X509Name = (*v1alpha2.X509DistinguishedName)(unsafe.Pointer(in.X509Name)) out.CommonName = in.CommonName out.Organization = *(*[]string)(unsafe.Pointer(&in.Organization)) out.Duration = (*v1.Duration)(unsafe.Pointer(in.Duration)) @@ -1057,3 +1069,39 @@ func autoConvert_certmanager_VenafiTPP_To_v1alpha2_VenafiTPP(in *certmanager.Ven func Convert_certmanager_VenafiTPP_To_v1alpha2_VenafiTPP(in *certmanager.VenafiTPP, out *v1alpha2.VenafiTPP, s conversion.Scope) error { return autoConvert_certmanager_VenafiTPP_To_v1alpha2_VenafiTPP(in, out, s) } + +func autoConvert_v1alpha2_X509DistinguishedName_To_certmanager_X509DistinguishedName(in *v1alpha2.X509DistinguishedName, out *certmanager.X509DistinguishedName, s conversion.Scope) error { + out.Country = *(*[]string)(unsafe.Pointer(&in.Country)) + out.Organization = *(*[]string)(unsafe.Pointer(&in.Organization)) + out.OrganizationalUnit = *(*[]string)(unsafe.Pointer(&in.OrganizationalUnit)) + out.Locality = *(*[]string)(unsafe.Pointer(&in.Locality)) + out.Province = *(*[]string)(unsafe.Pointer(&in.Province)) + out.StreetAddress = *(*[]string)(unsafe.Pointer(&in.StreetAddress)) + out.PostalCode = *(*[]string)(unsafe.Pointer(&in.PostalCode)) + out.SerialNumber = in.SerialNumber + out.CommonName = in.CommonName + return nil +} + +// Convert_v1alpha2_X509DistinguishedName_To_certmanager_X509DistinguishedName is an autogenerated conversion function. +func Convert_v1alpha2_X509DistinguishedName_To_certmanager_X509DistinguishedName(in *v1alpha2.X509DistinguishedName, out *certmanager.X509DistinguishedName, s conversion.Scope) error { + return autoConvert_v1alpha2_X509DistinguishedName_To_certmanager_X509DistinguishedName(in, out, s) +} + +func autoConvert_certmanager_X509DistinguishedName_To_v1alpha2_X509DistinguishedName(in *certmanager.X509DistinguishedName, out *v1alpha2.X509DistinguishedName, s conversion.Scope) error { + out.Country = *(*[]string)(unsafe.Pointer(&in.Country)) + out.Organization = *(*[]string)(unsafe.Pointer(&in.Organization)) + out.OrganizationalUnit = *(*[]string)(unsafe.Pointer(&in.OrganizationalUnit)) + out.Locality = *(*[]string)(unsafe.Pointer(&in.Locality)) + out.Province = *(*[]string)(unsafe.Pointer(&in.Province)) + out.StreetAddress = *(*[]string)(unsafe.Pointer(&in.StreetAddress)) + out.PostalCode = *(*[]string)(unsafe.Pointer(&in.PostalCode)) + out.SerialNumber = in.SerialNumber + out.CommonName = in.CommonName + return nil +} + +// Convert_certmanager_X509DistinguishedName_To_v1alpha2_X509DistinguishedName is an autogenerated conversion function. +func Convert_certmanager_X509DistinguishedName_To_v1alpha2_X509DistinguishedName(in *certmanager.X509DistinguishedName, out *v1alpha2.X509DistinguishedName, s conversion.Scope) error { + return autoConvert_certmanager_X509DistinguishedName_To_v1alpha2_X509DistinguishedName(in, out, s) +} diff --git a/pkg/internal/apis/certmanager/zz_generated.deepcopy.go b/pkg/internal/apis/certmanager/zz_generated.deepcopy.go index 6255768fc..c065bbc56 100644 --- a/pkg/internal/apis/certmanager/zz_generated.deepcopy.go +++ b/pkg/internal/apis/certmanager/zz_generated.deepcopy.go @@ -277,6 +277,11 @@ func (in *CertificateRequestStatus) DeepCopy() *CertificateRequestStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CertificateSpec) DeepCopyInto(out *CertificateSpec) { *out = *in + if in.X509Name != nil { + in, out := &in.X509Name, &out.X509Name + *out = new(X509DistinguishedName) + (*in).DeepCopyInto(*out) + } if in.Organization != nil { in, out := &in.Organization, &out.Organization *out = make([]string, len(*in)) @@ -752,3 +757,54 @@ func (in *VenafiTPP) DeepCopy() *VenafiTPP { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *X509DistinguishedName) DeepCopyInto(out *X509DistinguishedName) { + *out = *in + if in.Country != nil { + in, out := &in.Country, &out.Country + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Organization != nil { + in, out := &in.Organization, &out.Organization + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.OrganizationalUnit != nil { + in, out := &in.OrganizationalUnit, &out.OrganizationalUnit + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Locality != nil { + in, out := &in.Locality, &out.Locality + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Province != nil { + in, out := &in.Province, &out.Province + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.StreetAddress != nil { + in, out := &in.StreetAddress, &out.StreetAddress + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PostalCode != nil { + in, out := &in.PostalCode, &out.PostalCode + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new X509DistinguishedName. +func (in *X509DistinguishedName) DeepCopy() *X509DistinguishedName { + if in == nil { + return nil + } + out := new(X509DistinguishedName) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/util/pki/csr.go b/pkg/util/pki/csr.go index 6256a22bc..05dae3dad 100644 --- a/pkg/util/pki/csr.go +++ b/pkg/util/pki/csr.go @@ -124,14 +124,28 @@ Outer: const defaultOrganization = "cert-manager" // OrganizationForCertificate will return the Organization to set for the -// Certificate resource. +// Certificate resource or if X509Name.Organization is set will return that // If an Organization is not specifically set, a default will be used. func OrganizationForCertificate(crt *v1alpha2.Certificate) []string { - if len(crt.Spec.Organization) == 0 { + if len(crt.Spec.Organization) == 0 && len(crt.Spec.X509Name.Organization) == 0 { return []string{defaultOrganization} } - return crt.Spec.Organization + if len(crt.Spec.X509Name.Organization) == 0 { + return crt.Spec.Organization + } + + return crt.Spec.X509Name.Organization +} + +// CommonNameForCertificate will return the CommonName to set for the +// Certificate resource or if X509Name.CommonName is set will return that +func CommonNameForCertificate(crt *v1alpha2.Certificate) string { + if len(crt.Spec.X509Name.CommonName) == 0 { + return crt.Spec.CommonName + } + + return crt.Spec.X509Name.CommonName } var serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) @@ -164,9 +178,16 @@ func BuildKeyUsages(usages []v1alpha2.KeyUsage, isCA bool) (ku x509.KeyUsage, ek // The CSR will not be signed, and should be passed to either EncodeCSR or // to the x509.CreateCertificateRequest function. func GenerateCSR(crt *v1alpha2.Certificate) (*x509.CertificateRequest, error) { - commonName := crt.Spec.CommonName - iPAddresses := IPAddressesForCertificate(crt) + country := crt.Spec.X509Name.Country organization := OrganizationForCertificate(crt) + organizationalUnit := crt.Spec.X509Name.OrganizationalUnit + locality := crt.Spec.X509Name.Locality + province := crt.Spec.X509Name.Province + streetAddress := crt.Spec.X509Name.StreetAddress + postalCode := crt.Spec.X509Name.PostalCode + serialNumber := crt.Spec.X509Name.SerialNumber + commonName := CommonNameForCertificate(crt) + iPAddresses := IPAddressesForCertificate(crt) dnsNames, err := DNSNamesForCertificate(crt) if err != nil { @@ -192,8 +213,15 @@ func GenerateCSR(crt *v1alpha2.Certificate) (*x509.CertificateRequest, error) { SignatureAlgorithm: sigAlgo, PublicKeyAlgorithm: pubKeyAlgo, Subject: pkix.Name{ - Organization: organization, - CommonName: commonName, + Country: country, + Organization: organization, + OrganizationalUnit: organizationalUnit, + Locality: locality, + Province: province, + StreetAddress: streetAddress, + PostalCode: postalCode, + SerialNumber: serialNumber, + CommonName: commonName, }, DNSNames: dnsNames, IPAddresses: iPAddresses, @@ -208,7 +236,7 @@ func GenerateCSR(crt *v1alpha2.Certificate) (*x509.CertificateRequest, error) { // generated by GenerateCSR. // The PublicKey field must be populated by the caller. func GenerateTemplate(crt *v1alpha2.Certificate) (*x509.Certificate, error) { - commonName := crt.Spec.CommonName + commonName := CommonNameForCertificate(crt) dnsNames := crt.Spec.DNSNames ipAddresses := IPAddressesForCertificate(crt) organization := OrganizationForCertificate(crt)