Skip to content

Commit

Permalink
combine certificate and private key status
Browse files Browse the repository at this point in the history
  • Loading branch information
nilsgstrabo committed Jan 9, 2024
1 parent 938008b commit 52c38a5
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 291 deletions.
47 changes: 12 additions & 35 deletions api/deployments/models/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
117 changes: 6 additions & 111 deletions api/environments/environment_controller_externaldns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand All @@ -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

Expand All @@ -198,75 +194,14 @@ 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)
}

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

Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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,
Expand Down
42 changes: 12 additions & 30 deletions api/models/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,55 +81,37 @@ 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...)
}
}

externalDNSList = append(externalDNSList,
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,
},
},
)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
14 changes: 7 additions & 7 deletions api/secrets/secret_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -266,15 +266,15 @@ 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
}

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
}
Expand Down
Loading

0 comments on commit 52c38a5

Please sign in to comment.