diff options
Diffstat (limited to 'certServiceK8sExternalProvider/src')
17 files changed, 620 insertions, 107 deletions
diff --git a/certServiceK8sExternalProvider/src/certserviceclient/cert_service_client_mock.go b/certServiceK8sExternalProvider/src/certserviceclient/cert_service_client_mock.go new file mode 100644 index 00000000..d060a980 --- /dev/null +++ b/certServiceK8sExternalProvider/src/certserviceclient/cert_service_client_mock.go @@ -0,0 +1,33 @@ +/* + * ============LICENSE_START======================================================= + * oom-certservice-k8s-external-provider + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package certserviceclient + +type CertServiceClientMock struct { + GetCertificatesFunc func(csr []byte, key []byte) (*CertificatesResponse, error) +} + +func (client *CertServiceClientMock) GetCertificates(csr []byte, key []byte) (*CertificatesResponse, error) { + return client.GetCertificatesFunc(csr, key) +} + +func (client *CertServiceClientMock) CheckHealth() error { + return nil +} diff --git a/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller.go b/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller.go index 03eef35c..e5dc4d1c 100644 --- a/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller.go +++ b/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller.go @@ -140,7 +140,7 @@ func (controller *CertificateRequestController) Reconcile(k8sRequest ctrl.Reques signedPEM, trustedCAs, err := provisioner.Sign(ctx, certificateRequest, privateKeyBytes) if err != nil { controller.handleErrorFailedToSignCertificate(ctx, log, err, certificateRequest) - return ctrl.Result{}, err + return ctrl.Result{}, nil } // 11. Store signed certificates in CertificateRequest diff --git a/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller.go b/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller.go index c6e0e1da..9bc41e7f 100644 --- a/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller.go +++ b/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller.go @@ -49,6 +49,7 @@ type CMPv2IssuerController struct { Log logr.Logger Clock clock.Clock Recorder record.EventRecorder + ProvisionerFactory provisioners.ProvisionerFactory } // Reconcile will read and validate the CMPv2Issuer resources, it will set the @@ -84,7 +85,7 @@ func (controller *CMPv2IssuerController) Reconcile(req ctrl.Request) (ctrl.Resul } // 4. Create CMPv2 provisioner - provisioner, err := provisioners.CreateProvisioner(issuer, secret) + provisioner, err := controller.ProvisionerFactory.CreateProvisioner(issuer, secret) if err != nil { log.Error(err, "failed to initialize provisioner") statusUpdater.UpdateNoError(ctx, cmpv2api.ConditionFalse, "Error", "Failed to initialize provisioner: %v", err) diff --git a/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller_test.go b/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller_test.go index 79c78ed5..88aaf5ec 100644 --- a/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller_test.go +++ b/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller_test.go @@ -24,14 +24,47 @@ import ( "testing" "github.com/go-logr/logr" + certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + apiv1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" + "k8s.io/utils/clock" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" + certserviceapi "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" + provisioners "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner" + "onap.org/oom-certservice/k8s-external-provider/src/testdata" ) +func Test_shouldPrepareAndVerifyCMPv2Issuer_whenRequestReceived(t *testing.T) { + scheme := initScheme() + issuer, secret := testdata.GetValidIssuerWithSecret() + fakeClient := getFakeClient(scheme, issuer, secret) + fakeRequest := getFakeRequest() + fakeRecorder := record.NewFakeRecorder(3) + controller := getController(fakeRecorder, fakeClient) + + res, err := controller.Reconcile(fakeRequest) + + expectedProvisioner, _ := controller.ProvisionerFactory.CreateProvisioner(&issuer, secret) + actualProvisioner, _ := provisioners.Load(types.NamespacedName{Name: testdata.IssuerObjectName, Namespace: testdata.Namespace}) + assert.Nil(t, err) + assert.NotNil(t, res) + assert.Equal(t, <-fakeRecorder.Events, "Normal Verified CMPv2Issuer verified and ready to sign certificates") + assert.NotNil(t, actualProvisioner) + assert.ObjectsAreEqual(expectedProvisioner, actualProvisioner) +} + func Test_shouldBeValidCMPv2IssuerSpec_whenAllFieldsAreSet(t *testing.T) { - spec := getValidCMPv2IssuerSpec() + spec := testdata.GetValidCMPv2IssuerSpec() err := validateCMPv2IssuerSpec(spec, &MockLogger{}) assert.Nil(t, err) @@ -61,24 +94,46 @@ func Test_shouldBeInvalidCMPv2IssuerSpec_whenNotAllFieldsAreSet(t *testing.T) { } func test_shouldBeInvalidCMPv2IssuerSpec_whenFunctionApplied(t *testing.T, transformSpec func(spec *cmpv2api.CMPv2IssuerSpec)) { - spec := getValidCMPv2IssuerSpec() + spec := testdata.GetValidCMPv2IssuerSpec() transformSpec(&spec) err := validateCMPv2IssuerSpec(spec, nil) assert.NotNil(t, err) } -func getValidCMPv2IssuerSpec() cmpv2api.CMPv2IssuerSpec { - issuerSpec := cmpv2api.CMPv2IssuerSpec{ - URL: "https://oom-cert-service:8443/v1/certificate/", - CaName: "RA", - CertSecretRef: cmpv2api.SecretKeySelector{ - Name: "issuer-cert-secret", - KeyRef: "cmpv2Issuer-key.pem", - CertRef: "cmpv2Issuer-cert.pem", - CacertRef: "cacert.pem", +func getController(fakeRecorder *record.FakeRecorder, mockClient client.Client) CMPv2IssuerController { + controller := CMPv2IssuerController{ + Log: ctrl.Log.WithName("controllers").WithName("CertificateRequest"), + Clock: clock.RealClock{}, + Recorder: fakeRecorder, + Client: mockClient, + ProvisionerFactory: &provisioners.ProvisionerFactoryMock{}, + } + return controller +} + +func getFakeRequest() reconcile.Request { + fakeRequest := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: testdata.Namespace, + Name: testdata.IssuerObjectName, }, } - return issuerSpec + return fakeRequest +} + +func getFakeClient(scheme *runtime.Scheme, issuer cmpv2api.CMPv2Issuer, secret apiv1.Secret) client.Client { + fakeClient := func() client.Client { + return fake.NewFakeClientWithScheme(scheme, &issuer, &secret) + }() + return fakeClient +} + +func initScheme() *runtime.Scheme { + scheme := runtime.NewScheme() + _ = clientgoscheme.AddToScheme(scheme) + _ = certmanager.AddToScheme(scheme) + _ = certserviceapi.AddToScheme(scheme) + return scheme } type MockLogger struct { diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner.go b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner.go index 60610d32..14cb228f 100644 --- a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner.go +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner.go @@ -35,6 +35,7 @@ import ( "onap.org/oom-certservice/k8s-external-provider/src/certserviceclient" "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner/csr" ) var collection = new(sync.Map) @@ -83,7 +84,11 @@ func Store(namespacedName types.NamespacedName, provisioner *CertServiceCA) { collection.Store(namespacedName, provisioner) } -func (ca *CertServiceCA) Sign(ctx context.Context, certificateRequest *certmanager.CertificateRequest, privateKeyBytes []byte) ([]byte, []byte, error) { +func (ca *CertServiceCA) Sign( + ctx context.Context, + certificateRequest *certmanager.CertificateRequest, + privateKeyBytes []byte, +) (signedCertificateChain []byte, trustedCertificates []byte, err error) { log := ctrl.Log.WithName("certservice-provisioner") log.Info("Signing certificate: ", "cert-name", certificateRequest.Name) @@ -92,7 +97,12 @@ func (ca *CertServiceCA) Sign(ctx context.Context, certificateRequest *certmanag csrBytes := certificateRequest.Spec.Request log.Info("Csr PEM: ", "bytes", csrBytes) - response, err := ca.certServiceClient.GetCertificates(csrBytes, privateKeyBytes) + filteredCsrBytes, err := csr.FilterFieldsFromCSR(csrBytes, privateKeyBytes) + if err != nil { + return nil, nil, err + } + + response, err := ca.certServiceClient.GetCertificates(filteredCsrBytes, privateKeyBytes) if err != nil { return nil, nil, err } diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory.go b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory.go index 27f5c108..00abd2ea 100644 --- a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory.go +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory.go @@ -29,7 +29,15 @@ import ( "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" ) -func CreateProvisioner(issuer *cmpv2api.CMPv2Issuer, secret v1.Secret) (*CertServiceCA, error) { + +type ProvisionerFactory interface { + CreateProvisioner(issuer *cmpv2api.CMPv2Issuer, secret v1.Secret) (*CertServiceCA, error) +} + +type ProvisionerFactoryImpl struct { +} + +func (f *ProvisionerFactoryImpl) CreateProvisioner(issuer *cmpv2api.CMPv2Issuer, secret v1.Secret) (*CertServiceCA, error) { secretKeys := issuer.Spec.CertSecretRef keyBase64, err := readValueFromSecret(secret, secretKeys.KeyRef) if err != nil { diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_mock.go b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_mock.go new file mode 100644 index 00000000..f2ffa860 --- /dev/null +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_mock.go @@ -0,0 +1,43 @@ +/* + * ============LICENSE_START======================================================= + * oom-certservice-k8s-external-provider + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package cmpv2provisioner + +import ( + v1 "k8s.io/api/core/v1" + + "onap.org/oom-certservice/k8s-external-provider/src/certserviceclient" + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner/testdata" +) + +type ProvisionerFactoryMock struct { + CreateProvisionerFunc func(issuer *cmpv2api.CMPv2Issuer, secret v1.Secret) (*CertServiceCA, error) +} + +func (f *ProvisionerFactoryMock) CreateProvisioner(issuer *cmpv2api.CMPv2Issuer, secret v1.Secret) (*CertServiceCA, error) { + provisioner, err := New(issuer, &certserviceclient.CertServiceClientMock{ + GetCertificatesFunc: func(csr []byte, pk []byte) (response *certserviceclient.CertificatesResponse, e error) { + return &testdata.SampleCertServiceResponse, nil + }, + }) + + return provisioner, err +} diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_test.go b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_test.go index be19bde9..a7e975a4 100644 --- a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_test.go +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_test.go @@ -26,105 +26,70 @@ import ( "testing" "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" "onap.org/oom-certservice/k8s-external-provider/src/testdata" ) -const ( - secretName = "issuer-cert-secret" - url = "https://oom-cert-service:8443/v1/certificate/" - healthEndpoint = "actuator/health" - certEndpoint = "v1/certificate" - caName = "RA" - keySecretKey = "cmpv2Issuer-key.pem" - certSecretKey = "cmpv2Issuer-cert.pem" - cacertSecretKey = "cacert.pem" -) - func Test_shouldCreateProvisioner(t *testing.T) { - issuer, secret := getValidIssuerAndSecret() + issuer, secret := testdata.GetValidIssuerWithSecret() + provisionerFactory := ProvisionerFactoryImpl{} - provisioner, _ := CreateProvisioner(&issuer, secret) + provisioner, _ := provisionerFactory.CreateProvisioner(&issuer, secret) assert.NotNil(t, provisioner) - assert.Equal(t, url, provisioner.url) - assert.Equal(t, caName, provisioner.caName) - assert.Equal(t, healthEndpoint, provisioner.healthEndpoint) - assert.Equal(t, certEndpoint, provisioner.certEndpoint) + assert.Equal(t, testdata.Url, provisioner.url) + assert.Equal(t, testdata.CaName, provisioner.caName) + assert.Equal(t, testdata.HealthEndpoint, provisioner.healthEndpoint) + assert.Equal(t, testdata.CertEndpoint, provisioner.certEndpoint) } func Test_shouldReturnError_whenSecretMissingKeyRef(t *testing.T) { - issuer, secret := getValidIssuerAndSecret() - delete(secret.Data, keySecretKey) + issuer, secret := testdata.GetValidIssuerWithSecret() + delete(secret.Data, testdata.KeySecretKey) + provisionerFactory := ProvisionerFactoryImpl{} - provisioner, err := CreateProvisioner(&issuer, secret) + provisioner, err := provisionerFactory.CreateProvisioner(&issuer, secret) assert.Nil(t, provisioner) if assert.Error(t, err) { - assert.Equal(t, fmt.Errorf("secret %s does not contain key %s", secretName, keySecretKey), err) + assert.Equal(t, fmt.Errorf("secret %s does not contain key %s", testdata.SecretName, testdata.KeySecretKey), err) } } func Test_shouldReturnError_whenSecretMissingCertRef(t *testing.T) { - issuer, secret := getValidIssuerAndSecret() - delete(secret.Data, certSecretKey) + issuer, secret := testdata.GetValidIssuerWithSecret() + delete(secret.Data, testdata.CertSecretKey) + provisionerFactory := ProvisionerFactoryImpl{} - provisioner, err := CreateProvisioner(&issuer, secret) + provisioner, err := provisionerFactory.CreateProvisioner(&issuer, secret) assert.Nil(t, provisioner) if assert.Error(t, err) { - assert.Equal(t, fmt.Errorf("secret %s does not contain key %s", secretName, certSecretKey), err) + assert.Equal(t, fmt.Errorf("secret %s does not contain key %s", testdata.SecretName, testdata.CertSecretKey), err) } } func Test_shouldReturnError_whenSecretMissingCacertRef(t *testing.T) { - issuer, secret := getValidIssuerAndSecret() - delete(secret.Data, cacertSecretKey) + issuer, secret := testdata.GetValidIssuerWithSecret() + delete(secret.Data, testdata.CacertSecretKey) + provisionerFactory := ProvisionerFactoryImpl{} - provisioner, err := CreateProvisioner(&issuer, secret) + provisioner, err := provisionerFactory.CreateProvisioner(&issuer, secret) assert.Nil(t, provisioner) if assert.Error(t, err) { - assert.Equal(t, fmt.Errorf("secret %s does not contain key %s", secretName, cacertSecretKey), err) + assert.Equal(t, fmt.Errorf("secret %s does not contain key %s", testdata.SecretName, testdata.CacertSecretKey), err) } } func Test_shouldReturnError_whenCreationOfCertServiceClientReturnsError(t *testing.T) { - issuer, secret := getValidIssuerAndSecret() + issuer, secret := testdata.GetValidIssuerWithSecret() invalidKeySecretValue, _ := base64.StdEncoding.DecodeString("") - secret.Data[keySecretKey] = invalidKeySecretValue + secret.Data[testdata.KeySecretKey] = invalidKeySecretValue + provisionerFactory := ProvisionerFactoryImpl{} - provisioner, err := CreateProvisioner(&issuer, secret) + provisioner, err := provisionerFactory.CreateProvisioner(&issuer, secret) assert.Nil(t, provisioner) assert.Error(t, err) } - -func getValidIssuerAndSecret() (cmpv2api.CMPv2Issuer, v1.Secret) { - issuer := cmpv2api.CMPv2Issuer{ - Spec: cmpv2api.CMPv2IssuerSpec{ - URL: url, - HealthEndpoint: healthEndpoint, - CertEndpoint: certEndpoint, - CaName: caName, - CertSecretRef: cmpv2api.SecretKeySelector{ - Name: secretName, - KeyRef: keySecretKey, - CertRef: certSecretKey, - CacertRef: cacertSecretKey, - }, - }, - } - secret := v1.Secret{ - - Data: map[string][]byte{ - keySecretKey: testdata.KeyBytes, - certSecretKey: testdata.CertBytes, - cacertSecretKey: testdata.CacertBytes, - }, - } - secret.Name = secretName - return issuer, secret -} diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_test.go b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_test.go index 57e269a4..a483c72c 100644 --- a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_test.go +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_test.go @@ -30,12 +30,12 @@ import ( cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" "github.com/stretchr/testify/assert" + apiv1 "k8s.io/api/core/v1" apimach "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "onap.org/oom-certservice/k8s-external-provider/src/certserviceclient" "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" - "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner/testdata" ) const ISSUER_NAME = "cmpv2-issuer" @@ -72,11 +72,8 @@ func Test_shouldReturnCorrectSignedPemsWhenParametersAreCorrect(t *testing.T) { const EXPECTED_TRUSTED_FILENAME = "testdata/expected_trusted.pem" issuer := createIssuerAndCerts(ISSUER_NAME, ISSUER_URL) - provisioner, err := New(&issuer, &certServiceClientMock{ - getCertificatesFunc: func(csr []byte, pk []byte) (response *certserviceclient.CertificatesResponse, e error) { - return &testdata.SampleCertServiceResponse, nil - }, - }) + provisionerFactory := ProvisionerFactoryMock{} + provisioner, err := provisionerFactory.CreateProvisioner(&issuer, apiv1.Secret{}) issuerNamespaceName := createIssuerNamespaceName(ISSUER_NAMESPACE, ISSUER_NAME) Store(issuerNamespaceName, provisioner) @@ -87,8 +84,9 @@ func Test_shouldReturnCorrectSignedPemsWhenParametersAreCorrect(t *testing.T) { ctx := context.Background() request := createCertificateRequest() + privateKeyBytes := getPrivateKeyBytes() - signedPEM, trustedCAs, err := provisioner.Sign(ctx, request, nil) + signedPEM, trustedCAs, err := provisioner.Sign(ctx, request, privateKeyBytes) assert.Nil(t, err) @@ -153,6 +151,10 @@ func createCertificateRequest() *cmapi.CertificateRequest { return request } +func getPrivateKeyBytes() []byte { + return readFile("testdata/test_private_key.pem") +} + func areSlicesEqual(slice1 []byte, slice2 []byte) bool { return bytes.Compare(slice1, slice2) == 0 } diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/csr/csr.go b/certServiceK8sExternalProvider/src/cmpv2provisioner/csr/csr.go new file mode 100644 index 00000000..1a86866b --- /dev/null +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/csr/csr.go @@ -0,0 +1,62 @@ +/* + * ============LICENSE_START======================================================= + * oom-certservice-k8s-external-provider + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package csr + +import ( + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + + x509utils "onap.org/oom-certservice/k8s-external-provider/src/x509" +) + +func FilterFieldsFromCSR(csrBytes []byte, privateKeyBytes []byte) ([]byte, error) { + csr, err := x509utils.DecodeCSR(csrBytes) + if err != nil { + return nil, err + } + + key, err := x509utils.DecodePrivateKey(privateKeyBytes) + if err != nil { + return nil, err + } + + filteredSubject := filterFieldsFromSubject(csr.Subject) + + filteredCsr, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{ + Subject: filteredSubject, + DNSNames: csr.DNSNames, + }, key) + if err != nil { + return nil, err + } + + csrBytes = pem.EncodeToMemory(&pem.Block{Type: x509utils.PemCsrType, Bytes: filteredCsr}) + return csrBytes, nil +} + +func filterFieldsFromSubject(subject pkix.Name) pkix.Name { + subject.StreetAddress = []string{} + subject.SerialNumber = "" + subject.PostalCode = []string{} + return subject +} diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/csr/csr_test.go b/certServiceK8sExternalProvider/src/cmpv2provisioner/csr/csr_test.go new file mode 100644 index 00000000..08fb5538 --- /dev/null +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/csr/csr_test.go @@ -0,0 +1,103 @@ +/* + * ============LICENSE_START======================================================= + * oom-certservice-k8s-external-provider + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package csr + +import ( + "crypto/x509" + "encoding/pem" + "testing" + + "github.com/stretchr/testify/assert" + + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner/csr/testdata" +) + +func Test_FilterFieldsFromCSR_shouldFilterUnsupportedFields(t *testing.T) { + filteredCsrBytes, _ := FilterFieldsFromCSR(testdata.CsrBytesWithNotSupportedFields, testdata.PrivateKeyBytes) + + assertNotFilteredFieldsNotChanged(t, testdata.CsrBytesWithNotSupportedFields, filteredCsrBytes) + assertFilteredFieldsEmpty(t, filteredCsrBytes) +} + +func Test_FilterFieldsFromCSR_shouldNotChangeCsrWithoutNotSupportedFields(t *testing.T) { + filteredCsrBytes, _ := FilterFieldsFromCSR(testdata.CsrBytesWithoutNotSupportedFields, testdata.PrivateKeyBytes) + + assertNotFilteredFieldsNotChanged(t, testdata.CsrBytesWithoutNotSupportedFields, filteredCsrBytes) + assertFilteredFieldsEmpty(t, filteredCsrBytes) +} + +func Test_FilterFieldsFromCSR_shouldErrorWhenCsrPemCannotBeDecoded(t *testing.T) { + _, err := FilterFieldsFromCSR([]byte(""), testdata.PrivateKeyBytes) + + assert.Error(t, err) +} + +func Test_FilterFieldsFromCSR_shouldErrorWhenCsrCannotBeParsed(t *testing.T) { + //Private Key used as CSR + _, err := FilterFieldsFromCSR(testdata.PrivateKeyBytes, testdata.PrivateKeyBytes) + + assert.Error(t, err) +} + +func Test_FilterFieldsFromCSR_shouldErrorWhenPkPemCannotBeDecoded(t *testing.T) { + _, err := FilterFieldsFromCSR(testdata.CsrBytesWithNotSupportedFields, []byte("")) + + assert.Error(t, err) +} + +func Test_FilterFieldsFromCSR_shouldErrorWhenPkCannotBeParsed(t *testing.T) { + //CSR used as Private Key + _, err := FilterFieldsFromCSR(testdata.CsrBytesWithNotSupportedFields, testdata.CsrBytesWithNotSupportedFields) + + assert.Error(t, err) +} + +func assertNotFilteredFieldsNotChanged(t *testing.T, originalCsrBytes []byte, filteredCsrBytes []byte) { + originalCsr := parseCsrBytes(originalCsrBytes) + filteredCsr := parseCsrBytes(filteredCsrBytes) + + assert.Equal(t, originalCsr.DNSNames, filteredCsr.DNSNames) + assert.Equal(t, originalCsr.PublicKey, filteredCsr.PublicKey) + assert.Equal(t, originalCsr.PublicKeyAlgorithm, filteredCsr.PublicKeyAlgorithm) + assert.Equal(t, originalCsr.SignatureAlgorithm, filteredCsr.SignatureAlgorithm) + assert.Equal(t, originalCsr.Subject.CommonName, filteredCsr.Subject.CommonName) + assert.Equal(t, originalCsr.Subject.Country, filteredCsr.Subject.Country) + assert.Equal(t, originalCsr.Subject.Locality, filteredCsr.Subject.Locality) + assert.Equal(t, originalCsr.Subject.Organization, filteredCsr.Subject.Organization) + assert.Equal(t, originalCsr.Subject.OrganizationalUnit, filteredCsr.Subject.OrganizationalUnit) + assert.Equal(t, originalCsr.Subject.Province, filteredCsr.Subject.Province) +} + +func assertFilteredFieldsEmpty(t *testing.T, csrBytes []byte) { + csr := parseCsrBytes(csrBytes) + assert.Nil(t, csr.URIs) + assert.Nil(t, csr.EmailAddresses) + assert.Nil(t, csr.IPAddresses) + assert.Nil(t, csr.Subject.PostalCode) + assert.Equal(t, "", csr.Subject.SerialNumber) + assert.Nil(t, csr.Subject.StreetAddress) +} + +func parseCsrBytes(csrBytes []byte) *x509.CertificateRequest { + decodedCsr, _ := pem.Decode(csrBytes) + csr, _ := x509.ParseCertificateRequest(decodedCsr.Bytes) + return csr +} diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/csr/testdata/testdata.go b/certServiceK8sExternalProvider/src/cmpv2provisioner/csr/testdata/testdata.go new file mode 100644 index 00000000..c79f4f79 --- /dev/null +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/csr/testdata/testdata.go @@ -0,0 +1,29 @@ +/* + * ============LICENSE_START======================================================= + * oom-certservice-k8s-external-provider + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package testdata + +import "encoding/base64" + +var ( + CsrBytesWithNotSupportedFields, _ = base64.StdEncoding.DecodeString("LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJRG1EQ0NBb0FDQVFBd2dib3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saApNUll3RkFZRFZRUUhFdzFUWVc0dFJuSmhibU5wYzJOdk1SVXdFd1lEVlFRSkV3eHpZVzF3YkdWVGRISmxaWFF4CkR6QU5CZ05WQkJFVEJqRXlMVE0wTlRFWk1CY0dBMVVFQ2hNUVRHbHVkWGd0Um05MWJtUmhkR2x2YmpFTk1Bc0cKQTFVRUN4TUVUMDVCVURFY01Cb0dBMVVFQXhNVFkyVnlkR2x6YzNWbGNpNXZibUZ3TG05eVp6RU9NQXdHQTFVRQpCUk1GTVRJek5EVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDZUFLZVBUemEzCkI5K1E1d21qd0FtSVUxOG1Ec05taUkwMVcvblkzbndBaTVSeU9aeVUyTm02blV5cUI0NFdiK2cvMnNqejV6VmMKVkJwL2NGK2hucmJtd2I4TTRSMkltUnlqM0VtM2h1UHEwb091TXFQUDRZRzBKSkc3SXpNRHBMOHI0SHdSK21GSgpaVW8rWGlBYkhjaXJFeUdlNWNQTjY0NXhidVpUenpNZ3ZRMGV0TW4zcDdPVDRoVWM1R081WFRlQkluWXJFQU1tCktQaHcydk9qK1YvMHRvK2V1UStRS081cG9UV0szazhVT2x4L1FGVFhVWEMrQ1ZzNWFoRE5QWDZHYnc2aDl2MGsKUk9HOGJSWk80SGhrK05ueWdUNUx4OEVydE1PYWNtRkYxL3lxU1U1ZWhvTVdHaHFmY0x6ekZzaFJjd3BzWUJ3RApOcTAyVkhER1NFNVZBZ01CQUFHZ2daY3dnWlFHQ1NxR1NJYjNEUUVKRGpHQmhqQ0JnekJWQmdOVkhSRUVUakJNCmdnbHNiMk5oYkdodmMzU0NFMk5sY25ScGMzTjFaWEl1YjI1aGNDNXZjbWVCRFc5dVlYQkFiMjVoY0M1dmNtZUgKQkg4QUFBR0dGVzl1WVhBNkx5OWpiSFZ6ZEdWeUxteHZZMkZzTHpBTEJnTlZIUThFQkFNQ0FnUXdIUVlEVlIwbApCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQUVUeTNECmNEQ1BSSmJGbEVXWlh6NDdOWUtLMXl2QXprRTVEQlN2Qkc2bEVGU01TWXY2dDF0MzF6aU9pUm9WTXZ1WlJJZDQKZ0NNcHpwdlpXUmdwSFpNQWd1ZEI2TkRHOXBzdjJYam92KzJNOXBTS3VmS3ZIeXZ4SFR3czNOYU1ENXlQSy9JMgppVDE5TlFDbjRBZXpBclFiM3JHTVAzd2pnRFlwLzgwU0tzQTk3R3o2bkYvMEErY0dWNHprRExiYUx2RExNU2xLCkNaZmxWdFkxOFlYRXMxcUJvMVFqSlZKVFBjSTB6VkxHVlZKRExJZ1lLcmlEdldvbzFObEx5aUhnNWczaTF3NFYKc0QvOFR0UWhYaTBUWTZDdkJObGkwQTdjUDhyOHFKakxFRDlpMnFZcWxvekFvS256R1c3d2hpZ3RhYnVUTVdIRQpKWDJ2LzFGbE1lVlZ1OUMvCi0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo=") + CsrBytesWithoutNotSupportedFields, _ = base64.StdEncoding.DecodeString("LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJREJEQ0NBZXdDQVFBd2dZSXhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saApNUll3RkFZRFZRUUhFdzFUWVc0dFJuSmhibU5wYzJOdk1Sa3dGd1lEVlFRS0V4Qk1hVzUxZUMxR2IzVnVaR0YwCmFXOXVNUTB3Q3dZRFZRUUxFd1JQVGtGUU1Sd3dHZ1lEVlFRREV4TmpaWEowYVhOemRXVnlMbTl1WVhBdWIzSm4KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFuZ0NuajA4MnR3ZmZrT2NKbzhBSgppRk5mSmc3RFpvaU5OVnY1Mk41OEFJdVVjam1jbE5qWnVwMU1xZ2VPRm0vb1A5ckk4K2MxWEZRYWYzQmZvWjYyCjVzRy9ET0VkaUprY285eEp0NGJqNnRLRHJqS2p6K0dCdENTUnV5TXpBNlMvSytCOEVmcGhTV1ZLUGw0Z0d4M0kKcXhNaG51WER6ZXVPY1c3bVU4OHpJTDBOSHJUSjk2ZXprK0lWSE9SanVWMDNnU0oyS3hBREppajRjTnJ6by9sZgo5TGFQbnJrUGtDanVhYUUxaXQ1UEZEcGNmMEJVMTFGd3ZnbGJPV29RelQxK2htOE9vZmI5SkVUaHZHMFdUdUI0ClpQalo4b0UrUzhmQks3VERtbkpoUmRmOHFrbE9Yb2FERmhvYW4zQzg4eGJJVVhNS2JHQWNBemF0TmxSd3hraE8KVlFJREFRQUJvRHd3T2dZSktvWklodmNOQVFrT01TMHdLekFwQmdOVkhSRUVJakFnZ2dsc2IyTmhiR2h2YzNTQwpFMk5sY25ScGMzTjFaWEl1YjI1aGNDNXZjbWN3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUQyb1RNNmVtVUVFCkZnbnBXR3dQVEZEUWtGOStkYSt5aSthWTlYYU5aU1p0UnBseldFeGdOR2l2UVVpbHFzN2pPT1kyM0ZrbjNpZnoKd2tNbXBZWFVxSEhxeXJueURCQUxHcC9pN3pteGVQMVFwSmFMcmIzVXV3ZytXZzMzam5ZMkdkTTFxVXlRVHVqZwoxMjBNK1pOUjJOMjlKVkJZNnNyRmlWZFJGYlBoMVhVK3VFMm5KOThBK0JGOUhRLzBBaC9TaURyRlRXYktRYWdBCkhmOXY5ZlZXeTVkSG8yQldWb1JkN3hDZWV2TGpyeVdYck5wMzluOHhCaitHSEp1WXl0V1R2cnhiZHJkcU9OYnEKeXlEaWRhQlllY3NqTTJSTlRGVytJazBHazdpWHZTd2l4MWZHWEdyZ1NUNytpcGhwVHM4ekNZN3FOZmo5Z1MrNQpuQ1A4TTdrbVZiND0KLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tCg==") + PrivateKeyBytes, _ = base64.StdEncoding.DecodeString("LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktZd2dnU2lBZ0VBQW9JQkFRQ2VBS2VQVHphM0I5K1EKNXdtandBbUlVMThtRHNObWlJMDFXL25ZM253QWk1UnlPWnlVMk5tNm5VeXFCNDRXYitnLzJzano1elZjVkJwLwpjRitobnJibXdiOE00UjJJbVJ5ajNFbTNodVBxMG9PdU1xUFA0WUcwSkpHN0l6TURwTDhyNEh3UittRkpaVW8rClhpQWJIY2lyRXlHZTVjUE42NDV4YnVaVHp6TWd2UTBldE1uM3A3T1Q0aFVjNUdPNVhUZUJJbllyRUFNbUtQaHcKMnZPaitWLzB0bytldVErUUtPNXBvVFdLM2s4VU9seC9RRlRYVVhDK0NWczVhaEROUFg2R2J3Nmg5djBrUk9HOApiUlpPNEhoaytObnlnVDVMeDhFcnRNT2FjbUZGMS95cVNVNWVob01XR2hxZmNMenpGc2hSY3dwc1lCd0ROcTAyClZIREdTRTVWQWdNQkFBRUNnZ0VBZnZBdnJpY0o0a3BsQnRsWEdVT3paTjcyVm5FTjgxWkpmRkVFallOUlJHN2YKT0ZXb3dyemxUOVFJUDZJS09XUjJ4clpUTkFEUkVmaFB3WXJhaWRJbFI0Ym9vc1JKSWZGQTNUU2gxTngrUjY0UgpOT1ppVFNUek8zTVYwYThaMFJlT3J3V1BLREpyN3JkOXlsVjhuY3h6NTExYmFYbFdzaDFKTjBETnN3K0lHQWdICkcycHphdUYwOFNGSzVTVUNYRzBhK2lkOTI0ektpQzJrbzJwMVJGeUt1cWJYRWR1eUdhQVNCeDdwVUtnVWhVTzgKWjgyMENuSzB4Yi9KbnhqK0IwUVMrS2lHdDRORHY5eEVwYkxVQVJsd2czRXpaS2ozbzV2dUFKdXNjSTN4OWdZYgptZDZSVTl2NkVmRSs0NUMwb0FISlZFaTU0Qi9qVzhXK20vMXRmTFpTRVFLQmdRRFF6TUdVU3ZpaFZMR0tuTXgwCjdvTTAreVUyeHJ6bmljTHB2Y2R5LzI0QTg3NytweXhwMkRyR1dYV3ZJeE1PZnUwUWhva2VLZWEyeFVJWkttWksKVjBQVi9nUUEwdnZEWXJVekM0T1dGTk52MlhTOEYyWjhVRlI2QjFWb0ZPclF3VXFWZnEzUDg0KzNLZUYzb0U0NQpja2lUUUY0emI3R2srOHBrNk0vbDNIS0pCd0tCZ1FEQnVFTENiNXlKcXM1MlcvOWtLQ1gvZEVKNzY0NXFYMWxmCmdMQ0xvd25BWjlLQ004U1AxU0dlYVROU1hRMDNPTkxEYlJxN2FaRTgrVy9YSGZVNDZlcXNFYVlmTnZaczZPZkkKQ3RNcWNLSzlYMWNlV2p6QWowZTBuV1Jldjdvb1BENGZpck1scEs1RGoyK2RDcHlEVGlGcnRQR3ZtWXROL3BHTgp0TFlJSWJFaXd3S0JnQ1UwdHpIVGdpWk5UeTJoN3hUUEEzWkhZa1EyZWFiUWQzanJKa1VwbXRKMG5JNEgvL1FFClljVENZQ1FMSGZReFRkRHFDRGpsd29UQzlacENZZVBSVlBjbXdsUGJSSjEyVTA5N044RmdpS1NmblJ0L0YvWUMKNG1xMEw5WFNGRjJLYzkzYnF1RXBSMnlkKzkwekpYMDdEZytmSXFvTHR4M3MrQTJ2Sm95UkVaKzFBb0dBSmtYVgpTcnpxWEhpV21lUExMV3FSTjBzczUzYStpRjJXTnJ3aUpUUnM5TXFPV0pNSnZyRUpGMWNKZ0xoSnFDTmQ0S1pwCnBhVlJ3STQzZm5TNHdpblU1M0lGTkIyVHlpaXB3bWdCR0Yxd2lhR1FnTHFyVGhBQXhycGgzaVpiRFIvNTFobDkKRTI3Wnp4ZERvZTg2eE9abXkvVFAyeldteEI0TGU4ZThQeGNJL0NrQ2dZQU1YRERGOUF1L1hkcXRnLzcyTlJoSgpjd05hakErcU4wMDcvQ2NrdUwwaDRxNjY4NmRpOXAxQ0Z5Y1kvRFZvS3JCViszaklSbWdZUU5wQ3ovZEdUa0xrCkhJZWJaaTZJS2lodFNnWEhISE1VcENyTUp1c1VtaS9kUzMybzBvTGFBZ3QweEx2bTJZcFdnNVhvOG9IY0dGZzAKblZya0laQlRBUnpEV3dwZERma2pCQT09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K") +) diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/testdata/test_private_key.pem b/certServiceK8sExternalProvider/src/cmpv2provisioner/testdata/test_private_key.pem new file mode 100644 index 00000000..099593f9 --- /dev/null +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/testdata/test_private_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCeAKePTza3B9+Q +5wmjwAmIU18mDsNmiI01W/nY3nwAi5RyOZyU2Nm6nUyqB44Wb+g/2sjz5zVcVBp/ +cF+hnrbmwb8M4R2ImRyj3Em3huPq0oOuMqPP4YG0JJG7IzMDpL8r4HwR+mFJZUo+ +XiAbHcirEyGe5cPN645xbuZTzzMgvQ0etMn3p7OT4hUc5GO5XTeBInYrEAMmKPhw +2vOj+V/0to+euQ+QKO5poTWK3k8UOlx/QFTXUXC+CVs5ahDNPX6Gbw6h9v0kROG8 +bRZO4Hhk+NnygT5Lx8ErtMOacmFF1/yqSU5ehoMWGhqfcLzzFshRcwpsYBwDNq02 +VHDGSE5VAgMBAAECggEAfvAvricJ4kplBtlXGUOzZN72VnEN81ZJfFEEjYNRRG7f +OFWowrzlT9QIP6IKOWR2xrZTNADREfhPwYraidIlR4boosRJIfFA3TSh1Nx+R64R +NOZiTSTzO3MV0a8Z0ReOrwWPKDJr7rd9ylV8ncxz511baXlWsh1JN0DNsw+IGAgH +G2pzauF08SFK5SUCXG0a+id924zKiC2ko2p1RFyKuqbXEduyGaASBx7pUKgUhUO8 +Z820CnK0xb/Jnxj+B0QS+KiGt4NDv9xEpbLUARlwg3EzZKj3o5vuAJuscI3x9gYb +md6RU9v6EfE+45C0oAHJVEi54B/jW8W+m/1tfLZSEQKBgQDQzMGUSvihVLGKnMx0 +7oM0+yU2xrznicLpvcdy/24A877+pyxp2DrGWXWvIxMOfu0QhokeKea2xUIZKmZK +V0PV/gQA0vvDYrUzC4OWFNNv2XS8F2Z8UFR6B1VoFOrQwUqVfq3P84+3KeF3oE45 +ckiTQF4zb7Gk+8pk6M/l3HKJBwKBgQDBuELCb5yJqs52W/9kKCX/dEJ7645qX1lf +gLCLownAZ9KCM8SP1SGeaTNSXQ03ONLDbRq7aZE8+W/XHfU46eqsEaYfNvZs6OfI +CtMqcKK9X1ceWjzAj0e0nWRev7ooPD4firMlpK5Dj2+dCpyDTiFrtPGvmYtN/pGN +tLYIIbEiwwKBgCU0tzHTgiZNTy2h7xTPA3ZHYkQ2eabQd3jrJkUpmtJ0nI4H//QE +YcTCYCQLHfQxTdDqCDjlwoTC9ZpCYePRVPcmwlPbRJ12U097N8FgiKSfnRt/F/YC +4mq0L9XSFF2Kc93bquEpR2yd+90zJX07Dg+fIqoLtx3s+A2vJoyREZ+1AoGAJkXV +SrzqXHiWmePLLWqRN0ss53a+iF2WNrwiJTRs9MqOWJMJvrEJF1cJgLhJqCNd4KZp +paVRwI43fnS4winU53IFNB2TyiipwmgBGF1wiaGQgLqrThAAxrph3iZbDR/51hl9 +E27ZzxdDoe86xOZmy/TP2zWmxB4Le8e8PxcI/CkCgYAMXDDF9Au/Xdqtg/72NRhJ +cwNajA+qN007/CckuL0h4q6686di9p1CFycY/DVoKrBV+3jIRmgYQNpCz/dGTkLk +HIebZi6IKihtSgXHHHMUpCrMJusUmi/dS32o0oLaAgt0xLvm2YpWg5Xo8oHcGFg0 +nVrkIZBTARzDWwpdDfkjBA== +-----END PRIVATE KEY----- diff --git a/certServiceK8sExternalProvider/src/testdata/provider.go b/certServiceK8sExternalProvider/src/testdata/provider.go new file mode 100644 index 00000000..2e352cbf --- /dev/null +++ b/certServiceK8sExternalProvider/src/testdata/provider.go @@ -0,0 +1,89 @@ +/* + * ============LICENSE_START======================================================= + * oom-certservice-k8s-external-provider + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package testdata + +import ( + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" +) + +const ( + SecretName = "issuer-cert-secret" + Url = "https://oom-cert-service:8443/v1/certificate/" + HealthEndpoint = "actuator/health" + CertEndpoint = "v1/certificate" + CaName = "RA" + KeySecretKey = "cmpv2Issuer-key.pem" + CertSecretKey = "cmpv2Issuer-cert.pem" + CacertSecretKey = "cacert.pem" + Namespace = "default" + IssuerObjectName = "fakeIssuer" + Kind = "CMPv2Issuer" + APIVersion = "v1" +) + +func GetValidIssuerWithSecret() (cmpv2api.CMPv2Issuer, v1.Secret) { + issuer := cmpv2api.CMPv2Issuer{ + + TypeMeta: metav1.TypeMeta{ + APIVersion: APIVersion, + Kind: Kind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: IssuerObjectName, + Namespace: Namespace, + }, + Spec: GetValidCMPv2IssuerSpec(), + } + secret := v1.Secret{ + + Data: map[string][]byte{ + KeySecretKey: KeyBytes, + CertSecretKey: CertBytes, + CacertSecretKey: CacertBytes, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: SecretName, + Namespace: Namespace, + }, + } + secret.Name = SecretName + return issuer, secret +} + +func GetValidCMPv2IssuerSpec() cmpv2api.CMPv2IssuerSpec { + issuerSpec := cmpv2api.CMPv2IssuerSpec{ + URL: Url, + HealthEndpoint: HealthEndpoint, + CertEndpoint: CertEndpoint, + CaName: CaName, + CertSecretRef: cmpv2api.SecretKeySelector{ + Name: SecretName, + KeyRef: KeySecretKey, + CertRef: CertSecretKey, + CacertRef: CacertSecretKey, + }, + } + return issuerSpec +} + diff --git a/certServiceK8sExternalProvider/src/x509/testdata/test_data.go b/certServiceK8sExternalProvider/src/x509/testdata/test_data.go index dad5d094..b45d6102 100644 --- a/certServiceK8sExternalProvider/src/x509/testdata/test_data.go +++ b/certServiceK8sExternalProvider/src/x509/testdata/test_data.go @@ -20,8 +20,8 @@ package testdata - -const ValidCertificateSignRequest = (`-----BEGIN CERTIFICATE REQUEST----- +const ( + ValidCertificateSignRequest = `-----BEGIN CERTIFICATE REQUEST----- MIIDgjCCAmoCAQAwgaQxCzAJBgNVBAYTAlBMMRMwEQYDVQQIEwpEb2xueVNsYXNr MRAwDgYDVQQHEwdXcm9jbGF3MREwDwYDVQQJEwhMb3RuaWN6YTEPMA0GA1UEERMG MTItMzQ1MQ0wCwYDVQQKEwRPTkFQMQ0wCwYDVQQLEwRvbmFwMRwwGgYDVQQDExNj @@ -41,9 +41,8 @@ dnjRRFio75nCNRJdLOJ9FU3wIgdDZwGaiXdn5NDQxCe0BWcbElDJSYR/xOi7V0AM bcm17a8owa5dH9u/rmTmlrIT7PV4JHkZIogctIcSqod6xdr1mbi8G9DMFAqV+o7W 9kV7XDKhTqYoBIsXwfehNMu3lo72VuklIyVNiEVz4mVzpeZy2DgjRjCLt106yDHZ f3nco6O4y2EyexBVKq6QRFfZDUab6YcoEVvPAio01RmFrHgnxHs= ------END CERTIFICATE REQUEST-----`) - -const InvalidCertificateSignRequest = (`-----BEGIN INVALID REQUEST----- +-----END CERTIFICATE REQUEST-----` + InvalidCertificateSignRequest = `-----BEGIN INVALID REQUEST----- MIIDgjCCAmoCAQAwgaQxCzAJBgNVBAYTAlBMMRMwEQYDVQQIEwpEb2xueVNsYXNr MRAwDgYDVQQHEwdXcm9jbGF3MREwDwYDVQQJEwhMb3RuaWN6YTEPMA0GA1UEERMG MTItMzQ1MQ0wCwYDVQQKEwRPTkFQMQ0wCwYDVQQLEwRvbmFwMRwwGgYDVQQDExNj @@ -63,4 +62,61 @@ dnjRRFio75nCNRJdLOJ9FU3wIgdDZwGaiXdn5NDQxCe0BWcbElDJSYR/xOi7V0AM bcm17a8owa5dH9u/rmTmlrIT7PV4JHkZIogctIcSqod6xdr1mbi8G9DMFAqV+o7W 9kV7XDKhTqYoBIsXwfehNMu3lo72VuklIyVNiEVz4mVzpeZy2DgjRjCLt106yDHZ f3nco6O4y2EyexBVKq6QRFfZDUab6YcoEVvPAio01RmFrHgnxHs= ------END CERTIFICATE REQUEST-----`) +-----END CERTIFICATE REQUEST-----` + ValidPrivateKey = `-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCeAKePTza3B9+Q +5wmjwAmIU18mDsNmiI01W/nY3nwAi5RyOZyU2Nm6nUyqB44Wb+g/2sjz5zVcVBp/ +cF+hnrbmwb8M4R2ImRyj3Em3huPq0oOuMqPP4YG0JJG7IzMDpL8r4HwR+mFJZUo+ +XiAbHcirEyGe5cPN645xbuZTzzMgvQ0etMn3p7OT4hUc5GO5XTeBInYrEAMmKPhw +2vOj+V/0to+euQ+QKO5poTWK3k8UOlx/QFTXUXC+CVs5ahDNPX6Gbw6h9v0kROG8 +bRZO4Hhk+NnygT5Lx8ErtMOacmFF1/yqSU5ehoMWGhqfcLzzFshRcwpsYBwDNq02 +VHDGSE5VAgMBAAECggEAfvAvricJ4kplBtlXGUOzZN72VnEN81ZJfFEEjYNRRG7f +OFWowrzlT9QIP6IKOWR2xrZTNADREfhPwYraidIlR4boosRJIfFA3TSh1Nx+R64R +NOZiTSTzO3MV0a8Z0ReOrwWPKDJr7rd9ylV8ncxz511baXlWsh1JN0DNsw+IGAgH +G2pzauF08SFK5SUCXG0a+id924zKiC2ko2p1RFyKuqbXEduyGaASBx7pUKgUhUO8 +Z820CnK0xb/Jnxj+B0QS+KiGt4NDv9xEpbLUARlwg3EzZKj3o5vuAJuscI3x9gYb +md6RU9v6EfE+45C0oAHJVEi54B/jW8W+m/1tfLZSEQKBgQDQzMGUSvihVLGKnMx0 +7oM0+yU2xrznicLpvcdy/24A877+pyxp2DrGWXWvIxMOfu0QhokeKea2xUIZKmZK +V0PV/gQA0vvDYrUzC4OWFNNv2XS8F2Z8UFR6B1VoFOrQwUqVfq3P84+3KeF3oE45 +ckiTQF4zb7Gk+8pk6M/l3HKJBwKBgQDBuELCb5yJqs52W/9kKCX/dEJ7645qX1lf +gLCLownAZ9KCM8SP1SGeaTNSXQ03ONLDbRq7aZE8+W/XHfU46eqsEaYfNvZs6OfI +CtMqcKK9X1ceWjzAj0e0nWRev7ooPD4firMlpK5Dj2+dCpyDTiFrtPGvmYtN/pGN +tLYIIbEiwwKBgCU0tzHTgiZNTy2h7xTPA3ZHYkQ2eabQd3jrJkUpmtJ0nI4H//QE +YcTCYCQLHfQxTdDqCDjlwoTC9ZpCYePRVPcmwlPbRJ12U097N8FgiKSfnRt/F/YC +4mq0L9XSFF2Kc93bquEpR2yd+90zJX07Dg+fIqoLtx3s+A2vJoyREZ+1AoGAJkXV +SrzqXHiWmePLLWqRN0ss53a+iF2WNrwiJTRs9MqOWJMJvrEJF1cJgLhJqCNd4KZp +paVRwI43fnS4winU53IFNB2TyiipwmgBGF1wiaGQgLqrThAAxrph3iZbDR/51hl9 +E27ZzxdDoe86xOZmy/TP2zWmxB4Le8e8PxcI/CkCgYAMXDDF9Au/Xdqtg/72NRhJ +cwNajA+qN007/CckuL0h4q6686di9p1CFycY/DVoKrBV+3jIRmgYQNpCz/dGTkLk +HIebZi6IKihtSgXHHHMUpCrMJusUmi/dS32o0oLaAgt0xLvm2YpWg5Xo8oHcGFg0 +nVrkIZBTARzDWwpdDfkjBA== +-----END PRIVATE KEY-----` + InvalidPrivateKey = `-----BEGIN INVALID KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCeAKePTza3B9+Q +5wmjwAmIU18mDsNmiI01W/nY3nwAi5RyOZyU2Nm6nUyqB44Wb+g/2sjz5zVcVBp/ +cF+hnrbmwb8M4R2ImRyj3Em3huPq0oOuMqPP4YG0JJG7IzMDpL8r4HwR+mFJZUo+ +XiAbHcirEyGe5cPN645xbuZTzzMgvQ0etMn3p7OT4hUc5GO5XTeBInYrEAMmKPhw +2vOj+V/0to+euQ+QKO5poTWK3k8UOlx/QFTXUXC+CVs5ahDNPX6Gbw6h9v0kROG8 +bRZO4Hhk+NnygT5Lx8ErtMOacmFF1/yqSU5ehoMWGhqfcLzzFshRcwpsYBwDNq02 +VHDGSE5VAgMBAAECggEAfvAvricJ4kplBtlXGUOzZN72VnEN81ZJfFEEjYNRRG7f +OFWowrzlT9QIP6IKOWR2xrZTNADREfhPwYraidIlR4boosRJIfFA3TSh1Nx+R64R +NOZiTSTzO3MV0a8Z0ReOrwWPKDJr7rd9ylV8ncxz511baXlWsh1JN0DNsw+IGAgH +G2pzauF08SFK5SUCXG0a+id924zKiC2ko2p1RFyKuqbXEduyGaASBx7pUKgUhUO8 +Z820CnK0xb/Jnxj+B0QS+KiGt4NDv9xEpbLUARlwg3EzZKj3o5vuAJuscI3x9gYb +md6RU9v6EfE+45C0oAHJVEi54B/jW8W+m/1tfLZSEQKBgQDQzMGUSvihVLGKnMx0 +7oM0+yU2xrznicLpvcdy/24A877+pyxp2DrGWXWvIxMOfu0QhokeKea2xUIZKmZK +V0PV/gQA0vvDYrUzC4OWFNNv2XS8F2Z8UFR6B1VoFOrQwUqVfq3P84+3KeF3oE45 +ckiTQF4zb7Gk+8pk6M/l3HKJBwKBgQDBuELCb5yJqs52W/9kKCX/dEJ7645qX1lf +gLCLownAZ9KCM8SP1SGeaTNSXQ03ONLDbRq7aZE8+W/XHfU46eqsEaYfNvZs6OfI +CtMqcKK9X1ceWjzAj0e0nWRev7ooPD4firMlpK5Dj2+dCpyDTiFrtPGvmYtN/pGN +tLYIIbEiwwKBgCU0tzHTgiZNTy2h7xTPA3ZHYkQ2eabQd3jrJkUpmtJ0nI4H//QE +YcTCYCQLHfQxTdDqCDjlwoTC9ZpCYePRVPcmwlPbRJ12U097N8FgiKSfnRt/F/YC +4mq0L9XSFF2Kc93bquEpR2yd+90zJX07Dg+fIqoLtx3s+A2vJoyREZ+1AoGAJkXV +SrzqXHiWmePLLWqRN0ss53a+iF2WNrwiJTRs9MqOWJMJvrEJF1cJgLhJqCNd4KZp +paVRwI43fnS4winU53IFNB2TyiipwmgBGF1wiaGQgLqrThAAxrph3iZbDR/51hl9 +E27ZzxdDoe86xOZmy/TP2zWmxB4Le8e8PxcI/CkCgYAMXDDF9Au/Xdqtg/72NRhJ +cwNajA+qN007/CckuL0h4q6686di9p1CFycY/DVoKrBV+3jIRmgYQNpCz/dGTkLk +HIebZi6IKihtSgXHHHMUpCrMJusUmi/dS32o0oLaAgt0xLvm2YpWg5Xo8oHcGFg0 +nVrkIZBTARzDWwpdDfkjBA== +-----END PRIVATE KEY-----` +) diff --git a/certServiceK8sExternalProvider/src/x509/x509_utils.go b/certServiceK8sExternalProvider/src/x509/x509_utils.go index b2530132..e1e887f0 100644 --- a/certServiceK8sExternalProvider/src/x509/x509_utils.go +++ b/certServiceK8sExternalProvider/src/x509/x509_utils.go @@ -27,14 +27,16 @@ import ( "fmt" ) -// decodeCSR decodes a certificate request in PEM format and returns the +const ( + PemCsrType = "CERTIFICATE REQUEST" + pemPrivateKeyType = "PRIVATE KEY" +) + +// decodeCSR decodes a certificate request in PEM format func DecodeCSR(data []byte) (*x509.CertificateRequest, error) { - block, rest := pem.Decode(data) - if block == nil || len(rest) > 0 { - return nil, fmt.Errorf("unexpected CSR PEM on sign request") - } - if block.Type != "CERTIFICATE REQUEST" { - return nil, fmt.Errorf("PEM is not a certificate request") + block, err := decodePemBlock(data, PemCsrType) + if err != nil { + return nil, fmt.Errorf("error decoding CSR PEM: %v", err) } csr, err := x509.ParseCertificateRequest(block.Bytes) if err != nil { @@ -46,16 +48,30 @@ func DecodeCSR(data []byte) (*x509.CertificateRequest, error) { return csr, nil } -// encodeX509 will encode a *x509.Certificate into PEM format. -func EncodeX509(cert *x509.Certificate) ([]byte, error) { - caPem := bytes.NewBuffer([]byte{}) - err := pem.Encode(caPem, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) +func DecodePrivateKey(data []byte) (interface{}, error) { + block, err := decodePemBlock(data, pemPrivateKeyType) + if err != nil { + return nil, fmt.Errorf("error decoding Private Key PEM: %v", err) + } + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { - return nil, err + return nil, fmt.Errorf("error parsing Private Key: %v", err) } - return caPem.Bytes(), nil + return key, nil } +func decodePemBlock(data []byte, pemType string) (*pem.Block, error) { + block, rest := pem.Decode(data) + if block == nil || len(rest) > 0 { + return nil, fmt.Errorf("unexpected PEM") + } + if block.Type != pemType { + return nil, fmt.Errorf("PEM is not: %s", pemType) + } + return block, nil +} + + func ParseCertificateArrayToBytes(certificateArray []string) ([]byte, error) { buffer := bytes.NewBuffer([]byte{}) for _, cert := range certificateArray { diff --git a/certServiceK8sExternalProvider/src/x509/x509_utils_test.go b/certServiceK8sExternalProvider/src/x509/x509_utils_test.go index 2692bf4e..c867d2e8 100644 --- a/certServiceK8sExternalProvider/src/x509/x509_utils_test.go +++ b/certServiceK8sExternalProvider/src/x509/x509_utils_test.go @@ -28,15 +28,28 @@ import ( "onap.org/oom-certservice/k8s-external-provider/src/x509/testdata" ) -func TestShouldDecodeCSR(t *testing.T) { +func Test_DecodeCSR_ShouldDecodeValidCsr(t *testing.T) { csr, err := DecodeCSR([]byte(testdata.ValidCertificateSignRequest)) assert.Nil(t, err) assert.Equal(t, "ONAP", csr.Subject.Organization[0]) } -func TestShouldReturnError(t *testing.T) { +func Test_DecodeCSR_ShouldReturnErrorForInvalidCsr(t *testing.T) { _, err := DecodeCSR([]byte(testdata.InvalidCertificateSignRequest)) - assert.NotNil(t, err) + assert.Error(t, err) +} + +func Test_DecodePrivateKey_ShouldDecodeValidPrivateKey(t *testing.T) { + privateKey, err := DecodePrivateKey([]byte(testdata.ValidPrivateKey)) + + assert.Nil(t, err) + assert.NotNil(t, privateKey) +} + +func Test_DecodePrivateKey_ShouldReturnErrorForInvalidPrivateKey(t *testing.T) { + _, err := DecodePrivateKey([]byte(testdata.InvalidPrivateKey)) + + assert.Error(t, err) } |