Skip to content

Commit

Permalink
Add the TLS struct support for service operators
Browse files Browse the repository at this point in the history
Create specs Service for server-specific TLS secret, CA for CA specific settings for clients and services, and the TLS struct as a generic type encapsulating both.
Only single secrets for both Service and CA specs are created.

Signed-off-by: Veronika Fisarova <[email protected]>
  • Loading branch information
Deydra71 committed Oct 4, 2023
1 parent 1236966 commit b629be5
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 5 deletions.
98 changes: 93 additions & 5 deletions modules/common/tls/tls.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2022 Red Hat
Copyright 2023 Red Hat
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -18,17 +18,27 @@ limitations under the License.

package tls

import (
"context"
"fmt"

"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
"github.com/openstack-k8s-operators/lib-common/modules/common/secret"
corev1 "k8s.io/api/core/v1"
)

// Service contains server-specific TLS secret
type Service struct {
// Server-specific settings
SecretName string `json:"secretName,omitempty"`
DisableNonTLSListeners bool `json:"disableNonTLSListeners,omitempty"`
// +kubebuilder:validation:Optional
SecretName string `json:"secretName,omitempty"`
// +kubebuilder:validation:Optional
DisableNonTLSListeners bool `json:"disableNonTLSListeners,omitempty"`
}

// Ca contains CA-specific settings, which could be used both by services (to define their own CA certificates)
// and by clients (to verify the server's certificate)
type Ca struct {
// CA-specific settings
// +kubebuilder:validation:Optional
CaSecretName string `json:"caSecretName,omitempty"`
}

Expand All @@ -37,3 +47,81 @@ type TLS struct {
Service *Service `json:"service"`
Ca *Ca `json:"ca"`
}

// NewTLS - initialize and return a TLS struct
func NewTLS(ctx context.Context, namespace string, helperInstance *helper.Helper, service *Service, ca *Ca) (*TLS, error) {

// Ensure service SecretName exists or return an error
if service != nil && service.SecretName != "" {
secretData, _, err := secret.GetSecret(ctx, helperInstance, service.SecretName, namespace)
if err != nil {
return nil, fmt.Errorf("error ensuring secret %s exists: %w", service.SecretName, err)
}

_, keyOk := secretData.Data["tls.key"]
_, certOk := secretData.Data["tls.crt"]
if !keyOk || !certOk {
return nil, fmt.Errorf("secret %s does not contain both tls.key and tls.crt", service.SecretName)
}
}

return &TLS{
Service: service,
Ca: ca,
}, nil
}

// CreateVolumeMounts - add volume mount for TLS certificate and CA certificates, this counts on openstack-operator providing CA certs with unique names
func (t *TLS) CreateVolumeMounts() []corev1.VolumeMount {
var volumeMounts []corev1.VolumeMount

if t.Service != nil && t.Service.SecretName != "" {
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: "tls-certs",
MountPath: "/var/lib/config-data/tls-certificates",
ReadOnly: true,
})
}

if t.Ca != nil && t.Ca.CaSecretName != "" {
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: "ca-certs",
MountPath: "/var/lib/config-data/ca-certificates",
ReadOnly: true,
})
}

return volumeMounts
}

// CreateVolumes - add volume for TLS certificate and CA certificates
func (t *TLS) CreateVolumes() []corev1.Volume {
var volumes []corev1.Volume
mode := int32(0400)

if t.Service != nil && t.Service.SecretName != "" {
volumes = append(volumes, corev1.Volume{
Name: "tls-certs",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: t.Service.SecretName,
DefaultMode: &mode,
},
},
})
}

if t.Ca != nil && t.Ca.CaSecretName != "" {
volumes = append(volumes, corev1.Volume{
Name: "ca-certs",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: t.Ca.CaSecretName,
DefaultMode: &mode,
},
},
})
}

return volumes
}
109 changes: 109 additions & 0 deletions modules/common/tls/tls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
Copyright 2023 Red Hat
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tls

import (
"testing"
)

func TestCreateVolumeMounts(t *testing.T) {
tests := []struct {
name string
service *Service
ca *Ca
wantMountsLen int
}{
{
name: "No Secrets",
service: &Service{},
ca: &Ca{},
wantMountsLen: 0,
},
{
name: "Only TLS Secret",
service: &Service{SecretName: "test-tls-secret"},
ca: &Ca{},
wantMountsLen: 1,
},
{
name: "Only CA Secret",
service: &Service{},
ca: &Ca{CaSecretName: "test-ca1"},
wantMountsLen: 1,
},
{
name: "TLS and CA Secrets",
service: &Service{SecretName: "test-tls-secret"},
ca: &Ca{CaSecretName: "test-ca1"},
wantMountsLen: 2,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tlsInstance := &TLS{Service: tt.service, Ca: tt.ca}
mounts := tlsInstance.CreateVolumeMounts()
if len(mounts) != tt.wantMountsLen {
t.Errorf("CreateVolumeMounts() got = %v mounts, want %v mounts", len(mounts), tt.wantMountsLen)
}
})
}
}

func TestCreateVolumes(t *testing.T) {
tests := []struct {
name string
service *Service
ca *Ca
wantVolLen int
}{
{
name: "No Secrets",
service: &Service{},
ca: &Ca{},
wantVolLen: 0,
},
{
name: "Only TLS Secret",
service: &Service{SecretName: "test-tls-secret"},
ca: &Ca{},
wantVolLen: 1,
},
{
name: "Only CA Secret",
service: &Service{},
ca: &Ca{CaSecretName: "test-ca1"},
wantVolLen: 1,
},
{
name: "TLS and CA Secrets",
service: &Service{SecretName: "test-tls-secret"},
ca: &Ca{CaSecretName: "test-ca1"},
wantVolLen: 2,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tlsInstance := &TLS{Service: tt.service, Ca: tt.ca}
volumes := tlsInstance.CreateVolumes()
if len(volumes) != tt.wantVolLen {
t.Errorf("CreateVolumes() got = %v volumes, want %v volumes", len(volumes), tt.wantVolLen)
}
})
}
}

0 comments on commit b629be5

Please sign in to comment.