Add x509 v3 CA Issuers Extension

Signed-off-by: Jeremy Campbell <jeremy.campbell@okta.com>
This commit is contained in:
Jeremy Campbell 2023-11-16 12:45:16 -06:00
parent b0ed333413
commit dc876fef16
No known key found for this signature in database
GPG Key ID: D08763E26DEC0586
22 changed files with 136 additions and 0 deletions

View File

@ -1081,6 +1081,11 @@ spec:
type: array
items:
type: string
issuingCertificateURLs:
description: IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details. As an example, such a URL might be "http://ca.domain.com/ca.crt".
type: array
items:
type: string
ocspServers:
description: The OCSP server list is an X.509 v3 extension that defines a list of URLs of OCSP responders. The OCSP responders can be queried for the revocation status of an issued certificate. If not set, the certificate will be issued with no OCSP servers set. For example, an OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org".
type: array

View File

@ -1081,6 +1081,11 @@ spec:
type: array
items:
type: string
issuingCertificateURLs:
description: IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details. As an example, such a URL might be "http://ca.domain.com/ca.crt".
type: array
items:
type: string
ocspServers:
description: The OCSP server list is an X.509 v3 extension that defines a list of URLs of OCSP responders. The OCSP responders can be queried for the revocation status of an issued certificate. If not set, the certificate will be issued with no OCSP servers set. For example, an OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org".
type: array

View File

@ -292,6 +292,12 @@ type CAIssuer struct {
// certificate will be issued with no OCSP servers set. For example, an
// OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org".
OCSPServers []string
// IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates
// it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details.
// As an example, such a URL might be "http://ca.domain.com/ca.crt".
// +optional
IssuingCertificateURLs []string `json:"issuingCertificateURLs,omitempty"`
}
// IssuerStatus contains status information about an Issuer

View File

@ -401,6 +401,7 @@ func autoConvert_v1_CAIssuer_To_certmanager_CAIssuer(in *v1.CAIssuer, out *certm
out.SecretName = in.SecretName
out.CRLDistributionPoints = *(*[]string)(unsafe.Pointer(&in.CRLDistributionPoints))
out.OCSPServers = *(*[]string)(unsafe.Pointer(&in.OCSPServers))
out.IssuingCertificateURLs = *(*[]string)(unsafe.Pointer(&in.IssuingCertificateURLs))
return nil
}
@ -413,6 +414,7 @@ func autoConvert_certmanager_CAIssuer_To_v1_CAIssuer(in *certmanager.CAIssuer, o
out.SecretName = in.SecretName
out.CRLDistributionPoints = *(*[]string)(unsafe.Pointer(&in.CRLDistributionPoints))
out.OCSPServers = *(*[]string)(unsafe.Pointer(&in.OCSPServers))
out.IssuingCertificateURLs = *(*[]string)(unsafe.Pointer(&in.IssuingCertificateURLs))
return nil
}

View File

@ -309,6 +309,12 @@ type CAIssuer struct {
// OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org".
// +optional
OCSPServers []string `json:"ocspServers,omitempty"`
// IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates
// it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details.
// As an example, such a URL might be "http://ca.domain.com/ca.crt".
// +optional
IssuingCertificateURLs []string `json:"issuingCertificateURLs,omitempty"`
}
// IssuerStatus contains status information about an Issuer

View File

@ -399,6 +399,7 @@ func autoConvert_v1alpha2_CAIssuer_To_certmanager_CAIssuer(in *CAIssuer, out *ce
out.SecretName = in.SecretName
out.CRLDistributionPoints = *(*[]string)(unsafe.Pointer(&in.CRLDistributionPoints))
out.OCSPServers = *(*[]string)(unsafe.Pointer(&in.OCSPServers))
out.IssuingCertificateURLs = *(*[]string)(unsafe.Pointer(&in.IssuingCertificateURLs))
return nil
}
@ -411,6 +412,7 @@ func autoConvert_certmanager_CAIssuer_To_v1alpha2_CAIssuer(in *certmanager.CAIss
out.SecretName = in.SecretName
out.CRLDistributionPoints = *(*[]string)(unsafe.Pointer(&in.CRLDistributionPoints))
out.OCSPServers = *(*[]string)(unsafe.Pointer(&in.OCSPServers))
out.IssuingCertificateURLs = *(*[]string)(unsafe.Pointer(&in.IssuingCertificateURLs))
return nil
}

View File

@ -41,6 +41,11 @@ func (in *CAIssuer) DeepCopyInto(out *CAIssuer) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IssuingCertificateURLs != nil {
in, out := &in.IssuingCertificateURLs, &out.IssuingCertificateURLs
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}

View File

@ -309,6 +309,12 @@ type CAIssuer struct {
// OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org".
// +optional
OCSPServers []string `json:"ocspServers,omitempty"`
// IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates
// it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details.
// As an example, such a URL might be "http://ca.domain.com/ca.crt".
// +optional
IssuingCertificateURLs []string `json:"issuingCertificateURLs,omitempty"`
}
// IssuerStatus contains status information about an Issuer

