diff --git a/pkg/apis/certmanager/v1alpha1/types.go b/pkg/apis/certmanager/v1alpha1/types.go index ebcf11414..72fc9f01f 100644 --- a/pkg/apis/certmanager/v1alpha1/types.go +++ b/pkg/apis/certmanager/v1alpha1/types.go @@ -17,12 +17,13 @@ limitations under the License. package v1alpha1 const ( - AltNamesAnnotationKey = "certmanager.k8s.io/alt-names" - IPSANAnnotationKey = "certmanager.k8s.io/ip-sans" - CommonNameAnnotationKey = "certmanager.k8s.io/common-name" - IssuerNameAnnotationKey = "certmanager.k8s.io/issuer-name" - IssuerKindAnnotationKey = "certmanager.k8s.io/issuer-kind" - CertificateNameKey = "certmanager.k8s.io/certificate-name" + AltNamesAnnotationKey = "certmanager.k8s.io/alt-names" + IPSANAnnotationKey = "certmanager.k8s.io/ip-sans" + CommonNameAnnotationKey = "certmanager.k8s.io/common-name" + IssuerNameAnnotationKey = "certmanager.k8s.io/issuer-name" + IssuerKindAnnotationKey = "certmanager.k8s.io/issuer-kind" + CertificateNameKey = "certmanager.k8s.io/certificate-name" + CRPrivateKeyAnnotationKey = "certmanager.k8s.io/private-key-secret-name" ) // ConditionStatus represents a condition's status. diff --git a/pkg/controller/certificaterequests/BUILD.bazel b/pkg/controller/certificaterequests/BUILD.bazel index ca47a54af..9f2795c2f 100644 --- a/pkg/controller/certificaterequests/BUILD.bazel +++ b/pkg/controller/certificaterequests/BUILD.bazel @@ -33,25 +33,6 @@ go_library( ], ) -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [ - ":package-srcs", - "//pkg/controller/certificaterequests/ca:all-srcs", - "//pkg/controller/certificaterequests/fake:all-srcs", - "//pkg/controller/certificaterequests/util:all-srcs", - ], - tags = ["automanaged"], - visibility = ["//visibility:public"], -) - go_test( name = "go_default_test", srcs = ["sync_test.go"], @@ -71,3 +52,23 @@ go_test( "//vendor/k8s.io/utils/clock/testing:go_default_library", ], ) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [ + ":package-srcs", + "//pkg/controller/certificaterequests/ca:all-srcs", + "//pkg/controller/certificaterequests/fake:all-srcs", + "//pkg/controller/certificaterequests/selfsigned:all-srcs", + "//pkg/controller/certificaterequests/util:all-srcs", + ], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/controller/certificaterequests/selfsigned/BUILD.bazel b/pkg/controller/certificaterequests/selfsigned/BUILD.bazel new file mode 100644 index 000000000..54e731769 --- /dev/null +++ b/pkg/controller/certificaterequests/selfsigned/BUILD.bazel @@ -0,0 +1,54 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["selfsigned.go"], + importpath = "github.com/jetstack/cert-manager/pkg/controller/certificaterequests/selfsigned", + visibility = ["//visibility:public"], + deps = [ + "//pkg/api/util:go_default_library", + "//pkg/apis/certmanager/v1alpha1:go_default_library", + "//pkg/controller:go_default_library", + "//pkg/controller/certificaterequests:go_default_library", + "//pkg/issuer:go_default_library", + "//pkg/logs:go_default_library", + "//pkg/util/kube:go_default_library", + "//pkg/util/pki:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/client-go/listers/core/v1:go_default_library", + "//vendor/k8s.io/client-go/tools/record:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "selfsigned_test.go", + "util_test.go", + ], + embed = [":go_default_library"], + deps = [ + "//pkg/apis/certmanager/v1alpha1:go_default_library", + "//pkg/controller/test:go_default_library", + "//pkg/issuer:go_default_library", + "//pkg/util/pki:go_default_library", + "//test/unit/gen:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/controller/certificaterequests/selfsigned/selfsigned.go b/pkg/controller/certificaterequests/selfsigned/selfsigned.go new file mode 100644 index 000000000..b289bb493 --- /dev/null +++ b/pkg/controller/certificaterequests/selfsigned/selfsigned.go @@ -0,0 +1,124 @@ +/* +Copyright 2019 The Jetstack cert-manager contributors. + 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 selfsigned + +import ( + "context" + "errors" + "fmt" + + corev1 "k8s.io/api/core/v1" + corelisters "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/record" + + apiutil "github.com/jetstack/cert-manager/pkg/api/util" + "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1" + controllerpkg "github.com/jetstack/cert-manager/pkg/controller" + "github.com/jetstack/cert-manager/pkg/controller/certificaterequests" + "github.com/jetstack/cert-manager/pkg/issuer" + logf "github.com/jetstack/cert-manager/pkg/logs" + "github.com/jetstack/cert-manager/pkg/util/kube" + "github.com/jetstack/cert-manager/pkg/util/pki" +) + +const ( + CRControllerName = "certificaterequests-issuer-selfsigned" +) + +var ( + errorNoAnnotation = fmt.Errorf("self signed issuer requires '%s' annotation set to secret name holding the private key", + v1alpha1.CRPrivateKeyAnnotationKey) +) + +type SelfSigned struct { + // used to record Events about resources to the API + recorder record.EventRecorder + + secretsLister corelisters.SecretLister +} + +func init() { + // create certificate request controller for selfsigned issuer + controllerpkg.Register(CRControllerName, func(ctx *controllerpkg.Context) (controllerpkg.Interface, error) { + selfsigned := NewSelfSigned(ctx) + controller := certificaterequests.New(apiutil.IssuerCA, selfsigned) + + c, err := controllerpkg.New(ctx, CRControllerName, controller) + if err != nil { + return nil, err + } + + return c.Run, nil + }) +} + +func NewSelfSigned(ctx *controllerpkg.Context) *SelfSigned { + return &SelfSigned{ + recorder: ctx.Recorder, + secretsLister: ctx.KubeSharedInformerFactory.Core().V1().Secrets().Lister(), + } +} + +func (s *SelfSigned) Sign(ctx context.Context, cr *v1alpha1.CertificateRequest) (*issuer.IssueResponse, error) { + log := logf.FromContext(ctx, "sign") + + skRef, ok := cr.ObjectMeta.Annotations[v1alpha1.CRPrivateKeyAnnotationKey] + if !ok || skRef == "" { + return nil, errorNoAnnotation + } + + sk, err := kube.SecretTLSKey(ctx, s.secretsLister, cr.Namespace, skRef) + if err != nil { + return nil, fmt.Errorf("failed to get private key %s referenced in the annotation '%s': %s", + skRef, v1alpha1.CRPrivateKeyAnnotationKey, err) + } + + template, err := pki.GenerateTemplateFromCertificateRequest(cr) + if err != nil { + log.Error(err, "error generating certificate template") + s.recorder.Eventf(cr, corev1.EventTypeWarning, "ErrorSigning", "Error generating certificate template: %v", err) + return nil, err + } + + // extract the public component of the key + pk, err := pki.PublicKeyForPrivateKey(sk) + if err != nil { + log.Error(err, "failed to get public key from private key") + return nil, err + } + + ok, err = pki.PublicKeyMatchesPublicKey(pk, template.PublicKey) + if err != nil { + log.Error(err, "failed to verify CSR is signed by referenced private key") + return nil, err + } + + if !ok { + return nil, errors.New("CSR not signed by referenced private key") + } + + // sign and encode the certificate + certPem, _, err := pki.SignCertificate(template, template, pk, sk) + if err != nil { + s.recorder.Eventf(cr, corev1.EventTypeWarning, "ErrorSigning", "Error signing certificate: %v", err) + return nil, err + } + + log.Info("self signed certificate issued") + + return &issuer.IssueResponse{ + Certificate: certPem, + CA: certPem, + }, nil +} diff --git a/pkg/controller/certificaterequests/selfsigned/selfsigned_test.go b/pkg/controller/certificaterequests/selfsigned/selfsigned_test.go new file mode 100644 index 000000000..434abf5b7 --- /dev/null +++ b/pkg/controller/certificaterequests/selfsigned/selfsigned_test.go @@ -0,0 +1,353 @@ +/* +Copyright 2019 The Jetstack cert-manager contributors. + 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 selfsigned + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/pem" + "fmt" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1" + testpkg "github.com/jetstack/cert-manager/pkg/controller/test" + "github.com/jetstack/cert-manager/pkg/issuer" + "github.com/jetstack/cert-manager/pkg/util/pki" + "github.com/jetstack/cert-manager/test/unit/gen" +) + +func generateCSR(t *testing.T, secretKey crypto.Signer, alg x509.SignatureAlgorithm) []byte { + asn1Subj, _ := asn1.Marshal(pkix.Name{ + CommonName: "test", + }.ToRDNSequence()) + template := x509.CertificateRequest{ + RawSubject: asn1Subj, + SignatureAlgorithm: alg, + } + + csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, secretKey) + if err != nil { + t.Error(err) + t.FailNow() + } + + csr := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes}) + + return csr +} + +func TestSign(t *testing.T) { + skRSA, err := pki.GenerateRSAPrivateKey(2048) + if err != nil { + t.Errorf("failed to generate RSA private key: %s", err) + t.FailNow() + } + + skEC, err := pki.GenerateECPrivateKey(256) + if err != nil { + t.Errorf("failed to generate ECDA private key: %s", err) + t.FailNow() + } + + skAnotherRSA, err := pki.GenerateRSAPrivateKey(2048) + if err != nil { + t.Errorf("failed to generate RSA private key: %s", err) + t.FailNow() + } + + csrBytes := generateCSR(t, skRSA, x509.SHA256WithRSA) + csrECBytes := generateCSR(t, skEC, x509.ECDSAWithSHA256) + + skRSAPEMBytes := pki.EncodePKCS1PrivateKey(skRSA) + skAnotherRSAPEMBytes := pki.EncodePKCS1PrivateKey(skAnotherRSA) + skECPEMBytes, err := pki.EncodeECPrivateKey(skEC) + if err != nil { + t.Error(err) + t.FailNow() + } + + rsaKeySecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-rsa-key", + Namespace: gen.DefaultTestNamespace, + }, + Data: map[string][]byte{ + corev1.TLSPrivateKeyKey: skRSAPEMBytes, + }, + } + anotherRSAKeySecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-another-rsa-key", + Namespace: gen.DefaultTestNamespace, + }, + Data: map[string][]byte{ + corev1.TLSPrivateKeyKey: skAnotherRSAPEMBytes, + }, + } + ecKeySecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ecdsa-key", + Namespace: gen.DefaultTestNamespace, + }, + Data: map[string][]byte{ + corev1.TLSPrivateKeyKey: skECPEMBytes, + }, + } + badKeySecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-bad-key", + Namespace: gen.DefaultTestNamespace, + }, + Data: map[string][]byte{ + corev1.TLSPrivateKeyKey: []byte("this is a bad key"), + }, + } + + tests := map[string]selfsignedFixture{ + "a CertificateRequest with no certmanager.k8s.io/selfsigned-private-key annotation errors": { + Issuer: gen.Issuer("selfsigned-issuer", + gen.SetIssuerSelfSigned(v1alpha1.SelfSignedIssuer{}), + ), + // no data in annotation + CertificateRequest: gen.CertificateRequest("test-cr"), + Builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{}, + CertManagerObjects: []runtime.Object{}, + }, + Err: true, + CheckFn: func(t *testing.T, s *selfsignedFixture, args ...interface{}) { + err := args[2].(error) + if err == nil || err != errorNoAnnotation { + t.Errorf("unexpected error, exp='%s' got='%+v'", errorNoAnnotation, err) + } + }, + }, + "a CertificateRequest with a certmanager.k8s.io/private-key-secret-name annotation but empty string should error": { + Issuer: gen.Issuer("selfsigned-issuer", + gen.SetIssuerSelfSigned(v1alpha1.SelfSignedIssuer{}), + ), + // no data in annotation + CertificateRequest: gen.CertificateRequest("test-cr", + gen.AddCertificateRequestAnnotations(map[string]string{ + v1alpha1.CRPrivateKeyAnnotationKey: "", + }), + ), + Builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{}, + CertManagerObjects: []runtime.Object{}, + }, + Err: true, + CheckFn: func(t *testing.T, s *selfsignedFixture, args ...interface{}) { + err := args[2].(error) + if err == nil || err != errorNoAnnotation { + t.Errorf("unexpected error, exp='%s' got='%+v'", errorNoAnnotation, err) + } + }, + }, + "a CertificateRequest with a certmanager.k8s.io/private-key-secret-name annotation but the referenced secret doesn't exist should error": { + Issuer: gen.Issuer("selfsigned-issuer", + gen.SetIssuerSelfSigned(v1alpha1.SelfSignedIssuer{}), + ), + CertificateRequest: gen.CertificateRequest("test-cr", + gen.AddCertificateRequestAnnotations(map[string]string{ + v1alpha1.CRPrivateKeyAnnotationKey: "a-non-existent-secret", + }), + ), + Builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{}, + CertManagerObjects: []runtime.Object{}, + }, + Err: true, + CheckFn: func(t *testing.T, s *selfsignedFixture, args ...interface{}) { + notFoundError := `failed to get private key a-non-existent-secret referenced in the annotation 'certmanager.k8s.io/private-key-secret-name': secret "a-non-existent-secret" not found` + err := args[2].(error) + if err == nil || err.Error() != notFoundError { + t.Errorf("unexpected error, exp='%s' got='%+v'", notFoundError, err) + } + }, + }, + "a CertificateRequest with a bad CSR should error": { + Issuer: gen.Issuer("selfsigned-issuer", + gen.SetIssuerSelfSigned(v1alpha1.SelfSignedIssuer{}), + ), + CertificateRequest: gen.CertificateRequest("test-cr", + gen.AddCertificateRequestAnnotations(map[string]string{ + v1alpha1.CRPrivateKeyAnnotationKey: "test-rsa-key", + }), + gen.SetCertificateRequestCSR([]byte("this is a bad CSR")), + ), + Builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{rsaKeySecret}, + CertManagerObjects: []runtime.Object{}, + }, + Err: true, + CheckFn: func(t *testing.T, s *selfsignedFixture, args ...interface{}) { + parseCSRError := fmt.Sprintf("failed to decode csr from certificate request resource %s/test-cr", gen.DefaultTestNamespace) + err := args[2].(error) + if err == nil || err.Error() != parseCSRError { + t.Errorf("unexpected error, exp='%s' got='%+v'", parseCSRError, err) + } + }, + }, + "a CertificateRequest referencing a bad key should error": { + Issuer: gen.Issuer("selfsigned-issuer", + gen.SetIssuerSelfSigned(v1alpha1.SelfSignedIssuer{}), + ), + CertificateRequest: gen.CertificateRequest("test-cr", + gen.AddCertificateRequestAnnotations(map[string]string{ + v1alpha1.CRPrivateKeyAnnotationKey: "test-bad-key", + }), + gen.SetCertificateRequestCSR(csrBytes), + ), + Builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{badKeySecret}, + CertManagerObjects: []runtime.Object{}, + }, + Err: true, + CheckFn: func(t *testing.T, s *selfsignedFixture, args ...interface{}) { + badKeyError := "failed to get private key test-bad-key referenced in the annotation 'certmanager.k8s.io/private-key-secret-name': error decoding private key PEM block" + err := args[2].(error) + if err == nil || err.Error() != badKeyError { + t.Errorf("unexpected error, exp='%s' got='%+v'", badKeyError, err) + } + }, + }, + "a CertificateRequest referencing a key which did not sign the CSR should error": { + Issuer: gen.Issuer("selfsigned-issuer", + gen.SetIssuerSelfSigned(v1alpha1.SelfSignedIssuer{}), + ), + CertificateRequest: gen.CertificateRequest("test-cr", + gen.AddCertificateRequestAnnotations(map[string]string{ + v1alpha1.CRPrivateKeyAnnotationKey: "test-another-rsa-key", + }), + gen.SetCertificateRequestCSR(csrBytes), + ), + Builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{anotherRSAKeySecret}, + CertManagerObjects: []runtime.Object{}, + }, + Err: true, + CheckFn: func(t *testing.T, s *selfsignedFixture, args ...interface{}) { + badKeyError := "CSR not signed by referenced private key" + err := args[2].(error) + if err == nil || err.Error() != badKeyError { + t.Errorf("unexpected error, exp='%s' got='%+v'", badKeyError, err) + } + }, + }, + "a valid RSA key should sign a self signed certificate": { + Issuer: gen.Issuer("selfsigned-issuer", + gen.SetIssuerSelfSigned(v1alpha1.SelfSignedIssuer{}), + ), + CertificateRequest: gen.CertificateRequest("test-cr", + gen.AddCertificateRequestAnnotations(map[string]string{ + v1alpha1.CRPrivateKeyAnnotationKey: "test-rsa-key", + }), + gen.SetCertificateRequestCSR(csrBytes), + ), + Builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{rsaKeySecret}, + CertManagerObjects: []runtime.Object{}, + }, + Err: false, + CheckFn: func(t *testing.T, s *selfsignedFixture, args ...interface{}) { + resp := args[1].(*issuer.IssueResponse) + + // CA and cert should be the same. + if !bytes.Equal(resp.CA, resp.Certificate) { + t.Errorf("expected CA and cert to be the same but got:\nCA: %s\nCert: %s", + resp.CA, resp.Certificate) + } + + // No private key should be returned. + if len(resp.PrivateKey) > 0 { + t.Errorf("expected to private key returned but got: %s", + resp.PrivateKey) + } + }, + }, + "a valid ECDSA key should sign a self signed certificate": { + Issuer: gen.Issuer("selfsigned-issuer", + gen.SetIssuerSelfSigned(v1alpha1.SelfSignedIssuer{}), + ), + CertificateRequest: gen.CertificateRequest("test-cr", + gen.AddCertificateRequestAnnotations(map[string]string{ + v1alpha1.CRPrivateKeyAnnotationKey: "test-ecdsa-key", + }), + gen.SetCertificateRequestCSR(csrECBytes), + ), + Builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{ecKeySecret}, + CertManagerObjects: []runtime.Object{}, + }, + Err: false, + CheckFn: func(t *testing.T, s *selfsignedFixture, args ...interface{}) { + resp := args[1].(*issuer.IssueResponse) + if resp == nil { + t.Errorf("expected a response but got: %+v", + args[1]) + return + } + + // CA and cert should be the same. + if !bytes.Equal(resp.CA, resp.Certificate) { + t.Errorf("expected CA and cert to be the same but got:\nCA: %s\nCert: %s", + resp.CA, resp.Certificate) + } + + // No private key should be returned. + if len(resp.PrivateKey) > 0 { + t.Errorf("expected to private key returned but got: %s", + resp.PrivateKey) + } + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + if test.Builder == nil { + test.Builder = &testpkg.Builder{} + } + + test.Setup(t) + crCopy := test.CertificateRequest.DeepCopy() + resp, err := test.SelfSigned.Sign(test.Ctx, crCopy) + if err != nil && !test.Err { + t.Errorf("Expected function to not error, but got: %v", err) + } + if err == nil && test.Err { + t.Errorf("Expected function to get an error, but got: %v", err) + } + + if test.Err && resp != nil { + t.Errorf("unexpected response, exp=nil got='%+v'", resp) + } + + if !test.Err && resp == nil { + t.Errorf("expected response but got nil") + } + + test.Finish(t, crCopy, resp, err) + }) + } +} diff --git a/pkg/controller/certificaterequests/selfsigned/util_test.go b/pkg/controller/certificaterequests/selfsigned/util_test.go new file mode 100644 index 000000000..182754437 --- /dev/null +++ b/pkg/controller/certificaterequests/selfsigned/util_test.go @@ -0,0 +1,92 @@ +/* +Copyright 2019 The Jetstack cert-manager contributors. + 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 selfsigned + +import ( + "context" + "testing" + + "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1" + "github.com/jetstack/cert-manager/pkg/controller/test" +) + +type selfsignedFixture struct { + SelfSigned *SelfSigned + *test.Builder + + Issuer v1alpha1.GenericIssuer + Certificate *v1alpha1.Certificate + CertificateRequest *v1alpha1.CertificateRequest + + PreFn func(*testing.T, *selfsignedFixture) + CheckFn func(*testing.T, *selfsignedFixture, ...interface{}) + Err bool + + Ctx context.Context +} + +func (s *selfsignedFixture) Setup(t *testing.T) { + if s.Issuer == nil { + s.Issuer = &v1alpha1.Issuer{ + Spec: v1alpha1.IssuerSpec{ + IssuerConfig: v1alpha1.IssuerConfig{ + SelfSigned: &v1alpha1.SelfSignedIssuer{}, + }, + }, + } + } + if s.Ctx == nil { + s.Ctx = context.Background() + } + if s.Builder == nil { + // TODO: set default IssuerOptions + // defaultTestAcmeClusterResourceNamespace, + // defaultTestSolverImage, + // default dns01 nameservers + // ambient credentials settings + s.Builder = &test.Builder{} + } + if s.Builder.T == nil { + s.Builder.T = t + } + s.SelfSigned = s.buildFakeSelfSigned(s.Builder, s.Issuer) + if s.PreFn != nil { + s.PreFn(t, s) + s.Builder.Sync() + } +} + +func (s *selfsignedFixture) Finish(t *testing.T, args ...interface{}) { + defer s.Builder.Stop() + if err := s.Builder.AllReactorsCalled(); err != nil { + t.Errorf("Not all expected reactors were called: %v", err) + } + if err := s.Builder.AllActionsExecuted(); err != nil { + t.Errorf(err.Error()) + } + + // resync listers before running checks + s.Builder.Sync() + // run custom checks + if s.CheckFn != nil { + s.CheckFn(t, s, args...) + } +} + +func (s *selfsignedFixture) buildFakeSelfSigned(b *test.Builder, issuer v1alpha1.GenericIssuer) *SelfSigned { + b.Start() + selfsignedStruct := NewSelfSigned(b.Context) + b.Sync() + return selfsignedStruct +} diff --git a/pkg/util/pki/generate.go b/pkg/util/pki/generate.go index fccca4e6c..ef8c91952 100644 --- a/pkg/util/pki/generate.go +++ b/pkg/util/pki/generate.go @@ -210,9 +210,13 @@ func PublicKeyMatchesCertificate(check crypto.PublicKey, crt *x509.Certificate) // It will return an error if either of the passed parameters are of an // unrecognised type (i.e. non RSA/ECDSA) func PublicKeyMatchesCSR(check crypto.PublicKey, csr *x509.CertificateRequest) (bool, error) { - switch pub := csr.PublicKey.(type) { + return PublicKeyMatchesPublicKey(check, csr.PublicKey) +} + +func PublicKeyMatchesPublicKey(a, b crypto.PublicKey) (bool, error) { + switch pub := a.(type) { case *rsa.PublicKey: - rsaCheck, ok := check.(*rsa.PublicKey) + rsaCheck, ok := b.(*rsa.PublicKey) if !ok { return false, nil } @@ -221,7 +225,7 @@ func PublicKeyMatchesCSR(check crypto.PublicKey, csr *x509.CertificateRequest) ( } return true, nil case *ecdsa.PublicKey: - ecdsaCheck, ok := check.(*ecdsa.PublicKey) + ecdsaCheck, ok := b.(*ecdsa.PublicKey) if !ok { return false, nil } @@ -230,6 +234,6 @@ func PublicKeyMatchesCSR(check crypto.PublicKey, csr *x509.CertificateRequest) ( } return true, nil default: - return false, fmt.Errorf("unrecognised Certificate public key type") + return false, fmt.Errorf("unrecognised public key type") } }