diff --git a/go.mod b/go.mod index af8617624..077e65906 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/Azure/go-autorest/autorest/adal v0.5.0 github.com/Azure/go-autorest/autorest/to v0.3.0 github.com/Azure/go-autorest/autorest/validation v0.2.0 // indirect - github.com/Venafi/vcert v0.0.0-20190613103158-62139eb19b25 + github.com/Venafi/vcert v0.0.0-20200207035730-5a915d73be5d github.com/aws/aws-sdk-go v1.24.1 github.com/cloudflare/cloudflare-go v0.8.5 github.com/cpu/goacmedns v0.0.0-20180701200144-565ecf2a84df @@ -39,7 +39,7 @@ require ( golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 google.golang.org/api v0.4.0 - gopkg.in/ini.v1 v1.42.0 // indirect + gopkg.in/ini.v1 v1.52.0 // indirect k8s.io/api v0.17.0 k8s.io/apiextensions-apiserver v0.17.0 k8s.io/apimachinery v0.17.0 diff --git a/go.sum b/go.sum index d430a1bcc..1adc0b80b 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,8 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Venafi/vcert v0.0.0-20190613103158-62139eb19b25 h1:Vy4WHFF6SULSubt7vuyJRJ5AqwjJXzJQWdF37VcsyGo= -github.com/Venafi/vcert v0.0.0-20190613103158-62139eb19b25/go.mod h1:3sXw16DKVded/kLVDma2veqEUQC7O37h98ims7cIvN4= +github.com/Venafi/vcert v0.0.0-20200207035730-5a915d73be5d h1:oFQa1HzZX7Kcnjnosx9wEdVi8ADTDvs+2Tz96HIXrEU= +github.com/Venafi/vcert v0.0.0-20200207035730-5a915d73be5d/go.mod h1:5T4bFPhcgGXbdz8nVVRuE2gXSRDlZVL+9T5CwZZ3Yk4= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -645,8 +645,8 @@ gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.38.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= -gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.52.0 h1:j+Lt/M1oPPejkniCg1TkWE2J3Eh1oZTsHSXzMTzUXn4= +gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= diff --git a/hack/build/repos.bzl b/hack/build/repos.bzl index 1e2f63955..fa6e53252 100644 --- a/hack/build/repos.bzl +++ b/hack/build/repos.bzl @@ -1647,8 +1647,8 @@ def go_repositories(): build_file_generation = "on", build_file_proto_mode = "disable", importpath = "github.com/Venafi/vcert", - sum = "h1:Vy4WHFF6SULSubt7vuyJRJ5AqwjJXzJQWdF37VcsyGo=", - version = "v0.0.0-20190613103158-62139eb19b25", + sum = "h1:oFQa1HzZX7Kcnjnosx9wEdVi8ADTDvs+2Tz96HIXrEU=", + version = "v0.0.0-20200207035730-5a915d73be5d", ) go_repository( name = "com_github_xiang90_probing", @@ -1719,8 +1719,8 @@ def go_repositories(): build_file_generation = "on", build_file_proto_mode = "disable", importpath = "gopkg.in/ini.v1", - sum = "h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=", - version = "v1.42.0", + sum = "h1:j+Lt/M1oPPejkniCg1TkWE2J3Eh1oZTsHSXzMTzUXn4=", + version = "v1.52.0", ) go_repository( name = "in_gopkg_mgo_v2", diff --git a/pkg/apis/certmanager/v1alpha2/types.go b/pkg/apis/certmanager/v1alpha2/types.go index 062d4e785..da6acc720 100644 --- a/pkg/apis/certmanager/v1alpha2/types.go +++ b/pkg/apis/certmanager/v1alpha2/types.go @@ -98,6 +98,15 @@ const ( AllowsInjectionFromSecretAnnotation = "cert-manager.io/allow-direct-injection" ) +// Issuer specific Annotations +const ( + // VenafiCustomFieldsAnnotationKey is the annotation that passes on JSON encoded custom fields to the Venafi issuer + // This will only work with Venafi TPP v19.3 and higher + // The value is an array with objetcs containing the name and value keys + // for example: `[{"name": "custom-field", "value": "custom-value"}]` + VenafiCustomFieldsAnnotationKey = "venafi.cert-manager.io/custom-fields" +) + // KeyUsage specifies valid usage contexts for keys. // See: https://tools.ietf.org/html/rfc5280#section-4.2.1.3 // https://tools.ietf.org/html/rfc5280#section-4.2.1.12 diff --git a/pkg/apis/certmanager/v1alpha3/types.go b/pkg/apis/certmanager/v1alpha3/types.go index 4eec7b05c..19e499d34 100644 --- a/pkg/apis/certmanager/v1alpha3/types.go +++ b/pkg/apis/certmanager/v1alpha3/types.go @@ -98,6 +98,15 @@ const ( AllowsInjectionFromSecretAnnotation = "cert-manager.io/allow-direct-injection" ) +// Issuer specific Annotations +const ( + // VenafiCustomFieldsAnnotationKey is the annotation that passes on JSON encoded custom fields to the Venafi issuer + // This will only work with Venafi TPP v19.3 and higher + // The value is an array with objetcs containing the name and value keys + // for example: `[{"name": "custom-field", "value": "custom-value"}]` + VenafiCustomFieldsAnnotationKey = "venafi.cert-manager.io/custom-fields" +) + // KeyUsage specifies valid usage contexts for keys. // See: https://tools.ietf.org/html/rfc5280#section-4.2.1.3 // https://tools.ietf.org/html/rfc5280#section-4.2.1.12 diff --git a/pkg/controller/certificaterequests/venafi/BUILD.bazel b/pkg/controller/certificaterequests/venafi/BUILD.bazel index 355aa496f..cea41fcec 100644 --- a/pkg/controller/certificaterequests/venafi/BUILD.bazel +++ b/pkg/controller/certificaterequests/venafi/BUILD.bazel @@ -12,6 +12,7 @@ go_library( "//pkg/controller/certificaterequests:go_default_library", "//pkg/controller/certificaterequests/util:go_default_library", "//pkg/internal/venafi:go_default_library", + "//pkg/internal/venafi/api:go_default_library", "//pkg/issuer:go_default_library", "//pkg/logs:go_default_library", "@com_github_venafi_vcert//pkg/endpoint:go_default_library", @@ -46,6 +47,7 @@ go_test( "//pkg/controller/certificaterequests:go_default_library", "//pkg/controller/test:go_default_library", "//pkg/internal/venafi:go_default_library", + "//pkg/internal/venafi/api:go_default_library", "//pkg/internal/venafi/fake:go_default_library", "//pkg/util/pki:go_default_library", "//test/unit/gen:go_default_library", diff --git a/pkg/controller/certificaterequests/venafi/venafi.go b/pkg/controller/certificaterequests/venafi/venafi.go index 3bd8875e4..aff04d131 100644 --- a/pkg/controller/certificaterequests/venafi/venafi.go +++ b/pkg/controller/certificaterequests/venafi/venafi.go @@ -18,6 +18,8 @@ package venafi import ( "context" + "encoding/json" + "fmt" "github.com/Venafi/vcert/pkg/endpoint" k8sErrors "k8s.io/apimachinery/pkg/api/errors" @@ -29,6 +31,7 @@ import ( "github.com/jetstack/cert-manager/pkg/controller/certificaterequests" crutil "github.com/jetstack/cert-manager/pkg/controller/certificaterequests/util" venafiinternal "github.com/jetstack/cert-manager/pkg/internal/venafi" + internalvanafiapi "github.com/jetstack/cert-manager/pkg/internal/venafi/api" issuerpkg "github.com/jetstack/cert-manager/pkg/issuer" logf "github.com/jetstack/cert-manager/pkg/logs" ) @@ -88,12 +91,31 @@ func (v *Venafi) Sign(ctx context.Context, cr *cmapi.CertificateRequest, issuerO duration := apiutil.DefaultCertDuration(cr.Spec.Duration) - certPem, err := client.Sign(cr.Spec.CSRPEM, duration) + var customFields []internalvanafiapi.CustomField + if annotation, exists := cr.GetAnnotations()[cmapi.VenafiCustomFieldsAnnotationKey]; exists && annotation != "" { + err := json.Unmarshal([]byte(annotation), &customFields) + if err != nil { + message := fmt.Sprintf("Failed to parse %q annotation", cmapi.VenafiCustomFieldsAnnotationKey) + + v.reporter.Failed(cr, err, "CustomFieldsError", message) + log.Error(err, message) + + return nil, nil + } + } + + certPem, err := client.Sign(cr.Spec.CSRPEM, duration, customFields) // Check some known error types if err != nil { switch err.(type) { + case venafiinternal.ErrCustomFieldsType: + v.reporter.Failed(cr, err, "CustomFieldsError", err.Error()) + log.Error(err, err.Error()) + + return nil, nil + case endpoint.ErrCertificatePending: message := "Venafi certificate still in a pending state, the request will be retried" diff --git a/pkg/controller/certificaterequests/venafi/venafi_test.go b/pkg/controller/certificaterequests/venafi/venafi_test.go index db990d089..e0ce578e8 100644 --- a/pkg/controller/certificaterequests/venafi/venafi_test.go +++ b/pkg/controller/certificaterequests/venafi/venafi_test.go @@ -43,6 +43,7 @@ import ( controllertest "github.com/jetstack/cert-manager/pkg/controller/test" testpkg "github.com/jetstack/cert-manager/pkg/controller/test" internalvenafi "github.com/jetstack/cert-manager/pkg/internal/venafi" + internalvanafiapi "github.com/jetstack/cert-manager/pkg/internal/venafi/api" internalvenafifake "github.com/jetstack/cert-manager/pkg/internal/venafi/fake" "github.com/jetstack/cert-manager/pkg/util/pki" "github.com/jetstack/cert-manager/test/unit/gen" @@ -146,6 +147,12 @@ func TestSign(t *testing.T) { }), ) + tppCRWithCustomFields := gen.CertificateRequestFrom(tppCR, gen.SetCertificateRequestAnnotations(map[string]string{"venafi.cert-manager.io/custom-fields": `[{"name": "cert-manager-test", "value": "test ok"}]`})) + + tppCRWithInvalidCustomFields := gen.CertificateRequestFrom(tppCR, gen.SetCertificateRequestAnnotations(map[string]string{"venafi.cert-manager.io/custom-fields": `[{"name": cert-manager-test}]`})) + + tppCRWithInvalidCustomFieldType := gen.CertificateRequestFrom(tppCR, gen.SetCertificateRequestAnnotations(map[string]string{"venafi.cert-manager.io/custom-fields": `[{"name": "cert-manager-test", "value": "test ok", "type": "Bool"}]`})) + cloudCR := gen.CertificateRequestFrom(baseCR, gen.SetCertificateRequestIssuer(cmmeta.ObjectReference{ Group: certmanager.GroupName, @@ -177,7 +184,7 @@ func TestSign(t *testing.T) { } clientReturnsPending := &internalvenafifake.Venafi{ - SignFn: func([]byte, time.Duration) ([]byte, error) { + SignFn: func([]byte, time.Duration, []internalvanafiapi.CustomField) ([]byte, error) { return nil, endpoint.ErrCertificatePending{ CertificateID: "test-cert-id", Status: "test-status-pending", @@ -185,23 +192,38 @@ func TestSign(t *testing.T) { }, } clientReturnsTimeout := &internalvenafifake.Venafi{ - SignFn: func([]byte, time.Duration) ([]byte, error) { + SignFn: func([]byte, time.Duration, []internalvanafiapi.CustomField) ([]byte, error) { return nil, endpoint.ErrRetrieveCertificateTimeout{ CertificateID: "test-cert-id", } }, } clientReturnsGenericError := &internalvenafifake.Venafi{ - SignFn: func([]byte, time.Duration) ([]byte, error) { + SignFn: func([]byte, time.Duration, []internalvanafiapi.CustomField) ([]byte, error) { return nil, errors.New("this is an error") }, } clientReturnsCert := &internalvenafifake.Venafi{ - SignFn: func([]byte, time.Duration) ([]byte, error) { + SignFn: func([]byte, time.Duration, []internalvanafiapi.CustomField) ([]byte, error) { return certPEM, nil }, } + clientReturnsCertIfCustomField := &internalvenafifake.Venafi{ + SignFn: func(csr []byte, t time.Duration, fields []internalvanafiapi.CustomField) ([]byte, error) { + if len(fields) > 0 && fields[0].Name == "cert-manager-test" && fields[0].Value == "test ok" { + return certPEM, nil + } + return nil, errors.New("Custom field not set") + }, + } + + clientReturnsInvalidCustomFieldType := &internalvenafifake.Venafi{ + SignFn: func(csr []byte, t time.Duration, fields []internalvanafiapi.CustomField) ([]byte, error) { + return nil, internalvenafi.ErrCustomFieldsType{Type: fields[0].Type} + }, + } + metaFixedClockStart := metav1.NewTime(fixedClockStart) tests := map[string]testT{ "tpp: if fail to build client based on missing secret then return nil and hard fail": { @@ -573,6 +595,96 @@ func TestSign(t *testing.T) { fakeSecretLister: failGetSecretLister, fakeClient: clientReturnsCert, }, + "annotations: Custom Fields": { + certificateRequest: tppCRWithCustomFields.DeepCopy(), + issuer: tppIssuer, + builder: &controllertest.Builder{ + CertManagerObjects: []runtime.Object{tppCRWithCustomFields.DeepCopy(), tppIssuer.DeepCopy()}, + ExpectedEvents: []string{ + `Normal CertificateIssued Certificate fetched from issuer successfully`, + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewUpdateSubresourceAction( + cmapi.SchemeGroupVersion.WithResource("certificaterequests"), + "status", + gen.DefaultTestNamespace, + gen.CertificateRequestFrom(tppCRWithCustomFields, + gen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{ + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionTrue, + Reason: cmapi.CertificateRequestReasonIssued, + Message: "Certificate fetched from issuer successfully", + LastTransitionTime: &metaFixedClockStart, + }), + gen.SetCertificateRequestCertificate(certPEM), + ), + )), + }, + }, + fakeSecretLister: failGetSecretLister, + fakeClient: clientReturnsCertIfCustomField, + expectedErr: false, + }, + "annotations: Error on invalid JSON in custom fields": { + certificateRequest: tppCRWithInvalidCustomFields.DeepCopy(), + issuer: tppIssuer, + builder: &controllertest.Builder{ + CertManagerObjects: []runtime.Object{tppCRWithInvalidCustomFields.DeepCopy(), tppIssuer.DeepCopy()}, + ExpectedEvents: []string{ + `Warning CustomFieldsError Failed to parse "venafi.cert-manager.io/custom-fields" annotation: invalid character 'c' looking for beginning of value`, + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewUpdateSubresourceAction( + cmapi.SchemeGroupVersion.WithResource("certificaterequests"), + "status", + gen.DefaultTestNamespace, + gen.CertificateRequestFrom(tppCRWithInvalidCustomFields, + gen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{ + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionFalse, + Reason: cmapi.CertificateRequestReasonFailed, + Message: "Failed to parse \"venafi.cert-manager.io/custom-fields\" annotation: invalid character 'c' looking for beginning of value", + LastTransitionTime: &metaFixedClockStart, + }), + gen.SetCertificateRequestFailureTime(metaFixedClockStart), + ), + )), + }, + }, + fakeSecretLister: failGetSecretLister, + fakeClient: clientReturnsPending, + expectedErr: false, + }, + "annotations: Error on invalid type in custom fields": { + certificateRequest: tppCRWithInvalidCustomFieldType.DeepCopy(), + issuer: tppIssuer, + builder: &controllertest.Builder{ + CertManagerObjects: []runtime.Object{tppCRWithInvalidCustomFieldType.DeepCopy(), tppIssuer.DeepCopy()}, + ExpectedEvents: []string{ + `Warning CustomFieldsError certificate request contains an invalid Venafi custom fields type: "Bool": certificate request contains an invalid Venafi custom fields type: "Bool"`, + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewUpdateSubresourceAction( + cmapi.SchemeGroupVersion.WithResource("certificaterequests"), + "status", + gen.DefaultTestNamespace, + gen.CertificateRequestFrom(tppCRWithInvalidCustomFieldType, + gen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{ + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionFalse, + Reason: cmapi.CertificateRequestReasonFailed, + Message: "certificate request contains an invalid Venafi custom fields type: \"Bool\": certificate request contains an invalid Venafi custom fields type: \"Bool\"", + LastTransitionTime: &metaFixedClockStart, + }), + gen.SetCertificateRequestFailureTime(metaFixedClockStart), + ), + )), + }, + }, + fakeSecretLister: failGetSecretLister, + fakeClient: clientReturnsInvalidCustomFieldType, + expectedErr: false, + }, } for name, test := range tests { diff --git a/pkg/internal/venafi/BUILD.bazel b/pkg/internal/venafi/BUILD.bazel index 8abfae3bc..7ab70866e 100644 --- a/pkg/internal/venafi/BUILD.bazel +++ b/pkg/internal/venafi/BUILD.bazel @@ -10,6 +10,7 @@ go_library( visibility = ["//pkg:__subpackages__"], deps = [ "//pkg/apis/certmanager/v1alpha2:go_default_library", + "//pkg/internal/venafi/api:go_default_library", "//pkg/util/pki:go_default_library", "@com_github_venafi_vcert//:go_default_library", "@com_github_venafi_vcert//pkg/certificate:go_default_library", @@ -29,6 +30,7 @@ filegroup( name = "all-srcs", srcs = [ ":package-srcs", + "//pkg/internal/venafi/api:all-srcs", "//pkg/internal/venafi/fake:all-srcs", ], tags = ["automanaged"], @@ -45,6 +47,7 @@ go_test( deps = [ "//pkg/apis/certmanager/v1alpha2:go_default_library", "//pkg/apis/meta/v1:go_default_library", + "//pkg/internal/venafi/api:go_default_library", "//pkg/internal/venafi/fake:go_default_library", "//pkg/util:go_default_library", "//pkg/util/pki:go_default_library", diff --git a/pkg/internal/venafi/api/BUILD.bazel b/pkg/internal/venafi/api/BUILD.bazel new file mode 100644 index 000000000..4f4e15082 --- /dev/null +++ b/pkg/internal/venafi/api/BUILD.bazel @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) + +go_library( + name = "go_default_library", + srcs = ["api.go"], + importpath = "github.com/jetstack/cert-manager/pkg/internal/venafi/api", + visibility = ["//pkg:__subpackages__"], +) diff --git a/pkg/internal/venafi/api/api.go b/pkg/internal/venafi/api/api.go new file mode 100644 index 000000000..e2e44c661 --- /dev/null +++ b/pkg/internal/venafi/api/api.go @@ -0,0 +1,30 @@ +/* +Copyright 2020 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 api + +type CustomFieldType string + +const ( + CustomFieldTypePlain CustomFieldType = "Plain" +) + +// CustomField defines a custom field to be passed to Venafi +type CustomField struct { + Type CustomFieldType `json:"type,omitempty"` + Name string `json:"name"` + Value string `json:"value"` +} diff --git a/pkg/internal/venafi/fake/BUILD.bazel b/pkg/internal/venafi/fake/BUILD.bazel index cf3d07f5e..c1025b6bd 100644 --- a/pkg/internal/venafi/fake/BUILD.bazel +++ b/pkg/internal/venafi/fake/BUILD.bazel @@ -9,6 +9,7 @@ go_library( importpath = "github.com/jetstack/cert-manager/pkg/internal/venafi/fake", visibility = ["//pkg:__subpackages__"], deps = [ + "//pkg/internal/venafi/api:go_default_library", "@com_github_venafi_vcert//pkg/certificate:go_default_library", "@com_github_venafi_vcert//pkg/endpoint:go_default_library", "@com_github_venafi_vcert//pkg/venafi/fake:go_default_library", diff --git a/pkg/internal/venafi/fake/venafi.go b/pkg/internal/venafi/fake/venafi.go index 94163758f..f2f333c1d 100644 --- a/pkg/internal/venafi/fake/venafi.go +++ b/pkg/internal/venafi/fake/venafi.go @@ -20,11 +20,13 @@ import ( "time" "github.com/Venafi/vcert/pkg/endpoint" + + internalvanafiapi "github.com/jetstack/cert-manager/pkg/internal/venafi/api" ) type Venafi struct { PingFn func() error - SignFn func([]byte, time.Duration) ([]byte, error) + SignFn func([]byte, time.Duration, []internalvanafiapi.CustomField) ([]byte, error) ReadZoneConfigurationFn func() (*endpoint.ZoneConfiguration, error) } @@ -32,8 +34,8 @@ func (v *Venafi) Ping() error { return v.PingFn() } -func (v *Venafi) Sign(b []byte, t time.Duration) ([]byte, error) { - return v.SignFn(b, t) +func (v *Venafi) Sign(b []byte, t time.Duration, f []internalvanafiapi.CustomField) ([]byte, error) { + return v.SignFn(b, t, f) } func (v *Venafi) ReadZoneConfiguration() (*endpoint.ZoneConfiguration, error) { diff --git a/pkg/internal/venafi/sign.go b/pkg/internal/venafi/sign.go index e67150bac..8015ef890 100644 --- a/pkg/internal/venafi/sign.go +++ b/pkg/internal/venafi/sign.go @@ -19,18 +19,29 @@ package venafi import ( "crypto/x509" "errors" + "fmt" "strings" "time" "github.com/Venafi/vcert/pkg/certificate" + internalvanafiapi "github.com/jetstack/cert-manager/pkg/internal/venafi/api" "github.com/jetstack/cert-manager/pkg/util/pki" ) +// ErrCustomFieldsType provides a common error structure for a fivalid custom field types +type ErrCustomFieldsType struct { + Type internalvanafiapi.CustomFieldType +} + +func (err ErrCustomFieldsType) Error() string { + return fmt.Sprintf("certificate request contains an invalid Venafi custom fields type: %q", err.Type) +} + // This function sends a request to Venafi to for a signed certificate. // The CSR will be decoded to be validated against the zone configuration policy. // Upon the template being successfully defaulted and validated, the CSR will be sent, as is. -func (v *Venafi) Sign(csrPEM []byte, duration time.Duration) (cert []byte, err error) { +func (v *Venafi) Sign(csrPEM []byte, duration time.Duration, customFields []internalvanafiapi.CustomField) (cert []byte, err error) { // Retrieve a copy of the Venafi zone. // This contains default values and policy control info that we can apply // and check against locally. @@ -47,6 +58,30 @@ func (v *Venafi) Sign(csrPEM []byte, duration time.Duration) (cert []byte, err e // Create a vcert Request structure vreq := newVRequest(tmpl) + // Convert over custom fields from our struct type to venafi's + if len(customFields) > 0 { + vreq.CustomFields = []certificate.CustomField{} + for _, field := range customFields { + var fieldType certificate.CustomFieldType + switch field.Type { + case internalvanafiapi.CustomFieldTypePlain: + fieldType = certificate.CustomFieldPlain + break + case "": + fieldType = certificate.CustomFieldPlain + break + default: + return nil, ErrCustomFieldsType{Type: field.Type} + } + + vreq.CustomFields = append(vreq.CustomFields, certificate.CustomField{ + Type: fieldType, + Name: field.Name, + Value: field.Value, + }) + } + } + // Apply default values from the Venafi zone zoneCfg.UpdateCertificateRequest(vreq) diff --git a/pkg/internal/venafi/sign_test.go b/pkg/internal/venafi/sign_test.go index 63f35763d..27e514d65 100644 --- a/pkg/internal/venafi/sign_test.go +++ b/pkg/internal/venafi/sign_test.go @@ -30,6 +30,7 @@ import ( "github.com/Venafi/vcert/pkg/endpoint" "github.com/Venafi/vcert/pkg/venafi/fake" + internalvanafiapi "github.com/jetstack/cert-manager/pkg/internal/venafi/api" internalfake "github.com/jetstack/cert-manager/pkg/internal/venafi/fake" "github.com/jetstack/cert-manager/pkg/util" "github.com/jetstack/cert-manager/pkg/util/pki" @@ -173,6 +174,29 @@ func TestSign(t *testing.T) { checkFn: checkCertificateIssued, expectedErr: false, }, + "obtain a certificate with custom fields specified": { + csrPEM: csrPEM, + customFields: []internalvanafiapi.CustomField{{Name: "test", Value: "ok"}}, + client: internalfake.Connector{ + RetrieveCertificateFunc: func(r *certificate.Request) (*certificate.PEMCollection, error) { + if len(r.CustomFields) == 0 { + return nil, errors.New("custom fields not set") + } + if r.CustomFields[0].Name != "test" || r.CustomFields[0].Value != "ok" { + return nil, errors.New("custom fields content not correct") + } + return internalfake.Connector{}.Default().RetrieveCertificate(r) // hack to return to normal + }, + }.Default(), + checkFn: checkCertificateIssued, + expectedErr: false, + }, + "If invalid custom field type found the error": { + csrPEM: csrPEM, + customFields: []internalvanafiapi.CustomField{{Name: "test", Value: "ok", Type: "Bool"}}, + checkFn: checkNoCetificateIssued, + expectedErr: true, + }, } for name, test := range tests { @@ -188,6 +212,8 @@ type testSignT struct { expectedErr bool + customFields []internalvanafiapi.CustomField + checkFn func(*testing.T, []byte, []byte) } @@ -201,7 +227,7 @@ func (s *testSignT) runTest(t *testing.T) { client: client, } - resp, err := v.Sign(s.csrPEM, time.Minute) + resp, err := v.Sign(s.csrPEM, time.Minute, s.customFields) if err != nil && !s.expectedErr { t.Errorf("expected to not get an error, but got: %v", err) } diff --git a/pkg/internal/venafi/venafi.go b/pkg/internal/venafi/venafi.go index 0204c3066..b43d5f35d 100644 --- a/pkg/internal/venafi/venafi.go +++ b/pkg/internal/venafi/venafi.go @@ -26,6 +26,7 @@ import ( corelisters "k8s.io/client-go/listers/core/v1" cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2" + internalvanafiapi "github.com/jetstack/cert-manager/pkg/internal/venafi/api" ) const ( @@ -39,7 +40,7 @@ type VenafiClientBuilder func(namespace string, secretsLister corelisters.Secret issuer cmapi.GenericIssuer) (Interface, error) type Interface interface { - Sign(csrPEM []byte, duration time.Duration) (cert []byte, err error) + Sign(csrPEM []byte, duration time.Duration, customFields []internalvanafiapi.CustomField) (cert []byte, err error) Ping() error ReadZoneConfiguration() (*endpoint.ZoneConfiguration, error) SetClient(endpoint.Connector)