View File

@ -399,6 +399,7 @@ func autoConvert_v1alpha3_CAIssuer_To_certmanager_CAIssuer(in *CAIssuer, out *ce
out.SecretName = in.SecretName
out.CRLDistributionPoints = *(*[]string)(unsafe.Pointer(&in.CRLDistributionPoints))
out.OCSPServers = *(*[]string)(unsafe.Pointer(&in.OCSPServers))
out.IssuingCertificateURLs = *(*[]string)(unsafe.Pointer(&in.IssuingCertificateURLs))
return nil
}
@ -411,6 +412,7 @@ func autoConvert_certmanager_CAIssuer_To_v1alpha3_CAIssuer(in *certmanager.CAIss
out.SecretName = in.SecretName
out.CRLDistributionPoints = *(*[]string)(unsafe.Pointer(&in.CRLDistributionPoints))
out.OCSPServers = *(*[]string)(unsafe.Pointer(&in.OCSPServers))
out.IssuingCertificateURLs = *(*[]string)(unsafe.Pointer(&in.IssuingCertificateURLs))
return nil
}

View File

@ -41,6 +41,11 @@ func (in *CAIssuer) DeepCopyInto(out *CAIssuer) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IssuingCertificateURLs != nil {
in, out := &in.IssuingCertificateURLs, &out.IssuingCertificateURLs
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}

View File

@ -311,6 +311,12 @@ type CAIssuer struct {
// OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org".
// +optional
OCSPServers []string `json:"ocspServers,omitempty"`
// IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates
// it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details.
// As an example, such a URL might be "http://ca.domain.com/ca.crt".
// +optional
IssuingCertificateURLs []string `json:"issuingCertificateURLs,omitempty"`
}
// IssuerStatus contains status information about an Issuer

View File

@ -399,6 +399,7 @@ func autoConvert_v1beta1_CAIssuer_To_certmanager_CAIssuer(in *CAIssuer, out *cer
out.SecretName = in.SecretName
out.CRLDistributionPoints = *(*[]string)(unsafe.Pointer(&in.CRLDistributionPoints))
out.OCSPServers = *(*[]string)(unsafe.Pointer(&in.OCSPServers))
out.IssuingCertificateURLs = *(*[]string)(unsafe.Pointer(&in.IssuingCertificateURLs))
return nil
}
@ -411,6 +412,7 @@ func autoConvert_certmanager_CAIssuer_To_v1beta1_CAIssuer(in *certmanager.CAIssu
out.SecretName = in.SecretName
out.CRLDistributionPoints = *(*[]string)(unsafe.Pointer(&in.CRLDistributionPoints))
out.OCSPServers = *(*[]string)(unsafe.Pointer(&in.OCSPServers))
out.IssuingCertificateURLs = *(*[]string)(unsafe.Pointer(&in.IssuingCertificateURLs))
return nil
}

View File

@ -41,6 +41,11 @@ func (in *CAIssuer) DeepCopyInto(out *CAIssuer) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IssuingCertificateURLs != nil {
in, out := &in.IssuingCertificateURLs, &out.IssuingCertificateURLs
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}

View File

@ -244,6 +244,11 @@ func ValidateCAIssuerConfig(iss *certmanager.CAIssuer, fldPath *field.Path) fiel
el = append(el, field.Invalid(fldPath.Child("ocspServer").Index(i), ocspURL, "must be a valid URL, e.g., http://ocsp.int-x3.letsencrypt.org"))
}
}
for i, issuerURL := range iss.IssuingCertificateURLs {
if issuerURL == "" {
el = append(el, field.Invalid(fldPath.Child("issuingCertificateURLs").Index(i), issuerURL, "must be a valid URL"))
}
}
return el
}

View File

