diff --git a/deploy/charts/cert-manager/crds/certificates.yaml b/deploy/charts/cert-manager/crds/certificates.yaml index afb884b69..4bfe2ad7d 100644 --- a/deploy/charts/cert-manager/crds/certificates.yaml +++ b/deploy/charts/cert-manager/crds/certificates.yaml @@ -144,6 +144,43 @@ spec: description: SecretName is the name of the secret resource to store this secret in type: string + subject: + description: Full X509 name specification (https://golang.org/pkg/crypto/x509/pkix/#Name). + type: object + properties: + countries: + description: Countries to be used on the Certificate. + type: array + items: + type: string + localities: + description: Cities to be used on the Certificate. + type: array + items: + type: string + organizationalUnits: + description: Organizational Units to be used on the Certificate. + type: array + items: + type: string + postalCodes: + description: Postal codes to be used on the Certificate. + type: array + items: + type: string + provinces: + description: State/Provinces to be used on the Certificate. + type: array + items: + type: string + serialNumber: + description: Serial number to be used on the Certificate. + type: string + streetAddresses: + description: Street addresses to be used on the Certificate. + type: array + items: + type: string uriSANs: description: URISANs is a list of URI Subject Alternative Names to be set on this Certificate. diff --git a/deploy/manifests/00-crds.yaml b/deploy/manifests/00-crds.yaml index 21c5565c7..f08752954 100644 --- a/deploy/manifests/00-crds.yaml +++ b/deploy/manifests/00-crds.yaml @@ -335,6 +335,43 @@ spec: description: SecretName is the name of the secret resource to store this secret in type: string + subject: + description: Full X509 name specification (https://golang.org/pkg/crypto/x509/pkix/#Name). + type: object + properties: + countries: + description: Countries to be used on the Certificate. + type: array + items: + type: string + localities: + description: Cities to be used on the Certificate. + type: array + items: + type: string + organizationalUnits: + description: Organizational Units to be used on the Certificate. + type: array + items: + type: string + postalCodes: + description: Postal codes to be used on the Certificate. + type: array + items: + type: string + provinces: + description: State/Provinces to be used on the Certificate. + type: array + items: + type: string + serialNumber: + description: Serial number to be used on the Certificate. + type: string + streetAddresses: + description: Street addresses to be used on the Certificate. + type: array + items: + type: string uriSANs: description: URISANs is a list of URI Subject Alternative Names to be set on this Certificate. diff --git a/pkg/apis/certmanager/v1alpha2/types_certificate.go b/pkg/apis/certmanager/v1alpha2/types_certificate.go index b453dd504..2c5aa9217 100644 --- a/pkg/apis/certmanager/v1alpha2/types_certificate.go +++ b/pkg/apis/certmanager/v1alpha2/types_certificate.go @@ -72,6 +72,10 @@ 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). + // +optional + Subject *X509Subject `json:"subject,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 +149,31 @@ type CertificateSpec struct { KeyEncoding KeyEncoding `json:"keyEncoding,omitempty"` } +// X509Subject Full X509 name specification +type X509Subject struct { + // Countries to be used on the Certificate. + // +optional + Countries []string `json:"countries,omitempty"` + // Organizational Units to be used on the Certificate. + // +optional + OrganizationalUnits []string `json:"organizationalUnits,omitempty"` + // Cities to be used on the Certificate. + // +optional + Localities []string `json:"localities,omitempty"` + // State/Provinces to be used on the Certificate. + // +optional + Provinces []string `json:"provinces,omitempty"` + // Street addresses to be used on the Certificate. + // +optional + StreetAddresses []string `json:"streetAddresses,omitempty"` + // Postal codes to be used on the Certificate. + // +optional + PostalCodes []string `json:"postalCodes,omitempty"` + // Serial number to be used on the Certificate. + // +optional + SerialNumber string `json:"serialNumber,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..1ab249c59 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.Subject != nil { + in, out := &in.Subject, &out.Subject + *out = new(X509Subject) + (*in).DeepCopyInto(*out) + } if in.Organization != nil { in, out := &in.Organization, &out.Organization *out = make([]string, len(*in)) @@ -752,3 +757,49 @@ 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 *X509Subject) DeepCopyInto(out *X509Subject) { + *out = *in + if in.Countries != nil { + in, out := &in.Countries, &out.Countries + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.OrganizationalUnits != nil { + in, out := &in.OrganizationalUnits, &out.OrganizationalUnits + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Localities != nil { + in, out := &in.Localities, &out.Localities + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Provinces != nil { + in, out := &in.Provinces, &out.Provinces + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.StreetAddresses != nil { + in, out := &in.StreetAddresses, &out.StreetAddresses + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PostalCodes != nil { + in, out := &in.PostalCodes, &out.PostalCodes + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new X509Subject. +func (in *X509Subject) DeepCopy() *X509Subject { + if in == nil { + return nil + } + out := new(X509Subject) + 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..92b2cbfb1 100644 --- a/pkg/internal/apis/certmanager/types_certificate.go +++ b/pkg/internal/apis/certmanager/types_certificate.go @@ -59,6 +59,10 @@ const ( // CertificateSpec defines the desired state of Certificate type CertificateSpec struct { + // Full X509 name specification (https://golang.org/pkg/crypto/x509/pkix/#Name). + // +optional + Subject *X509Subject `json:"subject,omitempty"` + // A valid Certificate requires at least one of a CommonName, DNSName, or // URISAN to be valid. @@ -135,6 +139,31 @@ type CertificateSpec struct { KeyEncoding KeyEncoding `json:"keyEncoding,omitempty"` } +// X509Subject Full X509 name specification +type X509Subject struct { + // Countries to be used on the Certificate. + // +optional + Countries []string `json:"countries,omitempty"` + // Organizational Units to be used on the Certificate. + // +optional + OrganizationalUnits []string `json:"organizationalUnits,omitempty"` + // Cities to be used on the Certificate. + // +optional + Localities []string `json:"localities,omitempty"` + // State/Provinces to be used on the Certificate. + // +optional + Provinces []string `json:"provinces,omitempty"` + // Street addresses to be used on the Certificate. + // +optional + StreetAddresses []string `json:"streetAddresses,omitempty"` + // Postal codes to be used on the Certificate. + // +optional + PostalCodes []string `json:"postalCodes,omitempty"` + // Serial number to be used on the Certificate. + // +optional + SerialNumber string `json:"serialNumber,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..bfb860c0f 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.X509Subject)(nil), (*certmanager.X509Subject)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_X509Subject_To_certmanager_X509Subject(a.(*v1alpha2.X509Subject), b.(*certmanager.X509Subject), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*certmanager.X509Subject)(nil), (*v1alpha2.X509Subject)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_certmanager_X509Subject_To_v1alpha2_X509Subject(a.(*certmanager.X509Subject), b.(*v1alpha2.X509Subject), 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.Subject = (*certmanager.X509Subject)(unsafe.Pointer(in.Subject)) 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.Subject = (*v1alpha2.X509Subject)(unsafe.Pointer(in.Subject)) out.CommonName = in.CommonName out.Organization = *(*[]string)(unsafe.Pointer(&in.Organization)) out.Duration = (*v1.Duration)(unsafe.Pointer(in.Duration)) @@ -1057,3 +1069,35 @@ 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_X509Subject_To_certmanager_X509Subject(in *v1alpha2.X509Subject, out *certmanager.X509Subject, s conversion.Scope) error { + out.Countries = *(*[]string)(unsafe.Pointer(&in.Countries)) + out.OrganizationalUnits = *(*[]string)(unsafe.Pointer(&in.OrganizationalUnits)) + out.Localities = *(*[]string)(unsafe.Pointer(&in.Localities)) + out.Provinces = *(*[]string)(unsafe.Pointer(&in.Provinces)) + out.StreetAddresses = *(*[]string)(unsafe.Pointer(&in.StreetAddresses)) + out.PostalCodes = *(*[]string)(unsafe.Pointer(&in.PostalCodes)) + out.SerialNumber = in.SerialNumber + return nil +} + +// Convert_v1alpha2_X509Subject_To_certmanager_X509Subject is an autogenerated conversion function. +func Convert_v1alpha2_X509Subject_To_certmanager_X509Subject(in *v1alpha2.X509Subject, out *certmanager.X509Subject, s conversion.Scope) error { + return autoConvert_v1alpha2_X509Subject_To_certmanager_X509Subject(in, out, s) +} + +func autoConvert_certmanager_X509Subject_To_v1alpha2_X509Subject(in *certmanager.X509Subject, out *v1alpha2.X509Subject, s conversion.Scope) error { + out.Countries = *(*[]string)(unsafe.Pointer(&in.Countries)) + out.OrganizationalUnits = *(*[]string)(unsafe.Pointer(&in.OrganizationalUnits)) + out.Localities = *(*[]string)(unsafe.Pointer(&in.Localities)) + out.Provinces = *(*[]string)(unsafe.Pointer(&in.Provinces)) + out.StreetAddresses = *(*[]string)(unsafe.Pointer(&in.StreetAddresses)) + out.PostalCodes = *(*[]string)(unsafe.Pointer(&in.PostalCodes)) + out.SerialNumber = in.SerialNumber + return nil +} + +// Convert_certmanager_X509Subject_To_v1alpha2_X509Subject is an autogenerated conversion function. +func Convert_certmanager_X509Subject_To_v1alpha2_X509Subject(in *certmanager.X509Subject, out *v1alpha2.X509Subject, s conversion.Scope) error { + return autoConvert_certmanager_X509Subject_To_v1alpha2_X509Subject(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..91fbdd141 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.Subject != nil { + in, out := &in.Subject, &out.Subject + *out = new(X509Subject) + (*in).DeepCopyInto(*out) + } if in.Organization != nil { in, out := &in.Organization, &out.Organization *out = make([]string, len(*in)) @@ -752,3 +757,49 @@ 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 *X509Subject) DeepCopyInto(out *X509Subject) { + *out = *in + if in.Countries != nil { + in, out := &in.Countries, &out.Countries + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.OrganizationalUnits != nil { + in, out := &in.OrganizationalUnits, &out.OrganizationalUnits + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Localities != nil { + in, out := &in.Localities, &out.Localities + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Provinces != nil { + in, out := &in.Provinces, &out.Provinces + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.StreetAddresses != nil { + in, out := &in.StreetAddresses, &out.StreetAddresses + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PostalCodes != nil { + in, out := &in.PostalCodes, &out.PostalCodes + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new X509Subject. +func (in *X509Subject) DeepCopy() *X509Subject { + if in == nil { + return nil + } + out := new(X509Subject) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/util/pki/csr.go b/pkg/util/pki/csr.go index 6256a22bc..7767d42fc 100644 --- a/pkg/util/pki/csr.go +++ b/pkg/util/pki/csr.go @@ -134,6 +134,15 @@ func OrganizationForCertificate(crt *v1alpha2.Certificate) []string { return crt.Spec.Organization } +// SubjectForCertificate will return the Subject from the Certificate resource or an empty one if it is not set +func SubjectForCertificate(crt *v1alpha2.Certificate) v1alpha2.X509Subject { + if crt.Spec.Subject == nil { + return v1alpha2.X509Subject{} + } + + return *crt.Spec.Subject +} + var serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) func BuildKeyUsages(usages []v1alpha2.KeyUsage, isCA bool) (ku x509.KeyUsage, eku []x509.ExtKeyUsage, err error) { @@ -167,6 +176,7 @@ func GenerateCSR(crt *v1alpha2.Certificate) (*x509.CertificateRequest, error) { commonName := crt.Spec.CommonName iPAddresses := IPAddressesForCertificate(crt) organization := OrganizationForCertificate(crt) + subject := SubjectForCertificate(crt) dnsNames, err := DNSNamesForCertificate(crt) if err != nil { @@ -192,8 +202,15 @@ func GenerateCSR(crt *v1alpha2.Certificate) (*x509.CertificateRequest, error) { SignatureAlgorithm: sigAlgo, PublicKeyAlgorithm: pubKeyAlgo, Subject: pkix.Name{ - Organization: organization, - CommonName: commonName, + Country: subject.Countries, + Organization: organization, + OrganizationalUnit: subject.OrganizationalUnits, + Locality: subject.Localities, + Province: subject.Provinces, + StreetAddress: subject.StreetAddresses, + PostalCode: subject.PostalCodes, + SerialNumber: subject.SerialNumber, + CommonName: commonName, }, DNSNames: dnsNames, IPAddresses: iPAddresses, @@ -212,6 +229,7 @@ func GenerateTemplate(crt *v1alpha2.Certificate) (*x509.Certificate, error) { dnsNames := crt.Spec.DNSNames ipAddresses := IPAddressesForCertificate(crt) organization := OrganizationForCertificate(crt) + subject := SubjectForCertificate(crt) keyUsages, extKeyUsages, err := BuildKeyUsages(crt.Spec.Usages, crt.Spec.IsCA) if err != nil { return nil, err @@ -240,8 +258,15 @@ func GenerateTemplate(crt *v1alpha2.Certificate) (*x509.Certificate, error) { PublicKeyAlgorithm: pubKeyAlgo, IsCA: crt.Spec.IsCA, Subject: pkix.Name{ - Organization: organization, - CommonName: commonName, + Country: subject.Countries, + Organization: organization, + OrganizationalUnit: subject.OrganizationalUnits, + Locality: subject.Localities, + Province: subject.Provinces, + StreetAddress: subject.StreetAddresses, + PostalCode: subject.PostalCodes, + SerialNumber: subject.SerialNumber, + CommonName: commonName, }, NotBefore: time.Now(), NotAfter: time.Now().Add(certDuration),