Adds SelfSigned certificaterequest controller
Signed-off-by: JoshVanL <vleeuwenjoshua@gmail.com>
This commit is contained in:
parent
8fa48c2148
commit
0ce8aab9d2
@ -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.
|
||||
|
||||
@ -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"],
|
||||
)
|
||||
|
||||
54
pkg/controller/certificaterequests/selfsigned/BUILD.bazel
Normal file
54
pkg/controller/certificaterequests/selfsigned/BUILD.bazel
Normal file
@ -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"],
|
||||
)
|
||||
124
pkg/controller/certificaterequests/selfsigned/selfsigned.go
Normal file
124
pkg/controller/certificaterequests/selfsigned/selfsigned.go
Normal file
@ -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
|
||||
}
|
||||
353
pkg/controller/certificaterequests/selfsigned/selfsigned_test.go
Normal file
353
pkg/controller/certificaterequests/selfsigned/selfsigned_test.go
Normal file
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
92
pkg/controller/certificaterequests/selfsigned/util_test.go
Normal file
92
pkg/controller/certificaterequests/selfsigned/util_test.go
Normal file
@ -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
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user