@ -737,6 +737,30 @@ func TestValidateIssuerSpec(t *testing.T) {
field.Invalid(fldPath.Child("ca", "ocspServer").Index(0), "", `must be a valid URL, e.g., http://ocsp.int-x3.letsencrypt.org`),
},
},
"valid IssuingCertificateURLs": {
spec: &cmapi.IssuerSpec{
IssuerConfig: cmapi.IssuerConfig{
CA: &cmapi.CAIssuer{
SecretName: "valid",
IssuingCertificateURLs: []string{"http://ca.example.com/ca.crt"},
},
},
},
errs: []*field.Error{},
},
"invalid IssuingCertificateURLs": {
spec: &cmapi.IssuerSpec{
IssuerConfig: cmapi.IssuerConfig{
CA: &cmapi.CAIssuer{
SecretName: "valid",
IssuingCertificateURLs: []string{""},
},
},
},
errs: []*field.Error{
field.Invalid(fldPath.Child("ca", "issuingCertificateURLs").Index(0), "", `must be a valid URL`),
},
},
}
for n, s := range scenarios {
t.Run(n, func(t *testing.T) {

View File

@ -41,6 +41,11 @@ func (in *CAIssuer) DeepCopyInto(out *CAIssuer) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IssuingCertificateURLs != nil {
in, out := &in.IssuingCertificateURLs, &out.IssuingCertificateURLs
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}

View File

@ -314,6 +314,12 @@ type CAIssuer struct {
// OCSP server URL could be "http://ocsp.int-x3.letsencrypt.org".
// +optional
OCSPServers []string `json:"ocspServers,omitempty"`
// IssuingCertificateURLs is a list of URLs which this issuer should embed into certificates
// it creates. See https://www.rfc-editor.org/rfc/rfc5280#section-4.2.2.1 for more details.
// As an example, such a URL might be "http://ca.domain.com/ca.crt".
// +optional
IssuingCertificateURLs []string `json:"issuingCertificateURLs,omitempty"`
}
// IssuerStatus contains status information about an Issuer

View File

@ -41,6 +41,11 @@ func (in *CAIssuer) DeepCopyInto(out *CAIssuer) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IssuingCertificateURLs != nil {
in, out := &in.IssuingCertificateURLs, &out.IssuingCertificateURLs
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}

View File

@ -128,6 +128,7 @@ func (c *CA) Sign(ctx context.Context, cr *cmapi.CertificateRequest, issuerObj c
template.CRLDistributionPoints = issuerObj.GetSpec().CA.CRLDistributionPoints
template.OCSPServer = issuerObj.GetSpec().CA.OCSPServers
template.IssuingCertificateURL = issuerObj.GetSpec().CA.IssuingCertificateURLs
bundle, err := c.signingFn(caCerts, caKey, template)
if err != nil {

View File

@ -552,6 +552,24 @@ func TestCA_Sign(t *testing.T) {
assert.Equal(t, []string{"http://ocsp-v3.example.org"}, got.OCSPServer)
},
},
"when the Issuer has IssuingCertificateURL set, it should appear on the signed ca": {
givenCASecret: gen.SecretFrom(gen.Secret("secret-1"), gen.SetSecretNamespace("default"), gen.SetSecretData(secretDataFor(t, rootPK, rootCert))),
givenCAIssuer: gen.Issuer("issuer-1", gen.SetIssuerCA(cmapi.CAIssuer{
SecretName: "secret-1",
IssuingCertificateURLs: []string{"http://ca.letsencrypt.org/ca.crt"},
})),
givenCR: gen.CertificateRequest("cr-1",
gen.SetCertificateRequestCSR(testCSR),
gen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
Name: "issuer-1",
Group: certmanager.GroupName,
Kind: "Issuer",
}),
),
assertSignedCert: func(t *testing.T, got *x509.Certificate) {
assert.Equal(t, []string{"http://ca.letsencrypt.org/ca.crt"}, got.IssuingCertificateURL)
},
},
"when the Issuer has crlDistributionPoints set, it should appear on the signed ca ": {
givenCASecret: gen.SecretFrom(gen.Secret("secret-1"), gen.SetSecretNamespace("default"), gen.SetSecretData(secretDataFor(t, rootPK, rootCert))),
givenCAIssuer: gen.Issuer("issuer-1", gen.SetIssuerCA(cmapi.CAIssuer{

View File

@ -129,6 +129,7 @@ func (c *CA) Sign(ctx context.Context, csr *certificatesv1.CertificateSigningReq
template.CRLDistributionPoints = issuerObj.GetSpec().CA.CRLDistributionPoints
template.OCSPServer = issuerObj.GetSpec().CA.OCSPServers
template.IssuingCertificateURL = issuerObj.GetSpec().CA.IssuingCertificateURLs
bundle, err := c.signingFn(caCerts, caKey, template)
if err != nil {

View File

@ -705,6 +705,20 @@ func TestCA_Sign(t *testing.T) {
assert.Equal(t, []string{"http://ocsp-v3.example.org"}, got.OCSPServer)
},
},
"when the Issuer has issuingCertificateURLs set, it should appear on the signed ca": {
givenCASecret: gen.SecretFrom(gen.Secret("secret-1"), gen.SetSecretNamespace("default"), gen.SetSecretData(secretDataFor(t, rootPK, rootCert))),
givenCAIssuer: gen.Issuer("issuer-1", gen.SetIssuerCA(cmapi.CAIssuer{
SecretName: "secret-1",
IssuingCertificateURLs: []string{"http://ca.example.com/ca.crt"},
})),
givenCSR: gen.CertificateSigningRequest("cr-1",
gen.SetCertificateSigningRequestRequest(testCSR),
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/"+gen.DefaultTestNamespace+".issuer-1"),
),
assertSignedCert: func(t *testing.T, got *x509.Certificate) {
assert.Equal(t, []string{"http://ca.example.com/ca.crt"}, got.IssuingCertificateURL)
},
},
"when the Issuer has crlDistributionPoints set, it should appear on the signed ca ": {
givenCASecret: gen.SecretFrom(gen.Secret("secret-1"), gen.SetSecretNamespace("default"), gen.SetSecretData(secretDataFor(t, rootPK, rootCert))),
givenCAIssuer: gen.Issuer("issuer-1", gen.SetIssuerCA(cmapi.CAIssuer{