From 28f2d071ec4ae63d6c7fc73028b41f438049fae0 Mon Sep 17 00:00:00 2001 From: JoshVanL Date: Mon, 5 Aug 2019 17:50:15 +0100 Subject: [PATCH] Beginning porting and building new venafi tests Signed-off-by: JoshVanL --- pkg/internal/venafi/connector_test.go | 75 +++++++++++++ pkg/internal/venafi/sign_test.go | 156 ++++++++++++++++++++++++++ pkg/issuer/venafi/issue.go | 16 +-- pkg/issuer/venafi/setup.go | 3 +- pkg/issuer/venafi/venafi.go | 4 + 5 files changed, 244 insertions(+), 10 deletions(-) create mode 100644 pkg/internal/venafi/connector_test.go create mode 100644 pkg/internal/venafi/sign_test.go diff --git a/pkg/internal/venafi/connector_test.go b/pkg/internal/venafi/connector_test.go new file mode 100644 index 000000000..7771cb8e8 --- /dev/null +++ b/pkg/internal/venafi/connector_test.go @@ -0,0 +1,75 @@ +/* +Copyright 2018 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 venafi + +import ( + "github.com/Venafi/vcert/pkg/certificate" + "github.com/Venafi/vcert/pkg/endpoint" + "github.com/Venafi/vcert/pkg/venafi/fake" +) + +type fakeConnector struct { + *fake.Connector + + PingFunc func() error + ReadZoneConfigurationFunc func() (*endpoint.ZoneConfiguration, error) + RetrieveCertificateFunc func(*certificate.Request) (*certificate.PEMCollection, error) + RequestCertificateFunc func(*certificate.Request) (string, error) + RenewCertificateFunc func(*certificate.RenewalRequest) (string, error) +} + +func (f fakeConnector) Default() *fakeConnector { + if f.Connector == nil { + f.Connector = fake.NewConnector(true, nil) + } + return &f +} + +func (f *fakeConnector) Ping() (err error) { + if f.PingFunc != nil { + return f.PingFunc() + } + return f.Connector.Ping() +} + +func (f *fakeConnector) ReadZoneConfiguration() (config *endpoint.ZoneConfiguration, err error) { + if f.ReadZoneConfigurationFunc != nil { + return f.ReadZoneConfigurationFunc() + } + return f.Connector.ReadZoneConfiguration() +} + +func (f *fakeConnector) RetrieveCertificate(req *certificate.Request) (certificates *certificate.PEMCollection, err error) { + if f.RetrieveCertificateFunc != nil { + return f.RetrieveCertificateFunc(req) + } + return f.Connector.RetrieveCertificate(req) +} + +func (f *fakeConnector) RequestCertificate(req *certificate.Request) (requestID string, err error) { + if f.RequestCertificateFunc != nil { + return f.RequestCertificateFunc(req) + } + return f.Connector.RequestCertificate(req) +} + +func (f *fakeConnector) RenewCertificate(req *certificate.RenewalRequest) (requestID string, err error) { + if f.RenewCertificateFunc != nil { + return f.RenewCertificateFunc(req) + } + return f.Connector.RenewCertificate(req) +} diff --git a/pkg/internal/venafi/sign_test.go b/pkg/internal/venafi/sign_test.go new file mode 100644 index 000000000..f9567515b --- /dev/null +++ b/pkg/internal/venafi/sign_test.go @@ -0,0 +1,156 @@ +/* +Copyright 2018 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 venafi + +import ( + "crypto" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/pem" + "testing" + + "github.com/Venafi/vcert/pkg/venafi/fake" + + //"github.com/jetstack/cert-manager/pkg/issuer" + "github.com/jetstack/cert-manager/pkg/util" + "github.com/jetstack/cert-manager/pkg/util/pki" + //"github.com/jetstack/cert-manager/test/unit/gen" +) + +func checkCertificateIssued(t *testing.T, csrPEM []byte, resp []byte, err error) { + if len(resp) == 0 { + t.Errorf("expected IssueResponse to be non-nil") + t.FailNow() + return + } + + if err != nil { + t.Errorf("expected no error to be returned, but got: %v", err) + return + } + + csr, err := pki.DecodeX509CertificateRequestBytes(csrPEM) + if err != nil { + t.Errorf("failed to decode CSR PEM: %s", err) + return + } + + crt, err := pki.DecodeX509CertificateBytes(resp) + if err != nil { + t.Errorf("unable to decode x509 certificate: %v", err) + return + } + + ok, err := pki.PublicKeyMatchesCSR(crt.PublicKey, csr) + if err != nil { + t.Errorf("error checking private key: %v", err) + return + } + if !ok { + t.Errorf("private key does not match certificate") + } + + // validate the common name is correct + expectedCN := csr.Subject.CommonName + if expectedCN != crt.Subject.CommonName { + t.Errorf("expected common name to be %q but it was %q", expectedCN, crt.Subject.CommonName) + } + + // validate the dns names are correct + expectedDNSNames := csr.DNSNames + if !util.EqualUnsorted(crt.DNSNames, expectedDNSNames) { + t.Errorf("expected dns names to be %q but it was %q", expectedDNSNames, crt.DNSNames) + } +} + +func generateCSR(t *testing.T, sk crypto.Signer, commonName string, dnsNames []string) []byte { + asn1Subj, _ := asn1.Marshal(pkix.Name{ + CommonName: commonName, + }.ToRDNSequence()) + template := x509.CertificateRequest{ + RawSubject: asn1Subj, + SignatureAlgorithm: x509.SHA256WithRSA, + DNSNames: dnsNames, + } + + csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, sk) + 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) { + sk, err := pki.GenerateRSAPrivateKey(2048) + if err != nil { + t.Error(err) + t.FailNow() + } + + csrPEM := generateCSR(t, sk, "common-name", []string{ + "foo.example.com", "bar.example.com"}) + + tests := map[string]testT{ + "obtain a certificate with a single dnsname specified": { + csrPEM: csrPEM, + CheckFn: checkCertificateIssued, + expectedErr: false, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + runTest(t, test) + }) + } +} + +type testT struct { + csrPEM []byte + client connector + + expectedErr bool + + CheckFn func(*testing.T, []byte, []byte, error) +} + +func runTest(t *testing.T, test testT) { + if test.client == nil { + test.client = fake.NewConnector(true, nil) + } + + v := &Venafi{ + client: test.client, + } + + resp, err := v.Sign(test.csrPEM) + if err != nil && !test.expectedErr { + t.Errorf("expected to not get an error, but got: %v", err) + } + if err == nil && test.expectedErr { + t.Errorf("expected to get an error but did not get one") + } + + if test.CheckFn != nil { + test.CheckFn(t, test.csrPEM, resp, err) + } +} diff --git a/pkg/issuer/venafi/issue.go b/pkg/issuer/venafi/issue.go index c2f377cb9..36bb28ff1 100644 --- a/pkg/issuer/venafi/issue.go +++ b/pkg/issuer/venafi/issue.go @@ -25,10 +25,7 @@ import ( "github.com/Venafi/vcert/pkg/endpoint" corev1 "k8s.io/api/core/v1" - //"k8s.io/klog" - "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1" - "github.com/jetstack/cert-manager/pkg/internal/venafi" "github.com/jetstack/cert-manager/pkg/issuer" logf "github.com/jetstack/cert-manager/pkg/logs" "github.com/jetstack/cert-manager/pkg/util/pki" @@ -71,30 +68,33 @@ func (v *Venafi) Issue(ctx context.Context, crt *v1alpha1.Certificate) (*issuer. return nil, nil } + dbg.Info("generated new private key") + v.Recorder.Event(crt, corev1.EventTypeNormal, "GenerateKey", "Generated new private key") + pk, err := pki.EncodePKCS8PrivateKey(signeeKey) if err != nil { return nil, err } - dbg.Info("generated new private key") - v.Recorder.Event(crt, corev1.EventTypeNormal, "GenerateKey", "Generated new private key") - // We build a x509.Certificate as the vcert library has support for converting // this into its own internal Certificate Request type. dbg.Info("constructing certificate request template to submit to venafi") csr, err := pki.GenerateCSR(crt) if err != nil { + v.Recorder.Eventf(crt, corev1.EventTypeWarning, "GenerateCSR", "Failed to generate a CSR for the certificate: %v", err) return nil, err } csrPEM, err := pki.EncodeCSR(csr, signeeKey) if err != nil { + v.Recorder.Eventf(crt, corev1.EventTypeWarning, "EncodeCSR", "Failed to PEM encode CSR for the certificate: %v", err) return nil, err } - client, err := venafi.New(v.resourceNamespace, v.secretsLister, v.issuer) + client, err := v.clientBuilder(v.resourceNamespace, v.secretsLister, v.issuer) if err != nil { - return nil, err + v.Recorder.Eventf(v.issuer, corev1.EventTypeWarning, "FailedInit", "Failed to create Venafi client: %v", err) + return nil, fmt.Errorf("error creating Venafi client: %s", err.Error()) } cert, err := client.Sign(csrPEM) diff --git a/pkg/issuer/venafi/setup.go b/pkg/issuer/venafi/setup.go index b7c462535..bb346ed06 100644 --- a/pkg/issuer/venafi/setup.go +++ b/pkg/issuer/venafi/setup.go @@ -25,12 +25,11 @@ import ( apiutil "github.com/jetstack/cert-manager/pkg/api/util" "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1" - "github.com/jetstack/cert-manager/pkg/internal/venafi" ) func (v *Venafi) Setup(ctx context.Context) error { - client, err := venafi.New(v.resourceNamespace, v.secretsLister, v.issuer) + client, err := v.clientBuilder(v.resourceNamespace, v.secretsLister, v.issuer) if err != nil { return err } diff --git a/pkg/issuer/venafi/venafi.go b/pkg/issuer/venafi/venafi.go index 98b772a64..c1df1a009 100644 --- a/pkg/issuer/venafi/venafi.go +++ b/pkg/issuer/venafi/venafi.go @@ -22,6 +22,7 @@ import ( apiutil "github.com/jetstack/cert-manager/pkg/api/util" cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1" "github.com/jetstack/cert-manager/pkg/controller" + "github.com/jetstack/cert-manager/pkg/internal/venafi" "github.com/jetstack/cert-manager/pkg/issuer" ) @@ -43,6 +44,8 @@ type Venafi struct { // For Issuers, this will be the namespace of the Issuer. // For ClusterIssuers, this will be the cluster resource namespace. resourceNamespace string + + clientBuilder venafi.VenafiClientBuilder } func NewVenafi(ctx *controller.Context, issuer cmapi.GenericIssuer) (issuer.Interface, error) { @@ -50,6 +53,7 @@ func NewVenafi(ctx *controller.Context, issuer cmapi.GenericIssuer) (issuer.Inte issuer: issuer, secretsLister: ctx.KubeSharedInformerFactory.Core().V1().Secrets().Lister(), resourceNamespace: ctx.IssuerOptions.ResourceNamespace(issuer), + clientBuilder: venafi.New, }, nil }