From bca48b6978e1af2f0774178bbd3f2e8daee81b8c Mon Sep 17 00:00:00 2001 From: Martin Schuppert Date: Wed, 13 Dec 2023 17:58:42 +0100 Subject: [PATCH] [tlse] internal TLS support for Nova Creates TLS certs via cert-manager for NovaAPI, NovaMetadata and NovaNoVNCProxy. Depends-On: https://github.com/openstack-k8s-operators/lib-common/pull/384 Jira: TODO --- pkg/openstack/nova.go | 228 +++++++++++++++++++++++++++++++----------- 1 file changed, 167 insertions(+), 61 deletions(-) diff --git a/pkg/openstack/nova.go b/pkg/openstack/nova.go index 79704135e..02bd956d8 100644 --- a/pkg/openstack/nova.go +++ b/pkg/openstack/nova.go @@ -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" @@ -56,9 +58,16 @@ 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{} } @@ -66,31 +75,56 @@ func ReconcileNova(ctx context.Context, instance *corev1beta1.OpenStackControlPl 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 @@ -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 @@ -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 } } @@ -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 }