Merge pull request #5587 from SpectralHiss/SpectralHiss/add-fields-to-subject-rdn
Add support for required LDAP (rfc4514) RDNs in LiteralSubject
This commit is contained in:
commit
43e13bfa0d
@ -944,6 +944,18 @@ func Test_validateLiteralSubject(t *testing.T) {
|
||||
},
|
||||
a: someAdmissionRequest,
|
||||
},
|
||||
"valid with a `literalSubject` containing CN with special characters, multiple DC and well-known rfc4514 and rfc5280 RDN OIDs": {
|
||||
featureEnabled: true,
|
||||
cfg: &internalcmapi.Certificate{
|
||||
Spec: internalcmapi.CertificateSpec{
|
||||
Subject: &internalcmapi.X509Subject{SerialNumber: "1"},
|
||||
LiteralSubject: "CN=James \\\"Jim\\\" Smith\\, III,DC=dc,DC=net,UID=jamessmith,STREET=La Rambla,L=Barcelona,C=Spain,O=Acme,OU=IT,OU=Admins",
|
||||
SecretName: "abc",
|
||||
IssuerRef: validIssuerRef,
|
||||
},
|
||||
},
|
||||
a: someAdmissionRequest,
|
||||
},
|
||||
"invalid with a `literalSubject` without CN and no dnsNames, ipAddresses, or emailAddress": {
|
||||
featureEnabled: true,
|
||||
cfg: &internalcmapi.Certificate{
|
||||
|
||||
@ -73,7 +73,7 @@ nodes=20
|
||||
flake_attempts=1
|
||||
ginkgo_skip=
|
||||
ginkgo_focus=
|
||||
feature_gates=AdditionalCertificateOutputFormats=true,ExperimentalCertificateSigningRequestControllers=true,ExperimentalGatewayAPISupport=true
|
||||
feature_gates=AdditionalCertificateOutputFormats=true,ExperimentalCertificateSigningRequestControllers=true,ExperimentalGatewayAPISupport=true,LiteralCertificateSubject=true
|
||||
artifacts="./$BINDIR/artifacts"
|
||||
help() {
|
||||
cat <<EOF | color ""
|
||||
|
||||
@ -373,6 +373,8 @@ var OIDConstants = struct {
|
||||
Locality []int
|
||||
Province []int
|
||||
StreetAddress []int
|
||||
DomainComponent []int
|
||||
UniqueIdentifier []int
|
||||
}{
|
||||
Country: []int{2, 5, 4, 6},
|
||||
Organization: []int{2, 5, 4, 10},
|
||||
@ -382,10 +384,13 @@ var OIDConstants = struct {
|
||||
Locality: []int{2, 5, 4, 7},
|
||||
Province: []int{2, 5, 4, 8},
|
||||
StreetAddress: []int{2, 5, 4, 9},
|
||||
DomainComponent: []int{0, 9, 2342, 19200300, 100, 1, 25},
|
||||
UniqueIdentifier: []int{0, 9, 2342, 19200300, 100, 1, 1},
|
||||
}
|
||||
|
||||
// Copied from pkix.attributeTypeNames and inverted. (Sadly it is private.)
|
||||
// Source: https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/crypto/x509/pkix/pkix.go;l=26
|
||||
// Added RDNs identifier to support rfc4514 LDAP certificates, cf https://github.com/cert-manager/cert-manager/issues/5582
|
||||
var attributeTypeNames = map[string][]int{
|
||||
"C": OIDConstants.Country,
|
||||
"O": OIDConstants.Organization,
|
||||
@ -395,6 +400,8 @@ var attributeTypeNames = map[string][]int{
|
||||
"L": OIDConstants.Locality,
|
||||
"ST": OIDConstants.Province,
|
||||
"STREET": OIDConstants.StreetAddress,
|
||||
"DC": OIDConstants.DomainComponent,
|
||||
"UID": OIDConstants.UniqueIdentifier,
|
||||
}
|
||||
|
||||
func ParseSubjectStringToRdnSequence(subject string) (pkix.RDNSequence, error) {
|
||||
|
||||
127
test/e2e/suite/certificates/literalsubjectrdns.go
Normal file
127
test/e2e/suite/certificates/literalsubjectrdns.go
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
Copyright 2020 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"time"
|
||||
|
||||
"github.com/cert-manager/cert-manager/internal/webhook/feature"
|
||||
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
|
||||
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
|
||||
utilfeature "github.com/cert-manager/cert-manager/pkg/util/feature"
|
||||
"github.com/cert-manager/cert-manager/test/e2e/framework"
|
||||
e2eutil "github.com/cert-manager/cert-manager/test/e2e/util"
|
||||
"github.com/cert-manager/cert-manager/test/unit/gen"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
//. "github.com/onsi/gomega/gstruct"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var _ = framework.CertManagerDescribe("literalsubject rdn parsing", func() {
|
||||
|
||||
const (
|
||||
testName = "test-literalsubject-rdn-parsing"
|
||||
issuerName = "certificate-literalsubject-rdns"
|
||||
secretName = testName
|
||||
)
|
||||
|
||||
f := framework.NewDefaultFramework("certificate-literalsubject-rdns")
|
||||
|
||||
createCertificate := func(f *framework.Framework, literalSubject string) (*cmapi.Certificate, error) {
|
||||
framework.RequireFeatureGate(f, utilfeature.DefaultFeatureGate, feature.LiteralCertificateSubject)
|
||||
crt := &cmapi.Certificate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: testName + "-",
|
||||
Namespace: f.Namespace.Name,
|
||||
},
|
||||
Spec: cmapi.CertificateSpec{
|
||||
SecretName: secretName,
|
||||
PrivateKey: &cmapi.CertificatePrivateKey{RotationPolicy: cmapi.RotationPolicyAlways},
|
||||
IssuerRef: cmmeta.ObjectReference{
|
||||
Name: issuerName, Kind: "Issuer", Group: "cert-manager.io",
|
||||
},
|
||||
LiteralSubject: literalSubject,
|
||||
},
|
||||
}
|
||||
|
||||
By("creating Certificate with LiteralSubject")
|
||||
return f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name).Create(context.Background(), crt, metav1.CreateOptions{})
|
||||
|
||||
}
|
||||
|
||||
BeforeEach(func() {
|
||||
By("creating a self-signing issuer")
|
||||
issuer := gen.Issuer(issuerName,
|
||||
gen.SetIssuerNamespace(f.Namespace.Name),
|
||||
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}))
|
||||
Expect(f.CRClient.Create(context.Background(), issuer)).To(Succeed())
|
||||
|
||||
By("Waiting for Issuer to become Ready")
|
||||
err := e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
|
||||
issuerName, cmapi.IssuerCondition{Type: cmapi.IssuerConditionReady, Status: cmmeta.ConditionTrue})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Delete(context.Background(), issuerName, metav1.DeleteOptions{})).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
// The parsed RDNSequence should be in REVERSE order as RDNs in String format are expected to be written in reverse order.
|
||||
// Meaning, a string of "CN=Foo,OU=Bar,O=Baz" actually should have "O=Baz" as the first element in the RDNSequence.
|
||||
It("Should create a certificate with all the supplied RDNs as subject names in reverse string order, including DC and UID", func() {
|
||||
crt, err := createCertificate(f, "CN=James \\\"Jim\\\" Smith\\, III,UID=jamessmith,SERIALNUMBER=1234512345,OU=Admins,OU=IT,DC=net,DC=dc,O=Acme,STREET=La Rambla,L=Barcelona,C=Spain")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = f.Helper().WaitForCertificateReadyAndDoneIssuing(crt, time.Minute*2)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to wait for Certificate to become Ready")
|
||||
|
||||
secret, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), secretName, metav1.GetOptions{})
|
||||
Expect(err).To(BeNil())
|
||||
Expect(secret.Data).To(HaveKey("tls.crt"))
|
||||
crtPEM := secret.Data["tls.crt"]
|
||||
pemBlock, _ := pem.Decode(crtPEM)
|
||||
cert, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
Expect(cert.Subject.Names).To(Equal([]pkix.AttributeTypeAndValue{
|
||||
{Type: asn1.ObjectIdentifier{2, 5, 4, 6}, Value: "Spain"},
|
||||
{Type: asn1.ObjectIdentifier{2, 5, 4, 7}, Value: "Barcelona"},
|
||||
{Type: asn1.ObjectIdentifier{2, 5, 4, 9}, Value: "La Rambla"},
|
||||
{Type: asn1.ObjectIdentifier{2, 5, 4, 10}, Value: "Acme"},
|
||||
{Type: asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 25}, Value: "dc"},
|
||||
{Type: asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 25}, Value: "net"},
|
||||
{Type: asn1.ObjectIdentifier{2, 5, 4, 11}, Value: "IT"},
|
||||
{Type: asn1.ObjectIdentifier{2, 5, 4, 11}, Value: "Admins"},
|
||||
{Type: asn1.ObjectIdentifier{2, 5, 4, 5}, Value: "1234512345"},
|
||||
{Type: asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 1}, Value: "jamessmith"},
|
||||
{Type: asn1.ObjectIdentifier{2, 5, 4, 3}, Value: "James \"Jim\" Smith, III"},
|
||||
}))
|
||||
})
|
||||
|
||||
It("Should not allow unknown RDN component", func() {
|
||||
_, err := createCertificate(f, "UNKNOWN=blah")
|
||||
Expect(err).NotTo(BeNil())
|
||||
Expect(err.Error()).To(ContainSubstring("Literal subject contains unrecognized key with value [blah]"))
|
||||
})
|
||||
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user