From 52c38a5529488c45ce21338393487863a703074e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Tue, 9 Jan 2024 09:27:42 +0100 Subject: [PATCH] combine certificate and private key status --- api/deployments/models/tls.go | 47 ++----- ...environment_controller_externaldns_test.go | 117 +----------------- api/models/component.go | 42 ++----- ...aldns_tls.go => update_externaldns_tls.go} | 6 +- api/secrets/secret_controller.go | 14 +-- api/secrets/secret_controller_test.go | 38 ++---- api/secrets/secret_handler.go | 7 +- api/utils/tlsvalidation/interface.go | 5 - .../mock/tls_secret_validator_mock.go | 15 --- swaggerui/html/swagger.json | 84 +++++-------- 10 files changed, 84 insertions(+), 291 deletions(-) rename api/secrets/models/{set_externaldns_tls.go => update_externaldns_tls.go} (50%) diff --git a/api/deployments/models/tls.go b/api/deployments/models/tls.go index 7c1568ac..d7d4ab91 100644 --- a/api/deployments/models/tls.go +++ b/api/deployments/models/tls.go @@ -6,28 +6,16 @@ import ( "time" ) -// swagger:enum PrivateKeyStatusEnum -type PrivateKeyStatusEnum string +// swagger:enum TLSStatusEnum +type TLSStatusEnum string const ( - // Private key is not set - PrivateKeyPending PrivateKeyStatusEnum = "Pending" - // Private key is valid - PrivateKeyConsistent PrivateKeyStatusEnum = "Consistent" - // Private key is invalid - PrivateKeyInvalid PrivateKeyStatusEnum = "Invalid" -) - -// swagger:enum CertificateStatusEnum -type CertificateStatusEnum string - -const ( - // Certificate is not set - CertificatePending CertificateStatusEnum = "Pending" - // Certificate is valid - CertificateConsistent CertificateStatusEnum = "Consistent" - // Certificate is invalid - CertificateInvalid CertificateStatusEnum = "Invalid" + // TLS certificate and private key not set + TLSStatusPending TLSStatusEnum = "Pending" + // TLS certificate and private key is valid + TLSStatusConsistent TLSStatusEnum = "Consistent" + // TLS certificate and private key is invalid + TLSStatusInvalid TLSStatusEnum = "Invalid" ) // TLS configuration and status for external DNS @@ -38,27 +26,16 @@ type TLS struct { // required: true UseAutomation bool `json:"useAutomation"` - // Status of the private key - // - // required: true - // example: Consistent - PrivateKeyStatus PrivateKeyStatusEnum `json:"privateKeyStatus"` - - // PrivateKeyStatusMessages contains a list of messages related to PrivateKeyStatus - // - // required: false - PrivateKeyStatusMessages []string `json:"privateKeyStatusMessages,omitempty"` - - // Status of the certificate + // Status of TLS certificate and private key // // required: true // example: Consistent - CertificateStatus CertificateStatusEnum `json:"certificateStatus"` + Status TLSStatusEnum `json:"status"` - // CertificateStatusMessages contains a list of messages related to CertificateStatus + // StatusMessages contains a list of messages related to Status // // required: false - CertificateStatusMessages []string `json:"certificateStatusMessages,omitempty"` + StatusMessages []string `json:"statusMessages,omitempty"` // Certificates holds the X509 certificate chain // The first certificate in the list should be the host certificate and the rest should be intermediate certificates diff --git a/api/environments/environment_controller_externaldns_test.go b/api/environments/environment_controller_externaldns_test.go index 3002173f..4e301259 100644 --- a/api/environments/environment_controller_externaldns_test.go +++ b/api/environments/environment_controller_externaldns_test.go @@ -124,7 +124,6 @@ func (s *externalDnsTestSuite) Test_ExternalDNS_Consistent() { dnsNames := []string{"dns1", "dns2"} keyBytes, certBytes := []byte("any key"), s.buildCertificate(certCN, issuerCN, dnsNames, notBefore, notAfter) - s.tlsValidator.EXPECT().ValidatePrivateKey(keyBytes).Return(true, nil).Times(1) s.tlsValidator.EXPECT().ValidateX509Certificate(certBytes, keyBytes, s.alias).Return(true, nil).Times(1) sut := initHandler(s.kubeClient, s.radixClient, s.secretProviderClient) @@ -151,8 +150,7 @@ func (s *externalDnsTestSuite) Test_ExternalDNS_Consistent() { expectedExternalDNS := []deploymentModels.ExternalDNS{{ FQDN: s.alias, TLS: deploymentModels.TLS{ - PrivateKeyStatus: deploymentModels.PrivateKeyConsistent, - CertificateStatus: deploymentModels.CertificateConsistent, + Status: deploymentModels.TLSStatusConsistent, Certificates: []deploymentModels.X509Certificate{{ Subject: "CN=" + certCN, Issuer: "CN=" + issuerCN, @@ -165,15 +163,13 @@ func (s *externalDnsTestSuite) Test_ExternalDNS_Consistent() { s.ElementsMatch(expectedExternalDNS, environment.ActiveDeployment.Components[0].ExternalDNS) } -func (s *externalDnsTestSuite) Test_ExternalDNS_MissingKeyData() { +func (s *externalDnsTestSuite) Test_ExternalDNS_MissingPrivateKeyData() { notBefore, _ := time.Parse("2006-01-02", "2020-07-01") notAfter, _ := time.Parse("2006-01-02", "2020-08-01") certCN, issuerCN := "one.example.com", "issuer.example.com" dnsNames := []string{"dns1", "dns2"} certBytes := s.buildCertificate(certCN, issuerCN, dnsNames, notBefore, notAfter) - s.tlsValidator.EXPECT().ValidateX509Certificate(certBytes, nil, s.alias).Return(true, nil).Times(1) - sut := initHandler(s.kubeClient, s.radixClient, s.secretProviderClient) sut.tlsValidator = s.tlsValidator @@ -198,65 +194,7 @@ func (s *externalDnsTestSuite) Test_ExternalDNS_MissingKeyData() { expectedExternalDNS := []deploymentModels.ExternalDNS{{ FQDN: s.alias, TLS: deploymentModels.TLS{ - PrivateKeyStatus: deploymentModels.PrivateKeyPending, - CertificateStatus: deploymentModels.CertificateConsistent, - Certificates: []deploymentModels.X509Certificate{{ - Subject: "CN=" + certCN, - Issuer: "CN=" + issuerCN, - NotBefore: notBefore, - NotAfter: notAfter, - DNSNames: dnsNames, - }}, - }, - }} - s.ElementsMatch(expectedExternalDNS, environment.ActiveDeployment.Components[0].ExternalDNS) -} - -func (s *externalDnsTestSuite) Test_ExternalDNS_KeyDataValidationError() { - notBefore, _ := time.Parse("2006-01-02", "2020-07-01") - notAfter, _ := time.Parse("2006-01-02", "2020-08-01") - certCN, issuerCN := "one.example.com", "issuer.example.com" - dnsNames := []string{"dns1", "dns2"} - keyBytes, certBytes := []byte("any key"), s.buildCertificate(certCN, issuerCN, dnsNames, notBefore, notAfter) - keyValidationMsg := "any message" - - s.tlsValidator.EXPECT().ValidatePrivateKey(keyBytes).Return(false, []string{keyValidationMsg}).Times(1) - s.tlsValidator.EXPECT().ValidateX509Certificate(certBytes, keyBytes, s.alias).Return(true, nil).Times(1) - - sut := initHandler(s.kubeClient, s.radixClient, s.secretProviderClient) - sut.tlsValidator = s.tlsValidator - - _, err := s.kubeClient.CoreV1().Secrets(s.appName+"-"+s.environmentName).Create(context.Background(), - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: s.alias, - }, - Data: map[string][]byte{ - corev1.TLSPrivateKeyKey: keyBytes, - corev1.TLSCertKey: certBytes, - }, - }, - metav1.CreateOptions{}, - ) - require.NoError(s.T(), err) - - environment, statusCode, err := s.executeRequest(s.appName, s.environmentName) - s.Equal(statusCode, 200) - s.NoError(err) - - expectedExternalDNS := []deploymentModels.ExternalDNS{{ - FQDN: s.alias, - TLS: deploymentModels.TLS{ - PrivateKeyStatus: deploymentModels.PrivateKeyInvalid, - PrivateKeyStatusMessages: []string{keyValidationMsg}, - CertificateStatus: deploymentModels.CertificateConsistent, - Certificates: []deploymentModels.X509Certificate{{ - Subject: "CN=" + certCN, - Issuer: "CN=" + issuerCN, - NotBefore: notBefore, - NotAfter: notAfter, - DNSNames: dnsNames, - }}, + Status: deploymentModels.TLSStatusPending, }, }} s.ElementsMatch(expectedExternalDNS, environment.ActiveDeployment.Components[0].ExternalDNS) @@ -264,9 +202,6 @@ func (s *externalDnsTestSuite) Test_ExternalDNS_KeyDataValidationError() { func (s *externalDnsTestSuite) Test_ExternalDNS_MissingCertData() { keyBytes := []byte("any key") - - s.tlsValidator.EXPECT().ValidatePrivateKey(keyBytes).Return(true, nil).Times(1) - sut := initHandler(s.kubeClient, s.radixClient, s.secretProviderClient) sut.tlsValidator = s.tlsValidator @@ -291,45 +226,7 @@ func (s *externalDnsTestSuite) Test_ExternalDNS_MissingCertData() { expectedExternalDNS := []deploymentModels.ExternalDNS{{ FQDN: s.alias, TLS: deploymentModels.TLS{ - PrivateKeyStatus: deploymentModels.PrivateKeyConsistent, - CertificateStatus: deploymentModels.CertificatePending, - }, - }} - s.ElementsMatch(expectedExternalDNS, environment.ActiveDeployment.Components[0].ExternalDNS) -} - -func (s *externalDnsTestSuite) Test_ExternalDNS_CertDataParseError() { - keyBytes, certBytes := []byte("any key"), []byte("any cert") - - s.tlsValidator.EXPECT().ValidatePrivateKey(keyBytes).Return(true, nil).Times(1) - s.tlsValidator.EXPECT().ValidateX509Certificate(certBytes, keyBytes, s.alias).Return(true, nil).Times(1) - - sut := initHandler(s.kubeClient, s.radixClient, s.secretProviderClient) - sut.tlsValidator = s.tlsValidator - - _, err := s.kubeClient.CoreV1().Secrets(s.appName+"-"+s.environmentName).Create(context.Background(), - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: s.alias, - }, - Data: map[string][]byte{ - corev1.TLSPrivateKeyKey: keyBytes, - corev1.TLSCertKey: certBytes, - }, - }, - metav1.CreateOptions{}, - ) - require.NoError(s.T(), err) - - environment, statusCode, err := s.executeRequest(s.appName, s.environmentName) - s.Equal(statusCode, 200) - s.NoError(err) - - expectedExternalDNS := []deploymentModels.ExternalDNS{{ - FQDN: s.alias, - TLS: deploymentModels.TLS{ - PrivateKeyStatus: deploymentModels.PrivateKeyConsistent, - CertificateStatus: deploymentModels.CertificateConsistent, + Status: deploymentModels.TLSStatusPending, }, }} s.ElementsMatch(expectedExternalDNS, environment.ActiveDeployment.Components[0].ExternalDNS) @@ -343,7 +240,6 @@ func (s *externalDnsTestSuite) Test_ExternalDNS_CertDataValidationError() { keyBytes, certBytes := []byte("any key"), s.buildCertificate(certCN, issuerCN, dnsNames, notBefore, notAfter) certValidationMsg := "any msg" - s.tlsValidator.EXPECT().ValidatePrivateKey(keyBytes).Return(true, nil).Times(1) s.tlsValidator.EXPECT().ValidateX509Certificate(certBytes, keyBytes, s.alias).Return(false, []string{certValidationMsg}).Times(1) sut := initHandler(s.kubeClient, s.radixClient, s.secretProviderClient) @@ -370,9 +266,8 @@ func (s *externalDnsTestSuite) Test_ExternalDNS_CertDataValidationError() { expectedExternalDNS := []deploymentModels.ExternalDNS{{ FQDN: s.alias, TLS: deploymentModels.TLS{ - PrivateKeyStatus: deploymentModels.PrivateKeyConsistent, - CertificateStatus: deploymentModels.CertificateInvalid, - CertificateStatusMessages: []string{certValidationMsg}, + Status: deploymentModels.TLSStatusInvalid, + StatusMessages: []string{certValidationMsg}, Certificates: []deploymentModels.X509Certificate{{ Subject: "CN=" + certCN, Issuer: "CN=" + issuerCN, diff --git a/api/models/component.go b/api/models/component.go index 0f99f2ac..a2b98cda 100644 --- a/api/models/component.go +++ b/api/models/component.go @@ -81,42 +81,26 @@ func getComponentExternalDNS(component radixv1.RadixCommonDeployComponent, secre for _, externalAlias := range component.GetExternalDNS() { var certData, keyData []byte - certStatus := deploymentModels.CertificateConsistent - keyStatus := deploymentModels.PrivateKeyConsistent + status := deploymentModels.TLSStatusConsistent if secretValue, ok := slice.FindFirst(secretList, isSecretWithName(externalAlias.FQDN)); ok { certData = secretValue.Data[corev1.TLSCertKey] - if certValue := strings.TrimSpace(string(certData)); len(certValue) == 0 || strings.EqualFold(certValue, secretDefaultData) { - certStatus = deploymentModels.CertificatePending - certData = nil - } - keyData = secretValue.Data[corev1.TLSPrivateKeyKey] - if keyValue := strings.TrimSpace(string(keyData)); len(keyValue) == 0 || strings.EqualFold(keyValue, secretDefaultData) { - keyStatus = deploymentModels.PrivateKeyPending - keyData = nil + if certValue, keyValue := strings.TrimSpace(string(certData)), strings.TrimSpace(string(keyData)); len(certValue) == 0 || len(keyValue) == 0 || strings.EqualFold(certValue, secretDefaultData) || strings.EqualFold(keyValue, secretDefaultData) { + status = deploymentModels.TLSStatusPending } } else { - certStatus = deploymentModels.CertificatePending - keyStatus = deploymentModels.PrivateKeyPending + status = deploymentModels.TLSStatusPending } var x509Certs []deploymentModels.X509Certificate - var certStatusMessages []string - if certStatus == deploymentModels.CertificateConsistent { + var statusMessages []string + if status == deploymentModels.TLSStatusConsistent { x509Certs = append(x509Certs, deploymentModels.ParseX509CertificatesFromPEM(certData)...) if certIsValid, messages := tlsValidator.ValidateX509Certificate(certData, keyData, externalAlias.FQDN); !certIsValid { - certStatus = deploymentModels.CertificateInvalid - certStatusMessages = append(certStatusMessages, messages...) - } - } - - var keyStatusMessages []string - if keyStatus == deploymentModels.PrivateKeyConsistent { - if keyIsValid, messages := tlsValidator.ValidatePrivateKey(keyData); !keyIsValid { - keyStatus = deploymentModels.PrivateKeyInvalid - keyStatusMessages = append(keyStatusMessages, messages...) + status = deploymentModels.TLSStatusInvalid + statusMessages = append(statusMessages, messages...) } } @@ -124,12 +108,10 @@ func getComponentExternalDNS(component radixv1.RadixCommonDeployComponent, secre deploymentModels.ExternalDNS{ FQDN: externalAlias.FQDN, TLS: deploymentModels.TLS{ - UseAutomation: externalAlias.UseCertificateAutomation, - PrivateKeyStatus: keyStatus, - PrivateKeyStatusMessages: keyStatusMessages, - CertificateStatus: certStatus, - CertificateStatusMessages: certStatusMessages, - Certificates: x509Certs, + UseAutomation: externalAlias.UseCertificateAutomation, + Status: status, + StatusMessages: statusMessages, + Certificates: x509Certs, }, }, ) diff --git a/api/secrets/models/set_externaldns_tls.go b/api/secrets/models/update_externaldns_tls.go similarity index 50% rename from api/secrets/models/set_externaldns_tls.go rename to api/secrets/models/update_externaldns_tls.go index 2e0d270b..12bea409 100644 --- a/api/secrets/models/set_externaldns_tls.go +++ b/api/secrets/models/update_externaldns_tls.go @@ -1,8 +1,8 @@ package models -// SetExternalDNSTLSRequest describes request body for setting private key and certificate for external DNS TLS -// swagger:model SetExternalDNSTLSRequest -type SetExternalDNSTLSRequest struct { +// UpdateExternalDNSTLSRequest describes request body for setting private key and certificate for external DNS TLS +// swagger:model UpdateExternalDnsTlsRequest +type UpdateExternalDNSTLSRequest struct { // Private key in PEM format // // required: true diff --git a/api/secrets/secret_controller.go b/api/secrets/secret_controller.go index 9e07186f..b881c529 100644 --- a/api/secrets/secret_controller.go +++ b/api/secrets/secret_controller.go @@ -40,7 +40,7 @@ func (c *secretController) GetRoutes() models.Routes { models.Route{ Path: rootPath + "/environments/{envName}/components/{componentName}/externaldns/{fqdn}/tls", Method: "PUT", - HandlerFunc: c.SetComponentExternalDNSTLS, + HandlerFunc: c.UpdateComponentExternalDNSTLS, }, } return routes @@ -203,9 +203,9 @@ func (c *secretController) GetAzureKeyVaultSecretVersions(accounts models.Accoun c.JSONResponse(w, r, secretStatuses) } -// SetComponentExternalDNSTLS Set external DNS TLS private key and certificate for a component -func (c *secretController) SetComponentExternalDNSTLS(accounts models.Accounts, w http.ResponseWriter, r *http.Request) { - // swagger:operation PUT /applications/{appName}/environments/{envName}/components/{componentName}/externaldns/{fqdn}/tls component setComponentExternalDNSTLS +// UpdateComponentExternalDNSTLS Set external DNS TLS private key and certificate for a component +func (c *secretController) UpdateComponentExternalDNSTLS(accounts models.Accounts, w http.ResponseWriter, r *http.Request) { + // swagger:operation PUT /applications/{appName}/environments/{envName}/components/{componentName}/externaldns/{fqdn}/tls component updateComponentExternalDnsTls // --- // summary: Set external DNS TLS private key certificate for a component // parameters: @@ -234,7 +234,7 @@ func (c *secretController) SetComponentExternalDNSTLS(accounts models.Accounts, // description: New TLS private key and certificate // required: true // schema: - // "$ref": "#/definitions/SetExternalDNSTLSRequest" + // "$ref": "#/definitions/UpdateExternalDnsTlsRequest" // - name: Impersonate-User // in: header // description: Works only with custom setup of cluster. Allow impersonation of test users (Required if Impersonate-Group is set) @@ -266,7 +266,7 @@ func (c *secretController) SetComponentExternalDNSTLS(accounts models.Accounts, componentName := mux.Vars(r)["componentName"] fqdn := mux.Vars(r)["fqdn"] - var requestBody secretModels.SetExternalDNSTLSRequest + var requestBody secretModels.UpdateExternalDNSTLSRequest if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil { c.ErrorResponse(w, r, err) return @@ -274,7 +274,7 @@ func (c *secretController) SetComponentExternalDNSTLS(accounts models.Accounts, handler := c.getSecretHandler(accounts) - if err := handler.SetComponentExternalDNSSecretData(r.Context(), appName, envName, componentName, fqdn, requestBody.Certificate, requestBody.PrivateKey); err != nil { + if err := handler.UpdateComponentExternalDNSSecretData(r.Context(), appName, envName, componentName, fqdn, requestBody.Certificate, requestBody.PrivateKey); err != nil { c.ErrorResponse(w, r, err) return } diff --git a/api/secrets/secret_controller_test.go b/api/secrets/secret_controller_test.go index 4c422f7a..2324b5d5 100644 --- a/api/secrets/secret_controller_test.go +++ b/api/secrets/secret_controller_test.go @@ -302,7 +302,7 @@ func (s *externalDNSSecretTestSuite) setupSecretForExternalDNS(namespace, fqdn s return err } -func (s *externalDNSSecretTestSuite) executeRequest(appName, envName, componentName, fqdn string, body *secretModels.SetExternalDNSTLSRequest) *httptest.ResponseRecorder { +func (s *externalDNSSecretTestSuite) executeRequest(appName, envName, componentName, fqdn string, body *secretModels.UpdateExternalDNSTLSRequest) *httptest.ResponseRecorder { endpoint := fmt.Sprintf("/api/v1/applications/%s/environments/%s/components/%s/externaldns/%s/tls", appName, envName, componentName, fqdn) responseCh := s.controllerTestUtils.ExecuteRequestWithParameters(http.MethodPut, endpoint, body) return <-responseCh @@ -315,9 +315,8 @@ func (s *externalDNSSecretTestSuite) Test_UpdateSuccess() { s.Require().NoError(s.setupTestResources(appName, envName, componentName, []radixv1.RadixDeployExternalDNS{{FQDN: fqdn}}, radixv1.DeploymentActive)) s.Require().NoError(s.setupSecretForExternalDNS(ns, fqdn, nil, nil)) s.tlsValidator.EXPECT().ValidateX509Certificate([]byte(cert), []byte(privateKey), fqdn).Return(true, nil).Times(1) - s.tlsValidator.EXPECT().ValidatePrivateKey([]byte(privateKey)).Return(true, nil).Times(1) - response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.SetExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) + response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.UpdateExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) s.Equal(200, response.Code) expectedSecretData := map[string][]byte{ corev1.TLSCertKey: []byte(cert), @@ -333,7 +332,7 @@ func (s *externalDNSSecretTestSuite) Test_RadixDeploymentNotActive() { privateKey, cert := "any private key", "any certificate" s.Require().NoError(s.setupTestResources(appName, envName, componentName, []radixv1.RadixDeployExternalDNS{{FQDN: fqdn}}, radixv1.DeploymentInactive)) - response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.SetExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) + response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.UpdateExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) s.Equal(404, response.Code) var status radixhttp.Error s.Require().NoError(controllertest.GetResponseBody(response, &status)) @@ -345,7 +344,7 @@ func (s *externalDNSSecretTestSuite) Test_NonExistingComponent() { privateKey, cert := "any private key", "any certificate" s.Require().NoError(s.setupTestResources(appName, envName, "othercomp", []radixv1.RadixDeployExternalDNS{{FQDN: fqdn}}, radixv1.DeploymentActive)) - response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.SetExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) + response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.UpdateExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) s.Equal(404, response.Code) var status radixhttp.Error s.Require().NoError(controllertest.GetResponseBody(response, &status)) @@ -357,7 +356,7 @@ func (s *externalDNSSecretTestSuite) Test_NonExistingExternalDNS() { privateKey, cert := "any private key", "any certificate" s.Require().NoError(s.setupTestResources(appName, envName, componentName, []radixv1.RadixDeployExternalDNS{{FQDN: "other.example.com"}}, radixv1.DeploymentActive)) - response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.SetExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) + response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.UpdateExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) s.Equal(404, response.Code) var status radixhttp.Error s.Require().NoError(controllertest.GetResponseBody(response, &status)) @@ -369,42 +368,26 @@ func (s *externalDNSSecretTestSuite) Test_ExternalDNSUsesAutomation() { privateKey, cert := "any private key", "any certificate" s.Require().NoError(s.setupTestResources(appName, envName, componentName, []radixv1.RadixDeployExternalDNS{{FQDN: fqdn, UseCertificateAutomation: true}}, radixv1.DeploymentActive)) - response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.SetExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) + response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.UpdateExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) s.Equal(400, response.Code) var status radixhttp.Error s.Require().NoError(controllertest.GetResponseBody(response, &status)) s.Equal(fmt.Sprintf("External DNS %q is configured to use certificate automation", fqdn), status.Message) } -func (s *externalDNSSecretTestSuite) Test_TLSCertificateValidationError() { +func (s *externalDNSSecretTestSuite) Test_CertificateValidationError() { appName, envName, componentName, fqdn := "app", "env", "comp", "my.example.com" privateKey, cert := "any private key", "any certificate" validationMsg1, validationMsg2 := "validation error 1", "validation error 2" s.Require().NoError(s.setupTestResources(appName, envName, componentName, []radixv1.RadixDeployExternalDNS{{FQDN: fqdn}}, radixv1.DeploymentActive)) s.tlsValidator.EXPECT().ValidateX509Certificate([]byte(cert), []byte(privateKey), fqdn).Return(false, []string{validationMsg1, validationMsg2}).Times(1) - s.tlsValidator.EXPECT().ValidatePrivateKey([]byte(privateKey)).Return(true, nil).Times(1) - response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.SetExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) + response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.UpdateExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) s.Equal(400, response.Code) var status radixhttp.Error s.Require().NoError(controllertest.GetResponseBody(response, &status)) s.Equal(fmt.Sprintf("%s, %s", validationMsg1, validationMsg2), status.Message) - s.ErrorContains(status.Err, "Certificate failed validation") -} - -func (s *externalDNSSecretTestSuite) Test_TLSPrivateKeyValidationError() { - appName, envName, componentName, fqdn := "app", "env", "comp", "my.example.com" - privateKey, cert := "any private key", "any certificate" - validationMsg1, validationMsg2 := "validation error 1", "validation error 2" - s.Require().NoError(s.setupTestResources(appName, envName, componentName, []radixv1.RadixDeployExternalDNS{{FQDN: fqdn}}, radixv1.DeploymentActive)) - s.tlsValidator.EXPECT().ValidatePrivateKey([]byte(privateKey)).Return(false, []string{validationMsg1, validationMsg2}).Times(1) - - response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.SetExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) - s.Equal(400, response.Code) - var status radixhttp.Error - s.Require().NoError(controllertest.GetResponseBody(response, &status)) - s.Equal(fmt.Sprintf("%s, %s", validationMsg1, validationMsg2), status.Message) - s.ErrorContains(status.Err, "Private key failed validation") + s.ErrorContains(status.Err, "TLS failed validation") } func (s *externalDNSSecretTestSuite) Test_NonExistingSecretFails() { @@ -412,9 +395,8 @@ func (s *externalDNSSecretTestSuite) Test_NonExistingSecretFails() { privateKey, cert := "any private key", "any certificate" s.Require().NoError(s.setupTestResources(appName, envName, componentName, []radixv1.RadixDeployExternalDNS{{FQDN: fqdn}}, radixv1.DeploymentActive)) s.tlsValidator.EXPECT().ValidateX509Certificate([]byte(cert), []byte(privateKey), fqdn).Return(true, nil).Times(1) - s.tlsValidator.EXPECT().ValidatePrivateKey([]byte(privateKey)).Return(true, nil).Times(1) - response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.SetExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) + response := s.executeRequest(appName, envName, componentName, fqdn, &secretModels.UpdateExternalDNSTLSRequest{PrivateKey: privateKey, Certificate: cert}) s.Equal(500, response.Code) var status radixhttp.Error s.Require().NoError(controllertest.GetResponseBody(response, &status)) diff --git a/api/secrets/secret_handler.go b/api/secrets/secret_handler.go index b21b762e..bd4835de 100644 --- a/api/secrets/secret_handler.go +++ b/api/secrets/secret_handler.go @@ -139,7 +139,7 @@ func (eh *SecretHandler) ChangeComponentSecret(ctx context.Context, appName, env return eh.setSecretKeyValue(ctx, ns, secretObjName, map[string][]byte{partName: []byte(newSecretValue)}) } -func (eh *SecretHandler) SetComponentExternalDNSSecretData(ctx context.Context, appName, envName, componentName, fqdn string, certificate, privateKey string) error { +func (eh *SecretHandler) UpdateComponentExternalDNSSecretData(ctx context.Context, appName, envName, componentName, fqdn string, certificate, privateKey string) error { rdList, err := kubequery.GetRadixDeploymentsForEnvironment(ctx, eh.userAccount.RadixClient, appName, envName) if err != nil { return radixhttp.UnexpectedError("Failed to get deployments", err) @@ -167,11 +167,8 @@ func (eh *SecretHandler) SetComponentExternalDNSSecretData(ctx context.Context, certificateBytes, privateKeyBytes := []byte(certificate), []byte(privateKey) tlsValidator := eh.getTLSValidatorOrDefault() - if valid, validationMsgs := tlsValidator.ValidatePrivateKey(privateKeyBytes); !valid { - return radixhttp.ValidationError("Private key", strings.Join(validationMsgs, ", ")) - } if valid, validationMsgs := tlsValidator.ValidateX509Certificate(certificateBytes, privateKeyBytes, fqdn); !valid { - return radixhttp.ValidationError("Certificate", strings.Join(validationMsgs, ", ")) + return radixhttp.ValidationError("TLS", strings.Join(validationMsgs, ", ")) } ns := operatorutils.GetEnvironmentNamespace(appName, envName) diff --git a/api/utils/tlsvalidation/interface.go b/api/utils/tlsvalidation/interface.go index fe611ccb..64c95968 100644 --- a/api/utils/tlsvalidation/interface.go +++ b/api/utils/tlsvalidation/interface.go @@ -2,11 +2,6 @@ package tlsvalidation // Validator defines methods to validate certificate and private key for TLS type Validator interface { - // ValidatePrivateKey validates the private key - // keyBytes must be in PEM format - // Returns false is keyBytes is invalid, along with a list of validation error messages - ValidatePrivateKey(keyBytes []byte) (bool, []string) - // ValidateX509Certificate validates the certificate, dnsName and private key // certBytes and keyBytes must be in PEM format // Returns false if validation fails, along with a list of validation error messages diff --git a/api/utils/tlsvalidation/mock/tls_secret_validator_mock.go b/api/utils/tlsvalidation/mock/tls_secret_validator_mock.go index 9a482bd8..ddc472cb 100644 --- a/api/utils/tlsvalidation/mock/tls_secret_validator_mock.go +++ b/api/utils/tlsvalidation/mock/tls_secret_validator_mock.go @@ -33,21 +33,6 @@ func (m *MockValidator) EXPECT() *MockValidatorMockRecorder { return m.recorder } -// ValidatePrivateKey mocks base method. -func (m *MockValidator) ValidatePrivateKey(keyBytes []byte) (bool, []string) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidatePrivateKey", keyBytes) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].([]string) - return ret0, ret1 -} - -// ValidatePrivateKey indicates an expected call of ValidatePrivateKey. -func (mr *MockValidatorMockRecorder) ValidatePrivateKey(keyBytes interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatePrivateKey", reflect.TypeOf((*MockValidator)(nil).ValidatePrivateKey), keyBytes) -} - // ValidateX509Certificate mocks base method. func (m *MockValidator) ValidateX509Certificate(certBytes, keyBytes []byte, dnsName string) (bool, []string) { m.ctrl.T.Helper() diff --git a/swaggerui/html/swagger.json b/swaggerui/html/swagger.json index 402a3688..631855e7 100644 --- a/swaggerui/html/swagger.json +++ b/swaggerui/html/swagger.json @@ -1903,7 +1903,7 @@ "component" ], "summary": "Set external DNS TLS private key certificate for a component", - "operationId": "setComponentExternalDNSTLS", + "operationId": "updateComponentExternalDnsTls", "parameters": [ { "type": "string", @@ -1939,7 +1939,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/SetExternalDNSTLSRequest" + "$ref": "#/definitions/UpdateExternalDnsTlsRequest" } }, { @@ -7380,27 +7380,6 @@ }, "x-go-package": "github.com/equinor/radix-api/api/secrets/models" }, - "SetExternalDNSTLSRequest": { - "description": "SetExternalDNSTLSRequest describes request body for setting private key and certificate for external DNS TLS", - "type": "object", - "required": [ - "privateKey", - "certificate" - ], - "properties": { - "certificate": { - "description": "X509 certificate in PEM format", - "type": "string", - "x-go-name": "Certificate" - }, - "privateKey": { - "description": "Private key in PEM format", - "type": "string", - "x-go-name": "PrivateKey" - } - }, - "x-go-package": "github.com/equinor/radix-api/api/secrets/models" - }, "SlackConfig": { "description": "SlackConfig configuration options for Slack", "type": "object", @@ -7481,30 +7460,9 @@ "type": "object", "required": [ "useAutomation", - "privateKeyStatus", - "certificateStatus" + "status" ], "properties": { - "certificateStatus": { - "description": "Status of the certificate\nPending CertificatePending Certificate is not set\nConsistent CertificateConsistent Certificate is valid\nInvalid CertificateInvalid Certificate is invalid", - "type": "string", - "enum": [ - "Pending", - "Consistent", - "Invalid" - ], - "x-go-enum-desc": "Pending CertificatePending Certificate is not set\nConsistent CertificateConsistent Certificate is valid\nInvalid CertificateInvalid Certificate is invalid", - "x-go-name": "CertificateStatus", - "example": "Consistent" - }, - "certificateStatusMessages": { - "description": "CertificateStatusMessages contains a list of messages related to CertificateStatus", - "type": "array", - "items": { - "type": "string" - }, - "x-go-name": "CertificateStatusMessages" - }, "certificates": { "description": "Certificates holds the X509 certificate chain\nThe first certificate in the list should be the host certificate and the rest should be intermediate certificates", "type": "array", @@ -7513,25 +7471,25 @@ }, "x-go-name": "Certificates" }, - "privateKeyStatus": { - "description": "Status of the private key\nPending PrivateKeyPending Private key is not set\nConsistent PrivateKeyConsistent Private key is valid\nInvalid PrivateKeyInvalid Private key is invalid", + "status": { + "description": "Status of TLS certificate and private key\nPending TLSStatusPending TLS certificate and private key not set\nConsistent TLSStatusConsistent TLS certificate and private key is valid\nInvalid TLSStatusInvalid TLS certificate and private key is invalid", "type": "string", "enum": [ "Pending", "Consistent", "Invalid" ], - "x-go-enum-desc": "Pending PrivateKeyPending Private key is not set\nConsistent PrivateKeyConsistent Private key is valid\nInvalid PrivateKeyInvalid Private key is invalid", - "x-go-name": "PrivateKeyStatus", + "x-go-enum-desc": "Pending TLSStatusPending TLS certificate and private key not set\nConsistent TLSStatusConsistent TLS certificate and private key is valid\nInvalid TLSStatusInvalid TLS certificate and private key is invalid", + "x-go-name": "Status", "example": "Consistent" }, - "privateKeyStatusMessages": { - "description": "PrivateKeyStatusMessages contains a list of messages related to PrivateKeyStatus", + "statusMessages": { + "description": "StatusMessages contains a list of messages related to Status", "type": "array", "items": { "type": "string" }, - "x-go-name": "PrivateKeyStatusMessages" + "x-go-name": "StatusMessages" }, "useAutomation": { "description": "UseAutomation describes if TLS certificate is automatically issued using automation (ACME)", @@ -7562,6 +7520,28 @@ }, "x-go-package": "github.com/equinor/radix-api/api/alerting/models" }, + "UpdateExternalDnsTlsRequest": { + "description": "UpdateExternalDNSTLSRequest describes request body for setting private key and certificate for external DNS TLS", + "type": "object", + "required": [ + "privateKey", + "certificate" + ], + "properties": { + "certificate": { + "description": "X509 certificate in PEM format", + "type": "string", + "x-go-name": "Certificate" + }, + "privateKey": { + "description": "Private key in PEM format", + "type": "string", + "x-go-name": "PrivateKey" + } + }, + "x-go-name": "UpdateExternalDNSTLSRequest", + "x-go-package": "github.com/equinor/radix-api/api/secrets/models" + }, "UpdateReceiverConfigSecrets": { "description": "UpdateReceiverConfigSecrets defines secrets to be updated", "type": "object",