Skip to content

Commit

Permalink
[tlse] internal TLS support for Nova
Browse files Browse the repository at this point in the history
Creates TLS certs via cert-manager for NovaAPI, NovaMetadata and
NovaNoVNCProxy.

Depends-On: openstack-k8s-operators/lib-common#384

Jira: TODO
  • Loading branch information
stuggi committed Jan 10, 2024
1 parent 606eb89 commit bca48b6
Showing 1 changed file with 167 additions and 61 deletions.
228 changes: 167 additions & 61 deletions pkg/openstack/nova.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import (
"context"
"fmt"

"github.com/openstack-k8s-operators/lib-common/modules/certmanager"
"github.com/openstack-k8s-operators/lib-common/modules/common"
"github.com/openstack-k8s-operators/lib-common/modules/common/condition"
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
"github.com/openstack-k8s-operators/lib-common/modules/common/service"
"github.com/openstack-k8s-operators/lib-common/modules/common/tls"

"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
Expand Down Expand Up @@ -56,41 +58,73 @@ func ReconcileNova(ctx context.Context, instance *corev1beta1.OpenStackControlPl
return ctrl.Result{}, nil
}

// add selector to service overrides
// When component services got created check if there is the need to create routes and certificates
if err := helper.GetClient().Get(ctx, types.NamespacedName{Name: "nova", Namespace: instance.Namespace}, nova); err != nil {
if !k8s_errors.IsNotFound(err) {
return ctrl.Result{}, err
}
}

// Add selectors and CA bundle to service overrides for api, metadata and novncproxy
// NovaAPI
for _, endpointType := range []service.Endpoint{service.EndpointPublic, service.EndpointInternal} {
// NovaAPI
if instance.Spec.Nova.Template.APIServiceTemplate.Override.Service == nil {
instance.Spec.Nova.Template.APIServiceTemplate.Override.Service = map[service.Endpoint]service.RoutedOverrideSpec{}
}
instance.Spec.Nova.Template.APIServiceTemplate.Override.Service[endpointType] =
AddServiceComponentLabel(
instance.Spec.Nova.Template.APIServiceTemplate.Override.Service[endpointType],
nova.Name+"-api")
}
// preserve any previously set TLS certs,set CA cert
if instance.Spec.TLS.Enabled(service.EndpointInternal) {
instance.Spec.Nova.Template.APIServiceTemplate.TLS = nova.Spec.APIServiceTemplate.TLS
}
instance.Spec.Nova.Template.APIServiceTemplate.TLS.CaBundleSecretName = instance.Status.TLS.CaBundleSecretName

// cell NoVNCProxy service override
for cellName, cellTemplate := range instance.Spec.Nova.Template.CellTemplates {
// skip adding override for all the cells where novncproxy is disabled
if cellTemplate.NoVNCProxyServiceTemplate.Enabled == ptr.To(false) {
continue
}
// NovaMetadata
if metadataEnabled(instance.Spec.Nova.Template.MetadataServiceTemplate) {
if instance.Spec.Nova.Template.MetadataServiceTemplate.Override.Service == nil {
instance.Spec.Nova.Template.MetadataServiceTemplate.Override.Service = &service.OverrideSpec{}
}
instance.Spec.Nova.Template.MetadataServiceTemplate.Override.Service.AddLabel(centralMetadataLabelMap(nova.Name))

// preserve any previously set TLS certs,set CA cert
if instance.Spec.TLS.Enabled(service.EndpointInternal) {
instance.Spec.Nova.Template.MetadataServiceTemplate.TLS = nova.Spec.MetadataServiceTemplate.TLS
}
instance.Spec.Nova.Template.MetadataServiceTemplate.TLS.CaBundleSecretName = instance.Status.TLS.CaBundleSecretName
}
// Cells
for cellName, cellTemplate := range instance.Spec.Nova.Template.CellTemplates {
// add override where novncproxy enabled is not specified or explicitely set to true
if noVNCProxyEnabled(cellTemplate.NoVNCProxyServiceTemplate) {
if cellTemplate.NoVNCProxyServiceTemplate.Override.Service == nil {
cellTemplate.NoVNCProxyServiceTemplate.Override.Service = &service.RoutedOverrideSpec{}
}
cellTemplate.NoVNCProxyServiceTemplate.Override.Service.AddLabel(getNoVNCProxyLabelMap(nova.Name, cellName))

*cellTemplate.NoVNCProxyServiceTemplate.Override.Service =
AddServiceComponentLabel(
*cellTemplate.NoVNCProxyServiceTemplate.Override.Service,
getNoVNCProxyServiceLabel(nova.Name, cellName))

instance.Spec.Nova.Template.CellTemplates[cellName] = cellTemplate
// preserve any previously set TLS certs,set CA cert
if instance.Spec.TLS.Enabled(service.EndpointInternal) {
cellTemplate.NoVNCProxyServiceTemplate.TLS = nova.Spec.CellTemplates[cellName].NoVNCProxyServiceTemplate.TLS
}
cellTemplate.NoVNCProxyServiceTemplate.TLS.CaBundleSecretName = instance.Status.TLS.CaBundleSecretName
}
}

// When component services got created check if there is the need to create a route
if err := helper.GetClient().Get(ctx, types.NamespacedName{Name: "nova", Namespace: instance.Namespace}, nova); err != nil {
if !k8s_errors.IsNotFound(err) {
return ctrl.Result{}, err
// add override where metadata enabled is set to true
if metadataEnabled(cellTemplate.MetadataServiceTemplate) {
if cellTemplate.MetadataServiceTemplate.Override.Service == nil {
cellTemplate.MetadataServiceTemplate.Override.Service = &service.OverrideSpec{}
}
cellTemplate.MetadataServiceTemplate.Override.Service.AddLabel(cellMetadataLabelMap(nova.Name, cellName))

// preserve any previously set TLS certs,set CA cert
if instance.Spec.TLS.Enabled(service.EndpointInternal) {
cellTemplate.MetadataServiceTemplate.TLS = nova.Spec.CellTemplates[cellName].MetadataServiceTemplate.TLS
}
cellTemplate.MetadataServiceTemplate.TLS.CaBundleSecretName = instance.Status.TLS.CaBundleSecretName
}
instance.Spec.Nova.Template.CellTemplates[cellName] = cellTemplate
}

// Nova API
Expand All @@ -116,7 +150,7 @@ func ReconcileNova(ctx context.Context, instance *corev1beta1.OpenStackControlPl
instance.Spec.Nova.Template.APIServiceTemplate.Override.Service,
instance.Spec.Nova.APIOverride,
corev1beta1.OpenStackControlPlaneExposeNovaReadyCondition,
true, // TODO: (mschuppert) disable TLS for now until implemented
false, // TODO (mschuppert) could be removed when all integrated service support TLS
)
if err != nil {
return ctrlResult, err
Expand All @@ -125,58 +159,102 @@ func ReconcileNova(ctx context.Context, instance *corev1beta1.OpenStackControlPl
}

instance.Spec.Nova.Template.APIServiceTemplate.Override.Service = apiServiceEndpointDetails.GetEndpointServiceOverrides()

// set NovaAPI TLS cert secret
instance.Spec.Nova.Template.APIServiceTemplate.TLS.API.Public.SecretName =
apiServiceEndpointDetails.GetEndptCertSecret(service.EndpointPublic)
instance.Spec.Nova.Template.APIServiceTemplate.TLS.API.Internal.SecretName =
apiServiceEndpointDetails.GetEndptCertSecret(service.EndpointInternal)
}

if nova.Status.Conditions.IsTrue(novav1.NovaAllCellsReadyCondition) {
// cell NoVNCProxy
for cellName, cellTemplate := range instance.Spec.Nova.Template.CellTemplates {
// skip checking for/creating route if service is not enabled
if cellTemplate.NoVNCProxyServiceTemplate.Enabled == ptr.To(false) {
continue
}

if cellTemplate.NoVNCProxyServiceTemplate.Override.Service == nil {
cellTemplate.NoVNCProxyServiceTemplate.Override.Service = &service.RoutedOverrideSpec{}
}

svcs, err := service.GetServicesListWithLabel(
ctx,
helper,
instance.Namespace,
map[string]string{
common.AppSelector: getNoVNCProxyServiceLabel(nova.Name, cellName),
},
)
if err != nil {
return ctrl.Result{}, err
}

var ctrlResult reconcile.Result
var cellServiceEndpointDetails = Endpoints{}
cellServiceEndpointDetails, ctrlResult, err = EnsureEndpointConfig(
// create certificate for central Metadata agent if internal TLS and Metadata are enabled
if instance.Spec.TLS.Enabled(service.EndpointInternal) &&
metadataEnabled(instance.Spec.Nova.Template.MetadataServiceTemplate) {
certScrt, ctrlResult, err := certmanager.EnsureCertForServiceWithSelector(
ctx,
instance,
helper,
nova,
svcs,
map[service.Endpoint]service.RoutedOverrideSpec{
service.EndpointPublic: *cellTemplate.NoVNCProxyServiceTemplate.Override.Service,
},
instance.Spec.Nova.CellOverride[cellName].NoVNCProxy,
corev1beta1.OpenStackControlPlaneExposeNovaReadyCondition,
true, // TODO: (mschuppert) disable TLS for now until implemented
)
nova.Namespace,
instance.Spec.Nova.Template.MetadataServiceTemplate.Override.Service.Labels,
tls.DefaultCAPrefix+string(service.EndpointInternal))
if err != nil {
return ctrlResult, err
} else if (ctrlResult != ctrl.Result{}) {
return ctrlResult, nil
}

routedOverrideSpec := cellServiceEndpointDetails.GetEndpointServiceOverrides()
cellTemplate.NoVNCProxyServiceTemplate.Override.Service = ptr.To(routedOverrideSpec[service.EndpointPublic])
// update NovaMetadata cert secret
instance.Spec.Nova.Template.MetadataServiceTemplate.TLS.SecretName = ptr.To(certScrt)
}

instance.Spec.Nova.Template.CellTemplates[cellName] = cellTemplate
// cell Metadata and NoVNCProxy
for cellName, cellTemplate := range instance.Spec.Nova.Template.CellTemplates {
// create certificate for Metadata agend if internal TLS and Metadata per cell is enabled
if instance.Spec.TLS.Enabled(service.EndpointInternal) &&
metadataEnabled(cellTemplate.MetadataServiceTemplate) {

certScrt, ctrlResult, err := certmanager.EnsureCertForServiceWithSelector(
ctx,
helper,
nova.Namespace,
cellTemplate.MetadataServiceTemplate.Override.Service.Labels,
tls.DefaultCAPrefix+string(service.EndpointInternal))
if err != nil {
return ctrlResult, err
} else if (ctrlResult != ctrl.Result{}) {
return ctrlResult, nil
}

// update NovaMetadata cert secret
cellTemplate.MetadataServiceTemplate.TLS.SecretName = ptr.To(certScrt)
}

// NoVNCProxy check for/creating route if service is enabled
if noVNCProxyEnabled(cellTemplate.NoVNCProxyServiceTemplate) {
if cellTemplate.NoVNCProxyServiceTemplate.Override.Service == nil {
cellTemplate.NoVNCProxyServiceTemplate.Override.Service = &service.RoutedOverrideSpec{}
}

svcs, err := service.GetServicesListWithLabel(
ctx,
helper,
instance.Namespace,
getNoVNCProxyLabelMap(nova.Name, cellName),
)
if err != nil {
return ctrl.Result{}, err
}

var ctrlResult reconcile.Result
var cellServiceEndpointDetails = Endpoints{}
cellServiceEndpointDetails, ctrlResult, err = EnsureEndpointConfig(
ctx,
instance,
helper,
nova,
svcs,
map[service.Endpoint]service.RoutedOverrideSpec{
service.EndpointPublic: *cellTemplate.NoVNCProxyServiceTemplate.Override.Service,
},
instance.Spec.Nova.CellOverride[cellName].NoVNCProxy,
corev1beta1.OpenStackControlPlaneExposeNovaReadyCondition,
false, // TODO (mschuppert) could be removed when all integrated service support TLS
)
if err != nil {
return ctrlResult, err
} else if (ctrlResult != ctrl.Result{}) {
return ctrlResult, nil
}

routedOverrideSpec := cellServiceEndpointDetails.GetEndpointServiceOverrides()
cellTemplate.NoVNCProxyServiceTemplate.Override.Service = ptr.To(routedOverrideSpec[service.EndpointPublic])

// update NoVNCProxy cert secret
cellTemplate.NoVNCProxyServiceTemplate.TLS.SecretName =
cellServiceEndpointDetails.GetEndptCertSecret(service.EndpointPublic)
}

instance.Spec.Nova.Template.CellTemplates[cellName] = cellTemplate
}
}

Expand Down Expand Up @@ -233,6 +311,34 @@ func ReconcileNova(ctx context.Context, instance *corev1beta1.OpenStackControlPl
return ctrl.Result{}, nil
}

func getNoVNCProxyServiceLabel(name string, cellName string) string {
return name + "-novncproxy-" + cellName
func getNoVNCProxyLabelMap(name string, cellName string) map[string]string {
return map[string]string{
common.AppSelector: name + "-novncproxy",
"cell": cellName,
}
}

func getMetadataLabelMap(name string, instType string) map[string]string {
return map[string]string{
common.AppSelector: name + "-metadata",
"type": instType,
}
}

func centralMetadataLabelMap(name string) map[string]string {
return getMetadataLabelMap(name, "central")
}

func cellMetadataLabelMap(name string, cell string) map[string]string {
lm := getMetadataLabelMap(name, "cell")
lm["cell"] = cell
return lm
}

func metadataEnabled(metadata novav1.NovaMetadataTemplate) bool {
return metadata.Enabled != nil && *metadata.Enabled == true
}

func noVNCProxyEnabled(vncproxy novav1.NovaNoVNCProxyTemplate) bool {
return vncproxy.Enabled != nil && *vncproxy.Enabled == true
}

0 comments on commit bca48b6

Please sign in to comment.