From 00544efd2731aa0721dec9648776d11926cf2fac Mon Sep 17 00:00:00 2001 From: Veronika Fisarova Date: Tue, 19 Mar 2024 08:28:58 +0100 Subject: [PATCH] [tlse] tls support for octaviaAPI, amphora pod configuration, add TLS databse connection Public/Internal service cert secrets and the CA bundle secret can be passed to configure httpd virtual hosts for tls termination. The certs get direct mounted to the appropriate place in etc/pki/tls/certs/%s.crt|key and a CA bundle to /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem . Job deployments for bootstrap/cron get the CA bundle added if configured. Depends-On: openstack-k8s-operators/lib-common#428 Signed-off-by: Veronika Fisarova --- ...enstack.org_octaviaamphoracontrollers.yaml | 8 + .../octavia.openstack.org_octaviaapis.yaml | 30 ++ api/bases/octavia.openstack.org_octavias.yaml | 54 ++++ api/v1beta1/amphoracontroller_types.go | 4 + api/v1beta1/octaviaapi_types.go | 6 + api/v1beta1/zz_generated.deepcopy.go | 2 + ...enstack.org_octaviaamphoracontrollers.yaml | 8 + .../octavia.openstack.org_octaviaapis.yaml | 30 ++ .../bases/octavia.openstack.org_octavias.yaml | 54 ++++ .../samples/octavia_v1beta1_octavia_tls.yaml | 76 +++++ controllers/amphoracontroller_controller.go | 119 ++++++- controllers/octavia_controller.go | 35 +- controllers/octaviaapi_controller.go | 200 +++++++++++- go.mod | 6 +- pkg/amphoracontrollers/daemonset.go | 6 + pkg/octavia/dbsync.go | 6 + pkg/octavia/volumes.go | 6 + pkg/octaviaapi/deployment.go | 44 ++- templates/octaviaapi/config/httpd.conf | 53 +-- .../octaviaapi/config/octavia-api-config.json | 24 +- templates/octaviaapi/config/ssl.conf | 21 ++ tests/kuttl/tests/octavia_tls/01-assert.yaml | 14 + .../kuttl/tests/octavia_tls/01-tls-certs.yaml | 30 ++ tests/kuttl/tests/octavia_tls/02-assert.yaml | 301 ++++++++++++++++++ tests/kuttl/tests/octavia_tls/02-deploy.yaml | 76 +++++ 25 files changed, 1170 insertions(+), 43 deletions(-) create mode 100644 config/samples/octavia_v1beta1_octavia_tls.yaml create mode 100644 templates/octaviaapi/config/ssl.conf create mode 100644 tests/kuttl/tests/octavia_tls/01-assert.yaml create mode 100644 tests/kuttl/tests/octavia_tls/01-tls-certs.yaml create mode 100644 tests/kuttl/tests/octavia_tls/02-assert.yaml create mode 100644 tests/kuttl/tests/octavia_tls/02-deploy.yaml diff --git a/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml b/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml index 01ec82ea..37d9547f 100644 --- a/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml +++ b/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml @@ -220,6 +220,14 @@ spec: description: TenantName - the name of the OpenStack tenant that controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string diff --git a/api/bases/octavia.openstack.org_octaviaapis.yaml b/api/bases/octavia.openstack.org_octaviaapis.yaml index f96455db..2743a14b 100644 --- a/api/bases/octavia.openstack.org_octaviaapis.yaml +++ b/api/bases/octavia.openstack.org_octaviaapis.yaml @@ -362,6 +362,36 @@ spec: default: octavia description: ServiceUser - service user name type: string + tls: + description: TLS - Parameters related to the TLS + properties: + api: + description: API tls type which encapsulates for API services + properties: + internal: + description: Internal GenericService - holds the secret for + the internal endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + public: + description: Public GenericService - holds the secret for + the public endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + type: object + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string diff --git a/api/bases/octavia.openstack.org_octavias.yaml b/api/bases/octavia.openstack.org_octavias.yaml index 5254e7c8..890fbf9f 100644 --- a/api/bases/octavia.openstack.org_octavias.yaml +++ b/api/bases/octavia.openstack.org_octavias.yaml @@ -448,6 +448,36 @@ spec: default: octavia description: ServiceUser - service user name type: string + tls: + description: TLS - Parameters related to the TLS + properties: + api: + description: API tls type which encapsulates for API services + properties: + internal: + description: Internal GenericService - holds the secret + for the internal endpoint + properties: + secretName: + description: SecretName - holding the cert, key for + the service + type: string + type: object + public: + description: Public GenericService - holds the secret + for the public endpoint + properties: + secretName: + description: SecretName - holding the cert, key for + the service + type: string + type: object + type: object + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string @@ -634,6 +664,14 @@ spec: description: TenantName - the name of the OpenStack tenant that controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string @@ -820,6 +858,14 @@ spec: description: TenantName - the name of the OpenStack tenant that controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string @@ -1006,6 +1052,14 @@ spec: description: TenantName - the name of the OpenStack tenant that controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string diff --git a/api/v1beta1/amphoracontroller_types.go b/api/v1beta1/amphoracontroller_types.go index da8bb010..ec823bc8 100644 --- a/api/v1beta1/amphoracontroller_types.go +++ b/api/v1beta1/amphoracontroller_types.go @@ -18,6 +18,7 @@ package v1beta1 import ( "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -135,6 +136,9 @@ type OctaviaAmphoraControllerSpecCore struct { // +kubebuilder:default={} // List of Redis Host IP addresses RedisHostIPs []string `json:"redisHostIPs,omitempty"` + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.Ca `json:"tls,omitempty"` } // OctaviaAmphoraControllerStatus defines the observed state of the Octavia Amphora Controller diff --git a/api/v1beta1/octaviaapi_types.go b/api/v1beta1/octaviaapi_types.go index 7b204cf0..c0ef3879 100644 --- a/api/v1beta1/octaviaapi_types.go +++ b/api/v1beta1/octaviaapi_types.go @@ -19,6 +19,7 @@ package v1beta1 import ( "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -135,6 +136,11 @@ type OctaviaAPISpecCore struct { // +kubebuilder:validation:Optional // NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network NetworkAttachments []string `json:"networkAttachments,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.API `json:"tls,omitempty"` } // APIOverrideSpec to override the generated manifest of several child resources. diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index aca6786a..dee6925c 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -181,6 +181,7 @@ func (in *OctaviaAPISpecCore) DeepCopyInto(out *OctaviaAPISpecCore) { *out = make([]string, len(*in)) copy(*out, *in) } + in.TLS.DeepCopyInto(&out.TLS) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OctaviaAPISpecCore. @@ -346,6 +347,7 @@ func (in *OctaviaAmphoraControllerSpecCore) DeepCopyInto(out *OctaviaAmphoraCont *out = make([]string, len(*in)) copy(*out, *in) } + out.TLS = in.TLS } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OctaviaAmphoraControllerSpecCore. diff --git a/config/crd/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml b/config/crd/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml index 01ec82ea..37d9547f 100644 --- a/config/crd/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml +++ b/config/crd/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml @@ -220,6 +220,14 @@ spec: description: TenantName - the name of the OpenStack tenant that controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string diff --git a/config/crd/bases/octavia.openstack.org_octaviaapis.yaml b/config/crd/bases/octavia.openstack.org_octaviaapis.yaml index f96455db..2743a14b 100644 --- a/config/crd/bases/octavia.openstack.org_octaviaapis.yaml +++ b/config/crd/bases/octavia.openstack.org_octaviaapis.yaml @@ -362,6 +362,36 @@ spec: default: octavia description: ServiceUser - service user name type: string + tls: + description: TLS - Parameters related to the TLS + properties: + api: + description: API tls type which encapsulates for API services + properties: + internal: + description: Internal GenericService - holds the secret for + the internal endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + public: + description: Public GenericService - holds the secret for + the public endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + type: object + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string diff --git a/config/crd/bases/octavia.openstack.org_octavias.yaml b/config/crd/bases/octavia.openstack.org_octavias.yaml index 5254e7c8..890fbf9f 100644 --- a/config/crd/bases/octavia.openstack.org_octavias.yaml +++ b/config/crd/bases/octavia.openstack.org_octavias.yaml @@ -448,6 +448,36 @@ spec: default: octavia description: ServiceUser - service user name type: string + tls: + description: TLS - Parameters related to the TLS + properties: + api: + description: API tls type which encapsulates for API services + properties: + internal: + description: Internal GenericService - holds the secret + for the internal endpoint + properties: + secretName: + description: SecretName - holding the cert, key for + the service + type: string + type: object + public: + description: Public GenericService - holds the secret + for the public endpoint + properties: + secretName: + description: SecretName - holding the cert, key for + the service + type: string + type: object + type: object + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string @@ -634,6 +664,14 @@ spec: description: TenantName - the name of the OpenStack tenant that controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string @@ -820,6 +858,14 @@ spec: description: TenantName - the name of the OpenStack tenant that controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string @@ -1006,6 +1052,14 @@ spec: description: TenantName - the name of the OpenStack tenant that controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + type: object transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string diff --git a/config/samples/octavia_v1beta1_octavia_tls.yaml b/config/samples/octavia_v1beta1_octavia_tls.yaml new file mode 100644 index 00000000..786b83f5 --- /dev/null +++ b/config/samples/octavia_v1beta1_octavia_tls.yaml @@ -0,0 +1,76 @@ +apiVersion: octavia.openstack.org/v1beta1 +kind: Octavia +metadata: + name: octavia +spec: + databaseInstance: openstack + databaseUser: octavia + serviceUser: octavia + rabbitMqClusterName: rabbitmq + secret: osp-secret + preserveJobs: false + customServiceConfig: | + [DEFAULT] + debug = true + octaviaHousekeeping: + databaseInstance: openstack + databaseUser: octavia + serviceUser: octavia + serviceAccount: octavia + role: housekeeping + certssecret: octavia-amp-cert-data + certspassphrasesecret: octavia-ca-passphrase + secret: osp-secret + preserveJobs: false + customServiceConfig: | + [DEFAULT] + debug = true + tls: + caBundleSecretName: combined-ca-bundle + octaviaHealthManager: + databaseInstance: openstack + databaseUser: octavia + serviceUser: octavia + serviceAccount: octavia + role: healthmanager + certssecret: octavia-amp-cert-data + certspassphrasesecret: octavia-ca-passphrase + secret: osp-secret + preserveJobs: false + customServiceConfig: | + [DEFAULT] + debug = true + tls: + caBundleSecretName: combined-ca-bundle + octaviaWorker: + databaseInstance: openstack + databaseUser: octavia + serviceUser: octavia + serviceAccount: octavia + role: worker + certssecret: octavia-amp-cert-data + certspassphrasesecret: octavia-ca-passphrase + secret: osp-secret + preserveJobs: false + customServiceConfig: | + [DEFAULT] + debug = true + tls: + caBundleSecretName: combined-ca-bundle + octaviaAPI: + databaseInstance: openstack + databaseUser: octavia + serviceUser: octavia + serviceAccount: octavia + secret: osp-secret + preserveJobs: false + customServiceConfig: | + [DEFAULT] + debug = true + tls: + api: + internal: + secretName: cert-octavia-internal-svc + public: + secretName: cert-octavia-public-svc + caBundleSecretName: combined-ca-bundle diff --git a/controllers/amphoracontroller_controller.go b/controllers/amphoracontroller_controller.go index 4516666a..f7a9dd06 100644 --- a/controllers/amphoracontroller_controller.go +++ b/controllers/amphoracontroller_controller.go @@ -33,6 +33,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/labels" nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment" "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" @@ -45,12 +46,18 @@ import ( corev1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" ) // OctaviaAmphoraControllerReconciler reconciles an OctaviaAmmphoraController object @@ -111,6 +118,7 @@ func (r *OctaviaAmphoraControllerReconciler) Reconcile(ctx context.Context, req condition.UnknownCondition(condition.InputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), condition.UnknownCondition(condition.DeploymentReadyCondition, condition.InitReason, condition.DeploymentReadyInitMessage), condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage), + condition.UnknownCondition(condition.TLSInputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), ) // TODO(beagles): what other conditions? instance.Status.Conditions.Init(&cl) @@ -279,6 +287,38 @@ func (r *OctaviaAmphoraControllerReconciler) reconcileNormal(ctx context.Context instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage) + // + // TLS input validation + // + // Validate the CA cert secret if provided + if instance.Spec.TLS.CaBundleSecretName != "" { + hash, ctrlResult, err := tls.ValidateCACertSecret( + ctx, + helper.GetClient(), + types.NamespacedName{ + Name: instance.Spec.TLS.CaBundleSecretName, + Namespace: instance.Namespace, + }, + ) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.TLSInputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.TLSInputErrorMessage, + err.Error())) + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + if hash != "" { + configMapVars[tls.CABundleKey] = env.SetValue(hash) + } + } + // all cert input checks out so report InputReady + instance.Status.Conditions.MarkTrue(condition.TLSInputReadyCondition, condition.InputReadyMessage) + // // create hash over all the different input resources to identify if any those changed // and a restart/recreate is required. @@ -420,7 +460,19 @@ func (r *OctaviaAmphoraControllerReconciler) generateServiceConfigMaps( ) error { r.Log.Info(fmt.Sprintf("generating service config map for %s (%s)", instance.Name, instance.Kind)) cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(instance.ObjectMeta.Name), map[string]string{}) - customData := map[string]string{common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig} + db, err := mariadbv1.GetDatabaseByName(ctx, helper, octavia.DatabaseName) + if err != nil { + return err + } + var tlsCfg *tls.Service + if instance.Spec.TLS.CaBundleSecretName != "" { + tlsCfg = &tls.Service{} + } + + customData := map[string]string{ + common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig, + "my.cnf": db.GetDatabaseClientConfig(tlsCfg), //(mschuppert) for now just get the default my.cnf + } for key, data := range instance.Spec.DefaultConfigOverwrite { customData[key] = data } @@ -458,13 +510,13 @@ func (r *OctaviaAmphoraControllerReconciler) generateServiceConfigMaps( mariadbv1.MariaDBAccountReadyMessage) templateParameters := map[string]interface{}{ - "DatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s", + "DatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s?read_default_file=/etc/my.cnf", databaseAccount.Spec.UserName, string(dbSecret.Data[mariadbv1.DatabasePasswordSelector]), instance.Spec.DatabaseHostname, octavia.DatabaseName, ), - "PersistenceDatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s", + "PersistenceDatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s?read_default_file=/etc/my.cnf", persistenceDatabaseAccount.Spec.UserName, string(persistenceDbSecret.Data[mariadbv1.DatabasePasswordSelector]), instance.Spec.DatabaseHostname, @@ -626,12 +678,40 @@ func (r *OctaviaAmphoraControllerReconciler) createHashOfInputHashes( // SetupWithManager sets up the controller with the Manager. func (r *OctaviaAmphoraControllerReconciler) SetupWithManager(mgr ctrl.Manager) error { + // index passwordSecretField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &octaviav1.OctaviaAmphoraController{}, passwordSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*octaviav1.OctaviaAmphoraController) + if cr.Spec.Secret == "" { + return nil + } + return []string{cr.Spec.Secret} + }); err != nil { + return err + } + + // index caBundleSecretNameField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &octaviav1.OctaviaAmphoraController{}, caBundleSecretNameField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*octaviav1.OctaviaAmphoraController) + if cr.Spec.TLS.CaBundleSecretName == "" { + return nil + } + return []string{cr.Spec.TLS.CaBundleSecretName} + }); err != nil { + return err + } return ctrl.NewControllerManagedBy(mgr). For(&octaviav1.OctaviaAmphoraController{}). Owns(&corev1.Service{}). Owns(&corev1.Secret{}). Owns(&corev1.ConfigMap{}). Owns(&appsv1.DaemonSet{}). + Watches( + &corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). Complete(r) } @@ -673,3 +753,36 @@ func getPodIPs(name string, ns string, client kubernetes.Interface, log *logr.Lo sort.Strings(result) return result, nil } + +func (r *OctaviaAmphoraControllerReconciler) findObjectsForSrc(ctx context.Context, src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(context.Background()).WithName("Controllers").WithName("Amphora") + + for _, field := range allWatchFields { + crList := &octaviav1.OctaviaAmphoraControllerList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(field, src.GetName()), + Namespace: src.GetNamespace(), + } + err := r.Client.List(context.TODO(), crList, listOps) + if err != nil { + return []reconcile.Request{} + } + + for _, item := range crList.Items { + l.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace())) + + requests = append(requests, + reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + }, + ) + } + } + + return requests +} diff --git a/controllers/octavia_controller.go b/controllers/octavia_controller.go index acda675f..280bad63 100644 --- a/controllers/octavia_controller.go +++ b/controllers/octavia_controller.go @@ -39,6 +39,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/secret" oko_secret "github.com/openstack-k8s-operators/lib-common/modules/common/secret" "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" @@ -202,6 +203,23 @@ func (r *OctaviaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re return r.reconcileNormal(ctx, instance, helper) } +// fields to index to reconcile when change +const ( + passwordSecretField = ".spec.secret" + caBundleSecretNameField = ".spec.tls.caBundleSecretName" + tlsAPIInternalField = ".spec.tls.api.internal.secretName" + tlsAPIPublicField = ".spec.tls.api.public.secretName" +) + +var ( + allWatchFields = []string{ + passwordSecretField, + caBundleSecretNameField, + tlsAPIInternalField, + tlsAPIPublicField, + } +) + // SetupWithManager sets up the controller with the Manager. func (r *OctaviaReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). @@ -541,6 +559,7 @@ func (r *OctaviaReconciler) reconcileNormal(ctx context.Context, instance *octav return ctrl.Result{}, fmt.Errorf("failed create network annotation from %s: %w", instance.Spec.OctaviaAPI.NetworkAttachments, err) } + instance.Status.Conditions.MarkTrue(condition.NetworkAttachmentsReadyCondition, condition.NetworkAttachmentsReadyMessage) // Handle service init ctrlResult, err := r.reconcileInit(ctx, instance, helper, serviceLabels, serviceAnnotations) @@ -1039,11 +1058,19 @@ func (r *OctaviaReconciler) generateServiceConfigMaps( cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(octavia.ServiceName), map[string]string{}) + var tlsCfg *tls.Service + if instance.Spec.OctaviaAPI.TLS.Ca.CaBundleSecretName != "" { + tlsCfg = &tls.Service{} + } + // customData hold any customization for the service. // custom.conf is going to /etc//.conf.d // all other files get placed into /etc/ to allow overwrite of e.g. logging.conf or policy.json // TODO: make sure custom.conf can not be overwritten - customData := map[string]string{common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig} + customData := map[string]string{ + common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig, + "my.cnf": octaviaDb.GetDatabaseClientConfig(tlsCfg), //(mschuppert) for now just get the default my.cnf + } for key, data := range instance.Spec.DefaultConfigOverwrite { customData[key] = data } @@ -1057,13 +1084,13 @@ func (r *OctaviaReconciler) generateServiceConfigMaps( // hence only passing the database related parameters templateParameters := map[string]interface{}{ "MinimalConfig": true, // This tells the template to generate a minimal config - "DatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s", + "DatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s?read_default_file=/etc/my.cnf", databaseAccount.Spec.UserName, string(dbSecret.Data[mariadbv1.DatabasePasswordSelector]), instance.Status.DatabaseHostname, octavia.DatabaseName, ), - "PersistenceDatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s", + "PersistenceDatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s?read_default_file=/etc/my.cnf", persistenceDatabaseAccount.Spec.UserName, string(persistenceDbSecret.Data[mariadbv1.DatabasePasswordSelector]), instance.Status.DatabaseHostname, @@ -1144,6 +1171,7 @@ func (r *OctaviaReconciler) apiDeploymentCreateOrUpdate(instance *octaviav1.Octa deployment.Spec.TransportURLSecret = instance.Status.TransportURLSecret deployment.Spec.Secret = instance.Spec.Secret deployment.Spec.ServiceAccount = instance.RbacResourceName() + deployment.Spec.TLS = instance.Spec.OctaviaAPI.TLS if len(deployment.Spec.NodeSelector) == 0 { deployment.Spec.NodeSelector = instance.Spec.NodeSelector } @@ -1253,6 +1281,7 @@ func (r *OctaviaReconciler) amphoraControllerDaemonSetCreateOrUpdate( daemonset.Spec.LbMgmtNetworkID = tenantNetworkID daemonset.Spec.AmphoraCustomFlavors = instance.Spec.AmphoraCustomFlavors daemonset.Spec.RedisHostIPs = instance.Status.RedisHostIPs + daemonset.Spec.TLS = instance.Spec.OctaviaAPI.TLS.Ca if len(daemonset.Spec.NodeSelector) == 0 { daemonset.Spec.NodeSelector = instance.Spec.NodeSelector } diff --git a/controllers/octaviaapi_controller.go b/controllers/octaviaapi_controller.go index 5d255302..8f73f555 100644 --- a/controllers/octaviaapi_controller.go +++ b/controllers/octaviaapi_controller.go @@ -35,6 +35,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/secret" oko_secret "github.com/openstack-k8s-operators/lib-common/modules/common/secret" "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" @@ -46,13 +47,19 @@ import ( batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" ) // OctaviaAPIReconciler reconciles a OctaviaAPI object @@ -156,6 +163,7 @@ func (r *OctaviaAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request) condition.UnknownCondition(condition.KeystoneServiceReadyCondition, condition.InitReason, ""), condition.UnknownCondition(condition.KeystoneEndpointReadyCondition, condition.InitReason, ""), condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage), + condition.UnknownCondition(condition.TLSInputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), ) instance.Status.Conditions.Init(&cl) @@ -179,6 +187,55 @@ func (r *OctaviaAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request) // SetupWithManager sets up the controller with the Manager. func (r *OctaviaAPIReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { crs := &octaviav1.OctaviaAPIList{} + + // index passwordSecretField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &octaviav1.OctaviaAPI{}, passwordSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*octaviav1.OctaviaAPI) + if cr.Spec.Secret == "" { + return nil + } + return []string{cr.Spec.Secret} + }); err != nil { + return err + } + + // index caBundleSecretNameField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &octaviav1.OctaviaAPI{}, caBundleSecretNameField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*octaviav1.OctaviaAPI) + if cr.Spec.TLS.CaBundleSecretName == "" { + return nil + } + return []string{cr.Spec.TLS.CaBundleSecretName} + }); err != nil { + return err + } + + // index tlsAPIInternalField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &octaviav1.OctaviaAPI{}, tlsAPIInternalField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*octaviav1.OctaviaAPI) + if cr.Spec.TLS.API.Internal.SecretName == nil { + return nil + } + return []string{*cr.Spec.TLS.API.Internal.SecretName} + }); err != nil { + return err + } + + // index tlsAPIPublicField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &octaviav1.OctaviaAPI{}, tlsAPIPublicField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*octaviav1.OctaviaAPI) + if cr.Spec.TLS.API.Public.SecretName == nil { + return nil + } + return []string{*cr.Spec.TLS.API.Public.SecretName} + }); err != nil { + return err + } + return ctrl.NewControllerManagedBy(mgr). For(&octaviav1.OctaviaAPI{}). Owns(&keystonev1.KeystoneService{}). @@ -189,9 +246,47 @@ func (r *OctaviaAPIReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma Owns(&corev1.ConfigMap{}). Owns(&appsv1.Deployment{}). Watches(&ovnclient.OVNDBCluster{}, handler.EnqueueRequestsFromMapFunc(ovnclient.OVNDBClusterNamespaceMapFunc(crs, mgr.GetClient(), r.GetLogger(ctx)))). + Watches( + &corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). Complete(r) } +func (r *OctaviaAPIReconciler) findObjectsForSrc(ctx context.Context, src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(context.Background()).WithName("Controllers").WithName("OctaviaAPI") + + for _, field := range allWatchFields { + crList := &octaviav1.OctaviaAPIList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(field, src.GetName()), + Namespace: src.GetNamespace(), + } + err := r.Client.List(context.TODO(), crList, listOps) + if err != nil { + return []reconcile.Request{} + } + + for _, item := range crList.Items { + l.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace())) + + requests = append(requests, + reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + }, + ) + } + } + + return requests +} + func (r *OctaviaAPIReconciler) reconcileDelete(ctx context.Context, instance *octaviav1.OctaviaAPI, helper *helper.Helper) (ctrl.Result, error) { Log := r.GetLogger(ctx) util.LogForObject(helper, "Reconciling Service delete", instance) @@ -342,7 +437,11 @@ func (r *OctaviaAPIReconciler) reconcileInit( } // create service - end - // TODO: TLS, pass in https as protocol, create TLS cert + // if TLS is enabled + if instance.Spec.TLS.API.Enabled(endpointType) { + // set endpoint protocol to https + data.Protocol = ptr.To(service.ProtocolHTTPS) + } apiEndpoints[string(endpointType)], err = svc.GetAPIEndpoint( svcOverride.EndpointURL, data.Protocol, data.Path) if err != nil { @@ -486,6 +585,55 @@ func (r *OctaviaAPIReconciler) reconcileNormal(ctx context.Context, instance *oc // run check OpenStack secret - end + // + // TLS input validation + // + // Validate the CA cert secret if provided + if instance.Spec.TLS.CaBundleSecretName != "" { + hash, ctrlResult, err := tls.ValidateCACertSecret( + ctx, + helper.GetClient(), + types.NamespacedName{ + Name: instance.Spec.TLS.CaBundleSecretName, + Namespace: instance.Namespace, + }, + ) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.TLSInputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.TLSInputErrorMessage, + err.Error())) + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + if hash != "" { + configMapVars[tls.CABundleKey] = env.SetValue(hash) + } + + // Validate API service certs secrets + certsHash, ctrlResult, err := instance.Spec.TLS.API.ValidateCertSecrets(ctx, helper, instance.Namespace) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.TLSInputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.TLSInputErrorMessage, + err.Error())) + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + configMapVars[tls.TLSHashName] = env.SetValue(certsHash) + } + + // all cert input checks out so report InputReady + instance.Status.Conditions.MarkTrue(condition.TLSInputReadyCondition, condition.InputReadyMessage) + // // Create ConfigMaps and Secrets required as input for the Service and calculate an overall hash of hashes // @@ -588,10 +736,19 @@ func (r *OctaviaAPIReconciler) reconcileNormal(ctx context.Context, instance *oc // // Define a new Deployment object - depl := deployment.NewDeployment( - octaviaapi.Deployment(instance, inputHash, serviceLabels, serviceAnnotations), - time.Duration(5)*time.Second, - ) + + deplDef, err := octaviaapi.Deployment(instance, inputHash, serviceLabels, serviceAnnotations) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.DeploymentReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.DeploymentReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + + depl := deployment.NewDeployment(deplDef, time.Duration(5)*time.Second) ctrlResult, err = depl.CreateOrPatch(ctx, helper) if err != nil { @@ -672,11 +829,23 @@ func (r *OctaviaAPIReconciler) generateServiceConfigMaps( cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(octavia.ServiceName), map[string]string{}) + db, err := mariadbv1.GetDatabaseByName(ctx, h, octavia.DatabaseName) + if err != nil { + return err + } + var tlsCfg *tls.Service + if instance.Spec.TLS.CaBundleSecretName != "" { + tlsCfg = &tls.Service{} + } + // customData hold any customization for the service. // custom.conf is going to /etc//.conf.d // all other files get placed into /etc/ to allow overwrite of e.g. logging.conf or policy.json // TODO: make sure custom.conf can not be overwritten - customData := map[string]string{common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig} + customData := map[string]string{ + common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig, + "my.cnf": db.GetDatabaseClientConfig(tlsCfg), //(mschuppert) for now just get the default my.cnf + } for key, data := range instance.Spec.DefaultConfigOverwrite { customData[key] = data } @@ -735,13 +904,13 @@ func (r *OctaviaAPIReconciler) generateServiceConfigMaps( mariadbv1.MariaDBAccountReadyMessage) templateParameters := map[string]interface{}{ - "DatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s", + "DatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s?read_default_file=/etc/my.cnf", databaseAccount.Spec.UserName, string(dbSecret.Data[mariadbv1.DatabasePasswordSelector]), instance.Spec.DatabaseHostname, octavia.DatabaseName, ), - "PersistenceDatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s", + "PersistenceDatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s?read_default_file=/etc/my.cnf", persistenceDatabaseAccount.Spec.UserName, string(persistenceDbSecret.Data[mariadbv1.DatabasePasswordSelector]), instance.Spec.DatabaseHostname, @@ -762,6 +931,21 @@ func (r *OctaviaAPIReconciler) generateServiceConfigMaps( return err } + // create httpd vhost template parameters + httpdVhostConfig := map[string]interface{}{} + for _, endpt := range []service.Endpoint{service.EndpointInternal, service.EndpointPublic} { + endptConfig := map[string]interface{}{} + endptConfig["ServerName"] = fmt.Sprintf("%s-%s.%s.svc", octavia.ServiceName, endpt.String(), instance.Namespace) + endptConfig["TLS"] = false // default TLS to false, and set it bellow to true if enabled + if instance.Spec.TLS.API.Enabled(endpt) { + endptConfig["TLS"] = true + endptConfig["SSLCertificateFile"] = fmt.Sprintf("/etc/pki/tls/certs/%s.crt", endpt.String()) + endptConfig["SSLCertificateKeyFile"] = fmt.Sprintf("/etc/pki/tls/private/%s.key", endpt.String()) + } + httpdVhostConfig[endpt.String()] = endptConfig + } + templateParameters["VHosts"] = httpdVhostConfig + cms := []util.Template{ // ScriptsConfigMap { diff --git a/go.mod b/go.mod index fed0868e..da258abe 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,10 @@ require ( sigs.k8s.io/controller-runtime v0.16.5 ) -require golang.org/x/crypto v0.19.0 +require ( + golang.org/x/crypto v0.19.0 + k8s.io/utils v0.0.0-20240102154912-e7106e64919e +) require ( github.com/beorn7/perks v1.0.1 // indirect @@ -77,7 +80,6 @@ require ( k8s.io/component-base v0.28.7 // indirect k8s.io/klog/v2 v2.120.1 // indirect k8s.io/kube-openapi v0.0.0-20240126223410-2919ad4fcfec // indirect - k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/pkg/amphoracontrollers/daemonset.go b/pkg/amphoracontrollers/daemonset.go index cea4da00..adba8df8 100644 --- a/pkg/amphoracontrollers/daemonset.go +++ b/pkg/amphoracontrollers/daemonset.go @@ -84,6 +84,12 @@ func DaemonSet( envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") envVars["CONFIG_HASH"] = env.SetValue(configHash) + // Add the CA bundle + if instance.Spec.TLS.CaBundleSecretName != "" { + volumes = append(volumes, instance.Spec.TLS.CreateVolume()) + volumeMounts = append(volumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...) + } + daemonset := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: serviceName, diff --git a/pkg/octavia/dbsync.go b/pkg/octavia/dbsync.go index 5da05d89..c67992bb 100644 --- a/pkg/octavia/dbsync.go +++ b/pkg/octavia/dbsync.go @@ -45,6 +45,12 @@ func DbSyncJob( envVars := map[string]env.Setter{} envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") + // add CA cert if defined + if instance.Spec.OctaviaAPI.TLS.CaBundleSecretName != "" { + volumes = append(volumes, instance.Spec.OctaviaAPI.TLS.CreateVolume()) + volumeMounts = append(volumeMounts, instance.Spec.OctaviaAPI.TLS.CreateVolumeMounts(nil)...) + } + job := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: instance.Name + "-db-sync", diff --git a/pkg/octavia/volumes.go b/pkg/octavia/volumes.go index f4a1a769..5683e4fa 100644 --- a/pkg/octavia/volumes.go +++ b/pkg/octavia/volumes.go @@ -70,6 +70,12 @@ func GetInitVolumeMounts() []corev1.VolumeMount { MountPath: "/var/lib/config-data/merged", ReadOnly: false, }, + { + Name: "config-data", + MountPath: "/etc/my.cnf", + SubPath: "my.cnf", + ReadOnly: true, + }, } } diff --git a/pkg/octaviaapi/deployment.go b/pkg/octaviaapi/deployment.go index ded9dd22..529508ec 100644 --- a/pkg/octaviaapi/deployment.go +++ b/pkg/octaviaapi/deployment.go @@ -21,6 +21,8 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common" "github.com/openstack-k8s-operators/lib-common/modules/common/affinity" "github.com/openstack-k8s-operators/lib-common/modules/common/env" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" "github.com/openstack-k8s-operators/octavia-operator/pkg/octavia" @@ -41,7 +43,7 @@ func Deployment( configHash string, labels map[string]string, annotations map[string]string, -) *appsv1.Deployment { +) (*appsv1.Deployment, error) { runAsUser := int64(0) initVolumeMounts := octavia.GetInitVolumeMounts() @@ -71,6 +73,40 @@ func Deployment( Port: intstr.IntOrString{Type: intstr.Int, IntVal: int32(octavia.OctaviaPublicPort)}, } + if instance.Spec.TLS.API.Enabled(service.EndpointPublic) { + livenessProbe.HTTPGet.Scheme = corev1.URISchemeHTTPS + readinessProbe.HTTPGet.Scheme = corev1.URISchemeHTTPS + } + + // create Volume and VolumeMounts + volumes := getVolumes(instance.Name) + volumeMounts := getVolumeMounts("octavia-api") + + // add CA cert if defined + if instance.Spec.TLS.CaBundleSecretName != "" { + volumes = append(volumes, instance.Spec.TLS.CreateVolume()) + volumeMounts = append(volumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...) + } + + for _, endpt := range []service.Endpoint{service.EndpointInternal, service.EndpointPublic} { + if instance.Spec.TLS.API.Enabled(endpt) { + var tlsEndptCfg tls.GenericService + switch endpt { + case service.EndpointPublic: + tlsEndptCfg = instance.Spec.TLS.API.Public + case service.EndpointInternal: + tlsEndptCfg = instance.Spec.TLS.API.Internal + } + + svc, err := tlsEndptCfg.ToService() + if err != nil { + return nil, err + } + volumes = append(volumes, svc.CreateVolume(endpt.String())) + volumeMounts = append(volumeMounts, svc.CreateVolumeMounts(endpt.String())...) + } + } + envVars := map[string]env.Setter{} envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") envVars["CONFIG_HASH"] = env.SetValue(configHash) @@ -111,7 +147,7 @@ func Deployment( RunAsUser: &runAsUser, }, Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), - VolumeMounts: getVolumeMounts("octavia-api"), + VolumeMounts: volumeMounts, Resources: instance.Spec.Resources, ReadinessProbe: readinessProbe, LivenessProbe: livenessProbe, @@ -126,7 +162,7 @@ func Deployment( LivenessProbe: livenessProbe, }, }, - Volumes: getVolumes(instance.Name), + Volumes: volumes, }, }, }, @@ -156,5 +192,5 @@ func Deployment( } deployment.Spec.Template.Spec.InitContainers = octavia.InitContainer(initContainerDetails) - return deployment + return deployment, nil } diff --git a/templates/octaviaapi/config/httpd.conf b/templates/octaviaapi/config/httpd.conf index a8aeef37..45f233f9 100644 --- a/templates/octaviaapi/config/httpd.conf +++ b/templates/octaviaapi/config/httpd.conf @@ -10,15 +10,11 @@ Group apache Listen 9876 - TypesConfig /etc/mime.types Include conf.modules.d/*.conf -# XXX: To disable SSL -#+ exec /usr/sbin/httpd -#AH00526: Syntax error on line 85 of /etc/httpd/conf.d/ssl.conf: -#SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty -#Include conf.d/*.conf + +Include conf.d/*.conf LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" proxy @@ -27,22 +23,35 @@ SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded CustomLog /dev/stdout combined env=!forwarded CustomLog /dev/stdout proxy env=forwarded - - = 2.4> - ErrorLogFormat "%M" - - ErrorLog /dev/stdout - SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded - CustomLog /dev/stdout combined env=!forwarded - CustomLog /dev/stdout proxy env=forwarded - - ## WSGI configuration - WSGIProcessGroup octavia-wsgi - WSGIApplicationGroup %{GLOBAL} - WSGIPassAuthorization On - WSGIDaemonProcess octavia-wsgi processes=5 threads=1 user=octavia group=octavia display-name=%{GROUP} - WSGIScriptAlias / /usr/bin/octavia-wsgi - +{{ range $endpt, $vhost := .VHosts }} + # {{ $endpt }} vhost {{ $vhost.ServerName }} configuration + + ServerName {{ $vhost.ServerName }} + = 2.4> + ErrorLogFormat "%M" + + ErrorLog /dev/stdout + SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded + CustomLog /dev/stdout combined env=!forwarded + CustomLog /dev/stdout proxy env=forwarded + + {{- if $vhost.TLS }} + SetEnvIf X-Forwarded-Proto https HTTPS=1 + + ## SSL directives + SSLEngine on + SSLCertificateFile "{{ $vhost.SSLCertificateFile }}" + SSLCertificateKeyFile "{{ $vhost.SSLCertificateKeyFile }}" + {{- end }} + + ## WSGI configuration + WSGIProcessGroup {{ $endpt }} + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + WSGIDaemonProcess {{ $endpt }} processes=5 threads=1 user=octavia group=octavia display-name={{ $endpt }} + WSGIScriptAlias / /usr/bin/octavia-wsgi + +{{ end }} Alias /octavia-api /usr/bin/octavia-wsgi diff --git a/templates/octaviaapi/config/octavia-api-config.json b/templates/octaviaapi/config/octavia-api-config.json index 05592062..75f4ffec 100644 --- a/templates/octaviaapi/config/octavia-api-config.json +++ b/templates/octaviaapi/config/octavia-api-config.json @@ -18,7 +18,29 @@ "dest": "/etc/httpd/conf/httpd.conf", "owner": "root", "perm": "0644" - } + }, + { + "source": "/var/lib/config-data/merged/ssl.conf", + "dest": "/etc/httpd/conf.d/ssl.conf", + "owner": "root", + "perm": "0644" + }, + { + "source": "/var/lib/config-data/tls/certs/*", + "dest": "/etc/pki/tls/certs/", + "owner": "octavia", + "perm": "0440", + "optional": true, + "merge": true + }, + { + "source": "/var/lib/config-data/tls/private/*", + "dest": "/etc/pki/tls/private/", + "owner": "octavia", + "perm": "0400", + "optional": true, + "merge": true + } ], "permissions": [ { diff --git a/templates/octaviaapi/config/ssl.conf b/templates/octaviaapi/config/ssl.conf new file mode 100644 index 00000000..e3da4ecb --- /dev/null +++ b/templates/octaviaapi/config/ssl.conf @@ -0,0 +1,21 @@ + + SSLRandomSeed startup builtin + SSLRandomSeed startup file:/dev/urandom 512 + SSLRandomSeed connect builtin + SSLRandomSeed connect file:/dev/urandom 512 + + AddType application/x-x509-ca-cert .crt + AddType application/x-pkcs7-crl .crl + + SSLPassPhraseDialog builtin + SSLSessionCache "shmcb:/var/cache/mod_ssl/scache(512000)" + SSLSessionCacheTimeout 300 + Mutex default + SSLCryptoDevice builtin + SSLHonorCipherOrder On + SSLUseStapling Off + SSLStaplingCache "shmcb:/run/httpd/ssl_stapling(32768)" + SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!RC4:!3DES + SSLProtocol all -SSLv2 -SSLv3 -TLSv1 + SSLOptions StdEnvVars + diff --git a/tests/kuttl/tests/octavia_tls/01-assert.yaml b/tests/kuttl/tests/octavia_tls/01-assert.yaml new file mode 100644 index 00000000..aafb73fd --- /dev/null +++ b/tests/kuttl/tests/octavia_tls/01-assert.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Secret +metadata: + name: cert-octavia-internal-svc +--- +apiVersion: v1 +kind: Secret +metadata: + name: cert-octavia-public-svc +--- +apiVersion: v1 +kind: Secret +metadata: + name: combined-ca-bundle diff --git a/tests/kuttl/tests/octavia_tls/01-tls-certs.yaml b/tests/kuttl/tests/octavia_tls/01-tls-certs.yaml new file mode 100644 index 00000000..610a926e --- /dev/null +++ b/tests/kuttl/tests/octavia_tls/01-tls-certs.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Secret +metadata: + name: combined-ca-bundle + labels: + service: octavia +data: + tls-ca-bundle.pem: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNLZ0F3SUJBZ0lRUHhtRFFscmxjNTNhb215RVU5MU9pakFLQmdncWhrak9QUVFEQWpBZU1Sd3cKR2dZRFZRUURFeE5yZFhSMGJDMXpaV3htYzJsbmJtVmtMV05oTUI0WERUSXpNVEF4T0RFeU1EazFNMW9YRFRJMApNREV4TmpFeU1EazFNMW93SGpFY01Cb0dBMVVFQXhNVGEzVjBkR3d0YzJWc1puTnBaMjVsWkMxallUQlpNQk1HCkJ5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCSVdJY0JiR0cveEg4Lzlkc2lMbkJCdnRqcEZoQ2JRM3U4R0EKZXBVcnhTY25XM0hrZ2hrc1BCVE12M3NCeGdnVFQwL0Eva0dtazRYTkJ0dElnbUZJaFBpalFqQkFNQTRHQTFVZApEd0VCL3dRRUF3SUNwREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlFKaDd3VklFYjgxcFlsCkl3RDAraTBwSnlCTjNqQUtCZ2dxaGtqT1BRUURBZ05KQURCR0FpRUF2a3h5RzZjNzltSDlRWHRIVWFSM014REkKUUVRRGVtL1hZR3VGY1ZCUDJpQUNJUUNFeEZqeStQUTBkNFU5dEJacTVOd1gzdmxibnQxVlNCYWE5VFIrNkNkbAozdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tIyByb290Y2EtaW50ZXJuYWwKLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmekNDQVNhZ0F3SUJBZ0lRUWxlcTNZcDBtU2kwVDNiTm03Q29UVEFLQmdncWhrak9QUVFEQWpBZ01SNHcKSEFZRFZRUURFeFZ5YjI5MFkyRXRhM1YwZEd3dGFXNTBaWEp1WVd3d0hoY05NalF3TVRFMU1URTBOelUwV2hjTgpNelF3TVRFeU1URTBOelUwV2pBZ01SNHdIQVlEVlFRREV4VnliMjkwWTJFdGEzVjBkR3d0YVc1MFpYSnVZV3d3CldUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTRk9rNHJPUldVUGhoTjUrK09EN1I2MW5Gb1lBY0QKenpvUS91SW93NktjeGhwRWNQTDFxb3ZZUGxUYUJabEh3c2FpNE50VHA4aDA1RHVRSGZKOE9JNXFvMEl3UURBTwpCZ05WSFE4QkFmOEVCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXE3TGtFSk1TCm1MOVpKWjBSOUluKzZkclhycEl3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnVlN1K00ydnZ3QlF3eTJHMVlhdkkKQld2RGtSNlRla0I5U0VqdzJIblRSMWtDSUZSNFNkWGFPQkFGWjVHa2RLWCtSY2IzaDFIZm52eFJEVW96bTl2agphenp3Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KIyByb290Y2EtcHVibGljCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQpNSUlCZXpDQ0FTS2dBd0lCQWdJUU5IREdZc0JzNzk4aWJERDdxL28ybGpBS0JnZ3Foa2pPUFFRREFqQWVNUnd3CkdnWURWUVFERXhOeWIyOTBZMkV0YTNWMGRHd3RjSFZpYkdsak1CNFhEVEkwTURFeE5URXdNVFV6TmxvWERUTTAKTURFeE1qRXdNVFV6Tmxvd0hqRWNNQm9HQTFVRUF4TVRjbTl2ZEdOaExXdDFkSFJzTFhCMVlteHBZekJaTUJNRwpCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkQ3OGF2WHFocmhDNXc4czlXa2Q0SXBiZUV1MDNDUitYWFVkCmtEek9SeXhhOXdjY0lkRGl2YkdKakpGWlRUY1ZtYmpxMUJNWXNqcjEyVUlFNUVUM1ZscWpRakJBTUE0R0ExVWQKRHdFQi93UUVBd0lDcERBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJUS0ppeldVSjllVUtpMQpkczBscjZjNnNEN0VCREFLQmdncWhrak9QUVFEQWdOSEFEQkVBaUJJWndZcTYxQnFNSmFCNlVjRm9Sc3hlY3dICjV6L3pNT2RyT3llMG1OaThKZ0lnUUxCNHdES3JwZjl0WDJsb00rMHVUb3BBRFNZSW5yY2ZWdTRGQnVZVTNJZz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= +--- +apiVersion: v1 +kind: Secret +metadata: + name: cert-octavia-internal-svc + labels: + service: octavia +data: + ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkVENDQVJxZ0F3SUJBZ0lRTkZUVDE2eTc0RGJaOGJTL25ESDBkakFLQmdncWhrak9QUVFEQWpBYU1SZ3cKRmdZRFZRUURFdzl5YjI5MFkyRXRhVzUwWlhKdVlXd3dIaGNOTWpRd01URXdNVFV5T0RBMFdoY05NalF3TkRBNQpNVFV5T0RBMFdqQWFNUmd3RmdZRFZRUURFdzl5YjI5MFkyRXRhVzUwWlhKdVlXd3dXVEFUQmdjcWhrak9QUUlCCkJnZ3Foa2pPUFFNQkJ3TkNBQVFjK2d5OVFCNmw1NFNBQlkxUTJKZWx5MEhSTGEvMzlkRUxzU2RhNnJDRENKQWwKWjJ2bGlGbUo5WVlJNCtSbGRIejJWNXYvYjBpK2x0RjcxMGZ1OHJTbW8wSXdRREFPQmdOVkhROEJBZjhFQkFNQwpBcVF3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVeUsyc0hXaUxHNnR6bWlVbENkUmhsRTJLCnNHSXdDZ1lJS29aSXpqMEVBd0lEU1FBd1JnSWhBSzVtTi9zQlBVcXAwckd1QjhnMVRxY21KR3ZMVUpyNjlnaEEKaEozMldCT1BBaUVBbEtwU0dVTzhac25UcVQrQ1hWbXNuWkxBcVJMV1NhbUI5U2NyczNDZ05zWT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNhekNDQWhHZ0F3SUJBZ0lSQU1GRmpzWkpHY3BuaVBFNXNmQytrOEV3Q2dZSUtvWkl6ajBFQXdJd0dqRVkKTUJZR0ExVUVBeE1QY205dmRHTmhMV2x1ZEdWeWJtRnNNQjRYRFRJME1ERXhOVEV4TkRnMU1sb1hEVE0wTURFeApNakV4TkRnMU1sb3dBRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNRzhQSWwzCnc4RXdXMHdUUG5qRURpU2dTdVI4WHJaajcrSjYyUkJMTHJ3ZUxKdWd1Wm1MaUh3M09uSldWa0hEOVpaZzlYSGUKbGZ6UDY3Wi8rYXBNMzJ5VWJTVUcrRjlBdXlGMHRTK2lPODFkUFRSY1luNzVBK0xWdnk1UkVpOGIvTFkzNTNPbgpxUEhuK2kyeTNLUC9HZkhjSi9lVlVXNFJkV2wyTHEyejRtRDRUK2twS0VwSnRGSTJQa2lrSVNOV2RRdmtEeW1WClF3a1B3U01FVy9yaEdGL2s3b0gvVWtwdy9wU1N1R0M2a1lpSnlwOTFHT0xCMlVoc254Z3dLelh5VS9MdGFrZXoKS2RHSFUvNUNLTTRKczg0ZnlNTDBBNXMxalpZQXZEWkVLNEgvYVpCb3EzV0NoQ1R4WWhIOVVuczhIQy9KbHJCMApHaitwVHNuaEc2cUlFQ2tDQXdFQUFhT0JoakNCZ3pBT0JnTlZIUThCQWY4RUJBTUNCYUF3RXdZRFZSMGxCQXd3CkNnWUlLd1lCQlFVSEF3RXdEQVlEVlIwVEFRSC9CQUl3QURBZkJnTlZIU01FR0RBV2dCVElyYXdkYUlzYnEzT2EKSlNVSjFHR1VUWXF3WWpBdEJnTlZIUkVCQWY4RUl6QWhnaDlyWlhsemRHOXVaUzFwYm5SbGNtNWhiQzV2Y0dWdQpjM1JoWTJzdWMzWmpNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJRTFJYXcxcnRnU0ROZmxBSjJRek9VQjJxU1llCk03ZWdsaXZLVW01cmVOZThBaUVBMU93SGcwQ1YxOUNhYUpSSi9SS25UcXNJTGhNdjBEUVNPdnFwbWc0MWZDTT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= + tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBd2J3OGlYZkR3VEJiVEJNK2VNUU9KS0JLNUh4ZXRtUHY0bnJaRUVzdXZCNHNtNkM1Cm1ZdUlmRGM2Y2xaV1FjUDFsbUQxY2Q2Vi9NL3J0bi81cWt6ZmJKUnRKUWI0WDBDN0lYUzFMNkk3elYwOU5GeGkKZnZrRDR0Vy9MbEVTTHh2OHRqZm5jNmVvOGVmNkxiTGNvLzhaOGR3bjk1VlJiaEYxYVhZdXJiUGlZUGhQNlNrbwpTa20wVWpZK1NLUWhJMVoxQytRUEtaVkRDUS9CSXdSYit1RVlYK1R1Z2Y5U1NuRCtsSks0WUxxUmlJbktuM1VZCjRzSFpTR3lmR0RBck5mSlQ4dTFxUjdNcDBZZFQva0lvemdtenpoL0l3dlFEbXpXTmxnQzhOa1FyZ2Y5cGtHaXIKZFlLRUpQRmlFZjFTZXp3Y0w4bVdzSFFhUDZsT3llRWJxb2dRS1FJREFRQUJBb0lCQVFDQ0hweUdNK05OY040UQo1V2Z6RXJMeEZKdlloRlBVcXFDbWU1NG9uR1ppUU4zekZPc3pYbzBuNkt3ZnVTOHI4cUtUQXNJM1hhbGRhSVRIClNZTDFSN1pVSmdoOGN3Y0VhdVNFbnU5R2MrODRpbVFlTStLUHAwNWQzdlFOOXJPQTRvcEVGSjRtaHJnbzZZYVYKaE9rK1dJc2piNXVFWlV5UTRiYjdRejRzdW9IVVlDYXFkVGlqU1lYQzNOd092YUlwa3pTNEo0cU5CUlhyYnNWSwowaGt4ZFNIY1hKNEREN0hybktpcEsxT2xUbUVObVZYbmlaNnRPcWc2eUNFeXFteWN0UnlUVTZRRzVPbVM2clJVCm82Z25EclA1TlgwRUhuakY3b1lka0JVbGJxWk96UHVGbG5CdUVKOFpTUEtOZHA5ejhuS3lqbGJiL0YxWGRDdEkKZERhVUhmREZBb0dCQVBFZkZZbDhPb2VhU21oSElKcGpxd3RCanYrRjFuOXNJbHNuZWNyQ1JmN243RW53d1hXaQpReStXQ3l6aDJGRVVad1dod2RQeXFJT3NVaG1vaXBIQmN3NlVUaW9xalM4SlpvSDlURFBQUEd5OXIwMHZwRkNuCnFkdjNXMkhWVytRckMrWk1nc2ZKdUlTTnFtbFdFeHpCNFBJQWRHQTdKVzFMY0ZCcG1Zd25DdXZyQW9HQkFNMncKbS94cVRhMmgySjFnNUI0elE5UnBhM280SEoyL2pTaHEzNW9heVNGNWJDYWtnWGRxek0yU0FwQ0x4dzlvY3doRAp3WWRaMWliaHl6b1dDQVZZZ0RlaXViUi96ZTN3Nzk4NktScUNmNnptMk5HOEoxODVDZDdKSjBiaTZBTTgvalpTCnFqWkJIK0FqanF2aFFJM0FMMEdzNlFvc2Z3L3hOL2k1cG00UWM5TTdBb0dBZjZCbFpQVmxnWnN3WVV1c3ZTdWUKUUlIOTc5Qm12ZUY5dWVRR09rVmtpVTAzSzlnTWZuaFp1WmxnNXV2UDlQS29xVGw2Zi9aRUxoWUxDdHZFSk94UgpPMWxTbWswVmw5MFE3aU1scjVLMHVCWWE4TzhUdVVGVnprRjZsQ2s3ejJUZGtwUFM4VzhiaE1YN2VtLytBODIzCmhFQ3JXTGhWMGlrSkZQY2dPQ2YrUnVzQ2dZQTYvcld1cnhxNmUxb3l3WENNVE8zZWhhSUMrd2NTSTdlcjZRTmIKSXVXZlNVRkEwQndtRVNiT3ExczY5Q3hTK2dWTVVJcTRkSWJjdmhSWkE2cW5SZHY0bVI2a2E2ZTM0RXdjZllUKwppb0Z1S1FQMUcvODY2NVF1SndteDVqRGZoT1h3MU1MbkxzU2l0L0FhMGs5K21LbTFMNC9qa0NHZGcvVW16TEMwCmp0bDVzd0tCZ0VPTVI3ODVLT2hyNXFoWmE2b0MvU25JeEptS1FxTWdXU0NGV1pGMDZrVlRnSmthb1hwUEl0bUIKOUZGbE1nTTJSeC91S2V3YTNDSTdQK240ek1uYSswTmhDL0RwNkMxVFVsVWlrcnJYQ3I5a1NPR2dXaEFISDljTwozRENvdkhOcE1PaG51dnhoMlpDeTdYbjFJeGgxWXdlYnVobFZzeTFvR0tDQ0lJb00rOVg1Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== +--- +apiVersion: v1 +kind: Secret +metadata: + name: cert-octavia-public-svc + labels: + service: octavia +data: + ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlekNDQVNLZ0F3SUJBZ0lRTkhER1lzQnM3OThpYkREN3EvbzJsakFLQmdncWhrak9QUVFEQWpBZU1Sd3cKR2dZRFZRUURFeE55YjI5MFkyRXRhM1YwZEd3dGNIVmliR2xqTUI0WERUSTBNREV4TlRFd01UVXpObG9YRFRNMApNREV4TWpFd01UVXpObG93SGpFY01Cb0dBMVVFQXhNVGNtOXZkR05oTFd0MWRIUnNMWEIxWW14cFl6QlpNQk1HCkJ5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRDc4YXZYcWhyaEM1dzhzOVdrZDRJcGJlRXUwM0NSK1hYVWQKa0R6T1J5eGE5d2NjSWREaXZiR0pqSkZaVFRjVm1ianExQk1Zc2pyMTJVSUU1RVQzVmxxalFqQkFNQTRHQTFVZApEd0VCL3dRRUF3SUNwREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlRLSml6V1VKOWVVS2kxCmRzMGxyNmM2c0Q3RUJEQUtCZ2dxaGtqT1BRUURBZ05IQURCRUFpQklad1lxNjFCcU1KYUI2VWNGb1JzeGVjd0gKNXovek1PZHJPeWUwbU5pOEpnSWdRTEI0d0RLcnBmOXRYMmxvTSswdVRvcEFEU1lJbnJjZlZ1NEZCdVlVM0lnPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNiVENDQWhPZ0F3SUJBZ0lSQUtacXlMbUhLNC9VRTZmMi9LNWxiQnN3Q2dZSUtvWkl6ajBFQXdJd0hqRWMKTUJvR0ExVUVBeE1UY205dmRHTmhMV3QxZEhSc0xYQjFZbXhwWXpBZUZ3MHlOREF4TVRVeE1ESXdOVFJhRncwegpOREF4TVRJeE1ESXdOVFJhTUFBd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUMxCjhDcFJRVG1abHNzSUlmZ2hIK2ltUUtFMFdZVlJOeS8vMVM0aDVtV2tBcUZiVkhoUmptbFJ2cCtQUWpKOU16TDUKMXpXdmYxandEQ2pzYUxvL2FwSW9OSXJIcjN4TTRoYWl0emU0RjFwZzNoL3MvblExNWN5Q2U5dHdHR0RuWEllMwo2djBuNE9LNnAwSWJjcVk2Q1RBMTBwcGJZa3V6bzdVRkx6ZWxsc1ZhRlhzZ21JWDg4bTRXNmNBTi84cjJPWUI3Ck9HM0ZNOXAxSUFxT0hyT21EelFlTldqOUVjQy9TSCs5MGg4c1FyY1pvMWtWa1g1b2tpSUhDZjRlc2o3Q08rTGgKR3lsTmZyRzl6QTlPM0c3QVNDWVdPVWwyZTBhNHhZbE9QMmI4ejFEV3NIMTBVYXVsZHlRQXNtbkhtaW1VNzBmKwpEazZkQ1hXVHN4cGZ2cXphOVR4YkFnTUJBQUdqZ1lRd2dZRXdEZ1lEVlIwUEFRSC9CQVFEQWdXZ01CTUdBMVVkCkpRUU1NQW9HQ0NzR0FRVUZCd01CTUF3R0ExVWRFd0VCL3dRQ01BQXdId1lEVlIwakJCZ3dGb0FVeWlZczFsQ2YKWGxDb3RYYk5KYStuT3JBK3hBUXdLd1lEVlIwUkFRSC9CQ0V3SDRJZGEyVjVjM1J2Ym1VdGNIVmliR2xqTG05dwpaVzV6ZEdGamF5NXpkbU13Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnTzAzT2JmNm9uV2RiZG4xa282OVpuTFhMCmtQSHFYU3VRNlcxTDFvY3NDR3NDSVFEakEyVm9pWVdYN0hzSjVGNkZYV3FsZnl0RmduVVgvTmhvT1lIVnB2TWQKSGc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdGZBcVVVRTVtWmJMQ0NINElSL29wa0NoTkZtRlVUY3YvOVV1SWVabHBBS2hXMVI0ClVZNXBVYjZmajBJeWZUTXkrZGMxcjM5WThBd283R2k2UDJxU0tEU0t4Njk4VE9JV29yYzN1QmRhWU40ZjdQNTAKTmVYTWdudmJjQmhnNTF5SHQrcjlKK0RpdXFkQ0czS21PZ2t3TmRLYVcySkxzNk8xQlM4M3BaYkZXaFY3SUppRgovUEp1RnVuQURmL0s5am1BZXpodHhUUGFkU0FLamg2enBnODBIalZvL1JIQXYwaC92ZElmTEVLM0dhTlpGWkYrCmFKSWlCd24rSHJJK3dqdmk0UnNwVFg2eHZjd1BUdHh1d0VnbUZqbEpkbnRHdU1XSlRqOW0vTTlRMXJCOWRGR3IKcFhja0FMSnB4NW9wbE85SC9nNU9uUWwxazdNYVg3NnMydlU4V3dJREFRQUJBb0lCQUd0eVdvdUNLYkk3Qzh6Ugp3dWhOSCtpUFlxUzMrYlB0RTd2UytsdXE1WHZtMGNST0xvQjd5bGNzYks3K09UTVhlWk56TlpGZmMvYlFONXJtCmZwZlZLRnYySzcraU01WjBMMG9KU2k2K0cvSDVQSUdLQkxlUDd5ZGdYa2ZsSGRXRkgrSE9OWlBIakI4UGlFc04KZW4zcnp6ejZFNDdFamxDWTdkOFI4NXNuWDRYREN2bG1CQnhvcnpqVERuK1dTWWpKS09SSk5zY3oxQXFYR1VjVwpQaHRNYkwybC8zN2hPbTA4SjRRWXowTWduOWE5VUFXLzFNS2lXbHVpc1NHNG9YaFNPS1hkdk1IS3VxS09sUDJzCk9xWjBlR3JBNmpKdWlmZVY2Q2NIU2p3VUgwdHpiMmdZQVM2cm5RQlREbFkxR1I4Skx0YWhWREtqdUwyV0hjclkKbHhCOGZBRUNnWUVBMWpxc01weFo5cG9LNkpmRDZzVTU0ZUU1UWlNYlB0MERRZjlYU1J6NW5zQXlraHdKWEZDVwpKWTNiU3BhcGREeEgzakpDQ0VzN3NSWUhUeDRzNVJQSldtWC9oZTFwVEs4TDVlaFV2TmVudG5nVTB3aE4rZVEzCjl4Sk1VbHVYdGkvU0FpNi9jQk5HY1ZjQjFGRStmVzY0VDhqYVVQakRrL0Z2dFRXOXkzZnNVZnNDZ1lFQTJXbXMKYStuZ2RaS24rVTlCMlFCTXB0K0RLL0txNVF6RW1qZDVMSjJMb3FLUjhGbjlpVVZoUVljUEpobDJVV3VjTTl0RQp0QUlYdEY0anVUejlqUUNMMGQ5a09DeCswdTBKUFZJejdlVmFFVGs1enF0azRsTnhVUkJhQ3pCUEJkdjZJd3BDCkR4UXJWRXBXYlMra1JvYTNKSElOdHdjUWt2Nk50N1JIajd1WEVTRUNnWUJsREozbTdZc2Q0QUZmUHg4Qm9YQXgKRkt5T2ZzSytQei9uSkl0R2lHMVNMWFJ0S041ZGRnR3N5eUh5SitqY1ZBYk9UMFNJWnZ4TUJva0NEOGk3Y1Q3Ygo3aHErVUlNSDBkVzU1NEg0NVh4TmZJek9FaSs5dktHTllFc3gyZFJROG5PTDVnTVUyWEt6eVllcVgzd3JiRXR5CkR0cXpzUE9IMkMySiswU0FNaHY5ZXdLQmdRQ2szeWs5TUwvaUNWUk9rTmNybTdtRk5xeS9rQ2dleU43eTRDeUoKTS9RbllrZHYwSjZmRWJrZU96QzJ3TXBrRmtuL1hVR3RqSVN6YUV5STlnS0ZnaXVGL1hWL3orWmhTQllncFl6eAoxR0xIK3ZDbWxIMU4wTjkzRFFKcng3ZTFoc3NhOVhXQS85ZVg5VU96UzFTMWt3V2hvc2haeXdhN29rU1FVaXVPCmlVQ1hZUUtCZ1FEUVZUVHc3WUY3QzNTVmg5OWRObUdTaHV2LzZ2aTJmNDlOMklGMURNQ1haaEpoOUVZck9TV2kKY05oakxGRFhmdzVlZlFURWU3Ykx5bTJGVDd0YnZFSm5USHFyakVuUDRUWExqZnczL3RiQ3RxWVNZRlRqdThFUApadHVwd21ZWjhFVU1pSnVHS2l2SExmSjk2dy8xR21BOHVCZUVtV05YRW9FUU1ySmxuM3g5d3c9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= diff --git a/tests/kuttl/tests/octavia_tls/02-assert.yaml b/tests/kuttl/tests/octavia_tls/02-assert.yaml new file mode 100644 index 00000000..f4213e83 --- /dev/null +++ b/tests/kuttl/tests/octavia_tls/02-assert.yaml @@ -0,0 +1,301 @@ +apiVersion: octavia.openstack.org/v1beta1 +kind: Octavia +metadata: + name: octavia +spec: + customServiceConfig: | + [DEFAULT] + debug = true + databaseInstance: openstack + databaseAccount: octavia + persistenceDatabaseAccount: octavia-persistence + passwordSelectors: + service: OctaviaPassword + preserveJobs: false + secret: osp-secret + serviceUser: octavia + octaviaAPI: + customServiceConfig: | + [DEFAULT] + debug = true + databaseInstance: openstack + databaseAccount: octavia + persistenceDatabaseAccount: octavia-persistence + passwordSelectors: + service: OctaviaPassword + preserveJobs: false + replicas: 1 + secret: osp-secret + serviceUser: octavia + tls: + api: + internal: + secretName: cert-octavia-internal-svc + public: + secretName: cert-octavia-public-svc + caBundleSecretName: combined-ca-bundle + octaviaHousekeeping: + customServiceConfig: | + [DEFAULT] + debug = true + databaseInstance: openstack + databaseAccount: octavia + persistenceDatabaseAccount: octavia-persistence + passwordSelectors: + service: OctaviaPassword + secret: osp-secret + serviceUser: octavia + tls: + caBundleSecretName: combined-ca-bundle + octaviaHealthManager: + customServiceConfig: | + [DEFAULT] + debug = true + databaseInstance: openstack + databaseAccount: octavia + persistenceDatabaseAccount: octavia-persistence + passwordSelectors: + service: OctaviaPassword + secret: osp-secret + serviceUser: octavia + tls: + caBundleSecretName: combined-ca-bundle + octaviaWorker: + customServiceConfig: | + [DEFAULT] + debug = true + databaseInstance: openstack + databaseAccount: octavia + persistenceDatabaseAccount: octavia-persistence + passwordSelectors: + service: OctaviaPassword + secret: osp-secret + serviceUser: octavia + tls: + caBundleSecretName: combined-ca-bundle +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: octavia-api +spec: + replicas: 1 + template: + metadata: + labels: + service: octavia + spec: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchExpressions: + - key: service + operator: In + values: + - octavia-api + topologyKey: kubernetes.io/hostname + weight: 1 + containers: + - args: + - -c + - /usr/local/bin/kolla_set_configs && /usr/local/bin/kolla_start + command: + - /bin/bash + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 9876 + scheme: HTTPS + initialDelaySeconds: 3 + periodSeconds: 13 + successThreshold: 1 + timeoutSeconds: 15 + name: octavia-api + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 9876 + scheme: HTTPS + initialDelaySeconds: 5 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 15 + volumeMounts: + - mountPath: /usr/local/bin/container-scripts + name: scripts + readOnly: true + - mountPath: /var/lib/config-data/merged + name: config-data-merged + - mountPath: /var/lib/kolla/config_files/config.json + name: config-data-merged + readOnly: true + subPath: octavia-api-config.json + - mountPath: /run/octavia + name: octavia-run + - mountPath: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem + name: combined-ca-bundle + readOnly: true + subPath: tls-ca-bundle.pem + - mountPath: /var/lib/config-data/tls/certs/internal.crt + name: internal-tls-certs + readOnly: true + subPath: tls.crt + - mountPath: /var/lib/config-data/tls/private/internal.key + name: internal-tls-certs + readOnly: true + subPath: tls.key + - mountPath: /var/lib/config-data/tls/certs/public.crt + name: public-tls-certs + readOnly: true + subPath: tls.crt + - mountPath: /var/lib/config-data/tls/private/public.key + name: public-tls-certs + readOnly: true + subPath: tls.key + - env: + - name: CONFIG_HASH + - name: KOLLA_CONFIG_STRATEGY + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 9876 + scheme: HTTPS + initialDelaySeconds: 3 + periodSeconds: 13 + successThreshold: 1 + timeoutSeconds: 15 + name: octavia-api-provider-agent + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthcheck + port: 9876 + scheme: HTTPS + initialDelaySeconds: 5 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 15 + volumeMounts: + - mountPath: /usr/local/bin/container-scripts + name: scripts + readOnly: true + - mountPath: /var/lib/config-data/merged + name: config-data-merged + - mountPath: /var/lib/kolla/config_files/config.json + name: config-data-merged + readOnly: true + subPath: octavia-driver-agent-config.json + - mountPath: /run/octavia + name: octavia-run + initContainers: + - args: + - -c + - /usr/local/bin/container-scripts/init.sh + command: + - /bin/bash + env: + - name: AdminPassword + valueFrom: + secretKeyRef: + key: OctaviaPassword + name: osp-secret + - name: TransportURL + valueFrom: + secretKeyRef: + key: transport_url + name: rabbitmq-transport-url-octavia-octavia-transport + - name: DatabaseHost + value: openstack.octavia-kuttl-tests.svc + - name: DatabaseName + value: octavia + imagePullPolicy: IfNotPresent + name: init + resources: {} + volumeMounts: + - mountPath: /usr/local/bin/container-scripts + name: scripts + readOnly: true + - mountPath: /var/lib/config-data/default + name: config-data + readOnly: true + - mountPath: /var/lib/config-data/merged + name: config-data-merged + - mountPath: /etc/my.cnf + name: config-data + readOnly: true + subPath: my.cnf + restartPolicy: Always + serviceAccount: octavia-octavia + serviceAccountName: octavia-octavia + volumes: + - name: scripts + secret: + secretName: octavia-api-scripts + defaultMode: 493 + - name: config-data + secret: + secretName: octavia-api-config-data + defaultMode: 416 + - emptyDir: {} + name: config-data-merged + - emptyDir: {} + name: octavia-run + - name: combined-ca-bundle + secret: + secretName: combined-ca-bundle + defaultMode: 292 + - name: internal-tls-certs + secret: + secretName: cert-octavia-internal-svc + defaultMode: 256 + - name: public-tls-certs + secret: + secretName: cert-octavia-public-svc + defaultMode: 256 +status: + availableReplicas: 1 + replicas: 1 +--- +# the openshift annotations can't be checked through the deployment above +apiVersion: v1 +kind: Pod +metadata: + annotations: + openshift.io/scc: anyuid + labels: + service: octavia +--- +apiVersion: v1 +kind: Service +metadata: + labels: + endpoint: internal + service: octavia + name: octavia-internal +spec: + ports: + - name: octavia-internal + selector: + service: octavia + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + endpoint: public + service: octavia + name: octavia-public +spec: + ports: + - name: octavia-public + selector: + service: octavia + type: ClusterIP diff --git a/tests/kuttl/tests/octavia_tls/02-deploy.yaml b/tests/kuttl/tests/octavia_tls/02-deploy.yaml new file mode 100644 index 00000000..786b83f5 --- /dev/null +++ b/tests/kuttl/tests/octavia_tls/02-deploy.yaml @@ -0,0 +1,76 @@ +apiVersion: octavia.openstack.org/v1beta1 +kind: Octavia +metadata: + name: octavia +spec: + databaseInstance: openstack + databaseUser: octavia + serviceUser: octavia + rabbitMqClusterName: rabbitmq + secret: osp-secret + preserveJobs: false + customServiceConfig: | + [DEFAULT] + debug = true + octaviaHousekeeping: + databaseInstance: openstack + databaseUser: octavia + serviceUser: octavia + serviceAccount: octavia + role: housekeeping + certssecret: octavia-amp-cert-data + certspassphrasesecret: octavia-ca-passphrase + secret: osp-secret + preserveJobs: false + customServiceConfig: | + [DEFAULT] + debug = true + tls: + caBundleSecretName: combined-ca-bundle + octaviaHealthManager: + databaseInstance: openstack + databaseUser: octavia + serviceUser: octavia + serviceAccount: octavia + role: healthmanager + certssecret: octavia-amp-cert-data + certspassphrasesecret: octavia-ca-passphrase + secret: osp-secret + preserveJobs: false + customServiceConfig: | + [DEFAULT] + debug = true + tls: + caBundleSecretName: combined-ca-bundle + octaviaWorker: + databaseInstance: openstack + databaseUser: octavia + serviceUser: octavia + serviceAccount: octavia + role: worker + certssecret: octavia-amp-cert-data + certspassphrasesecret: octavia-ca-passphrase + secret: osp-secret + preserveJobs: false + customServiceConfig: | + [DEFAULT] + debug = true + tls: + caBundleSecretName: combined-ca-bundle + octaviaAPI: + databaseInstance: openstack + databaseUser: octavia + serviceUser: octavia + serviceAccount: octavia + secret: osp-secret + preserveJobs: false + customServiceConfig: | + [DEFAULT] + debug = true + tls: + api: + internal: + secretName: cert-octavia-internal-svc + public: + secretName: cert-octavia-public-svc + caBundleSecretName: combined-ca-bundle