diff --git a/api/bases/barbican.openstack.org_barbicanapis.yaml b/api/bases/barbican.openstack.org_barbicanapis.yaml index f52bb38..58018c2 100644 --- a/api/bases/barbican.openstack.org_barbicanapis.yaml +++ b/api/bases/barbican.openstack.org_barbicanapis.yaml @@ -361,6 +361,36 @@ spec: simpleCryptoBackendKEKSecret: description: Secret containing SimpleCrypto KEK 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/barbican.openstack.org_barbicankeystonelisteners.yaml b/api/bases/barbican.openstack.org_barbicankeystonelisteners.yaml index ddb1d63..2fa8aaa 100644 --- a/api/bases/barbican.openstack.org_barbicankeystonelisteners.yaml +++ b/api/bases/barbican.openstack.org_barbicankeystonelisteners.yaml @@ -183,6 +183,14 @@ spec: simpleCryptoBackendKEKSecret: description: Secret containing SimpleCrypto KEK 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: type: string required: diff --git a/api/bases/barbican.openstack.org_barbicans.yaml b/api/bases/barbican.openstack.org_barbicans.yaml index d9e0ee9..e52b1b4 100644 --- a/api/bases/barbican.openstack.org_barbicans.yaml +++ b/api/bases/barbican.openstack.org_barbicans.yaml @@ -323,6 +323,36 @@ spec: to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object + 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 required: - containerImage type: object diff --git a/api/bases/barbican.openstack.org_barbicanworkers.yaml b/api/bases/barbican.openstack.org_barbicanworkers.yaml index aab3e31..21d6b57 100644 --- a/api/bases/barbican.openstack.org_barbicanworkers.yaml +++ b/api/bases/barbican.openstack.org_barbicanworkers.yaml @@ -181,6 +181,14 @@ spec: simpleCryptoBackendKEKSecret: description: Secret containing SimpleCrypto KEK 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: type: string required: diff --git a/api/v1beta1/barbicanapi_types.go b/api/v1beta1/barbicanapi_types.go index 545f331..d8187bd 100644 --- a/api/v1beta1/barbicanapi_types.go +++ b/api/v1beta1/barbicanapi_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" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -34,6 +35,11 @@ type BarbicanAPITemplate struct { // Override, provides the ability to override the generated manifest of several child resources. Override APIOverrideSpec `json:"override,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/barbicankeystonelistener_types.go b/api/v1beta1/barbicankeystonelistener_types.go index 28e69df..99e4b58 100644 --- a/api/v1beta1/barbicankeystonelistener_types.go +++ b/api/v1beta1/barbicankeystonelistener_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" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -37,6 +38,11 @@ type BarbicanKeystoneListenerSpec struct { DatabaseHostname string `json:"databaseHostname"` TransportURLSecret string `json:"transportURLSecret,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.Ca `json:"tls,omitempty"` } // BarbicanKeystoneListenerStatus defines the observed state of BarbicanKeystoneListener diff --git a/api/v1beta1/barbicanworker_types.go b/api/v1beta1/barbicanworker_types.go index aa88eae..d31d8a3 100644 --- a/api/v1beta1/barbicanworker_types.go +++ b/api/v1beta1/barbicanworker_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" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -37,6 +38,11 @@ type BarbicanWorkerSpec struct { DatabaseHostname string `json:"databaseHostname"` TransportURLSecret string `json:"transportURLSecret,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.Ca `json:"tls,omitempty"` } // BarbicanWorkerStatus defines the observed state of BarbicanWorker diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index a633e6f..4d6f2de 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -208,6 +208,7 @@ func (in *BarbicanAPITemplate) DeepCopyInto(out *BarbicanAPITemplate) { *out = *in in.BarbicanComponentTemplate.DeepCopyInto(&out.BarbicanComponentTemplate) in.Override.DeepCopyInto(&out.Override) + in.TLS.DeepCopyInto(&out.TLS) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanAPITemplate. @@ -344,6 +345,7 @@ func (in *BarbicanKeystoneListenerSpec) DeepCopyInto(out *BarbicanKeystoneListen *out = *in out.BarbicanTemplate = in.BarbicanTemplate in.BarbicanKeystoneListenerTemplate.DeepCopyInto(&out.BarbicanKeystoneListenerTemplate) + out.TLS = in.TLS } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanKeystoneListenerSpec. @@ -590,6 +592,7 @@ func (in *BarbicanWorkerSpec) DeepCopyInto(out *BarbicanWorkerSpec) { *out = *in out.BarbicanTemplate = in.BarbicanTemplate in.BarbicanWorkerTemplate.DeepCopyInto(&out.BarbicanWorkerTemplate) + out.TLS = in.TLS } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanWorkerSpec. diff --git a/config/crd/bases/barbican.openstack.org_barbicanapis.yaml b/config/crd/bases/barbican.openstack.org_barbicanapis.yaml index f52bb38..58018c2 100644 --- a/config/crd/bases/barbican.openstack.org_barbicanapis.yaml +++ b/config/crd/bases/barbican.openstack.org_barbicanapis.yaml @@ -361,6 +361,36 @@ spec: simpleCryptoBackendKEKSecret: description: Secret containing SimpleCrypto KEK 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/barbican.openstack.org_barbicankeystonelisteners.yaml b/config/crd/bases/barbican.openstack.org_barbicankeystonelisteners.yaml index ddb1d63..2fa8aaa 100644 --- a/config/crd/bases/barbican.openstack.org_barbicankeystonelisteners.yaml +++ b/config/crd/bases/barbican.openstack.org_barbicankeystonelisteners.yaml @@ -183,6 +183,14 @@ spec: simpleCryptoBackendKEKSecret: description: Secret containing SimpleCrypto KEK 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: type: string required: diff --git a/config/crd/bases/barbican.openstack.org_barbicans.yaml b/config/crd/bases/barbican.openstack.org_barbicans.yaml index d9e0ee9..e52b1b4 100644 --- a/config/crd/bases/barbican.openstack.org_barbicans.yaml +++ b/config/crd/bases/barbican.openstack.org_barbicans.yaml @@ -323,6 +323,36 @@ spec: to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object + 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 required: - containerImage type: object diff --git a/config/crd/bases/barbican.openstack.org_barbicanworkers.yaml b/config/crd/bases/barbican.openstack.org_barbicanworkers.yaml index aab3e31..21d6b57 100644 --- a/config/crd/bases/barbican.openstack.org_barbicanworkers.yaml +++ b/config/crd/bases/barbican.openstack.org_barbicanworkers.yaml @@ -181,6 +181,14 @@ spec: simpleCryptoBackendKEKSecret: description: Secret containing SimpleCrypto KEK 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: type: string required: diff --git a/config/manifests/bases/barbican-operator.clusterserviceversion.yaml b/config/manifests/bases/barbican-operator.clusterserviceversion.yaml index 0abc65f..27d60b6 100644 --- a/config/manifests/bases/barbican-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/barbican-operator.clusterserviceversion.yaml @@ -18,16 +18,28 @@ spec: displayName: Barbican API kind: BarbicanAPI name: barbicanapis.barbican.openstack.org + specDescriptors: + - description: TLS - Parameters related to the TLS + displayName: TLS + path: tls version: v1beta1 - description: Barbican is the Schema for the barbicans API displayName: Barbican kind: Barbican name: barbicans.barbican.openstack.org + specDescriptors: + - description: TLS - Parameters related to the TLS + displayName: TLS + path: barbicanAPI.tls version: v1beta1 - description: BarbicanWorker is the Schema for the barbicanworkers API displayName: Barbican Worker kind: BarbicanWorker name: barbicanworkers.barbican.openstack.org + specDescriptors: + - description: TLS - Parameters related to the TLS + displayName: TLS + path: tls version: v1beta1 description: Barbican Operator displayName: Barbican Operator diff --git a/controllers/barbican_controller.go b/controllers/barbican_controller.go index a51a9ca..8bdeca8 100644 --- a/controllers/barbican_controller.go +++ b/controllers/barbican_controller.go @@ -478,6 +478,27 @@ func (r *BarbicanReconciler) reconcileDelete(ctx context.Context, instance *barb return ctrl.Result{}, nil } +// 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 ( + commonWatchFields = []string{ + passwordSecretField, + caBundleSecretNameField, + } + apinWatchFields = []string{ + passwordSecretField, + caBundleSecretNameField, + tlsAPIInternalField, + tlsAPIPublicField, + } +) + // SetupWithManager sets up the controller with the Manager. func (r *BarbicanReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). @@ -624,6 +645,7 @@ func (r *BarbicanReconciler) workerDeploymentCreateOrUpdate(ctx context.Context, BarbicanWorkerTemplate: instance.Spec.BarbicanWorker, DatabaseHostname: instance.Status.DatabaseHostname, TransportURLSecret: instance.Status.TransportURLSecret, + TLS: instance.Spec.BarbicanAPI.TLS.Ca, } deployment := &barbicanv1beta1.BarbicanWorker{ @@ -652,7 +674,6 @@ func (r *BarbicanReconciler) workerDeploymentCreateOrUpdate(ctx context.Context, } func (r *BarbicanReconciler) keystoneListenerDeploymentCreateOrUpdate(ctx context.Context, instance *barbicanv1beta1.Barbican, helper *helper.Helper) (*barbicanv1beta1.BarbicanKeystoneListener, controllerutil.OperationResult, error) { - Log := r.GetLogger(ctx) Log.Info(fmt.Sprintf("Creating barbican KeystoneListener spec. transporturlsecret: '%s'", instance.Status.TransportURLSecret)) Log.Info(fmt.Sprintf("database hostname: '%s'", instance.Status.DatabaseHostname)) @@ -661,6 +682,7 @@ func (r *BarbicanReconciler) keystoneListenerDeploymentCreateOrUpdate(ctx contex BarbicanKeystoneListenerTemplate: instance.Spec.BarbicanKeystoneListener, DatabaseHostname: instance.Status.DatabaseHostname, TransportURLSecret: instance.Status.TransportURLSecret, + TLS: instance.Spec.BarbicanAPI.TLS.Ca, } deployment := &barbicanv1beta1.BarbicanKeystoneListener{ diff --git a/controllers/barbicanapi_controller.go b/controllers/barbicanapi_controller.go index 37f27f2..e0889ba 100644 --- a/controllers/barbicanapi_controller.go +++ b/controllers/barbicanapi_controller.go @@ -36,13 +36,22 @@ import ( 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/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" + "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" + "sigs.k8s.io/controller-runtime/pkg/source" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -142,6 +151,7 @@ func (r *BarbicanAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request) // right now we have no dedicated KeystoneServiceReadyInitMessage and KeystoneEndpointReadyInitMessage condition.UnknownCondition(condition.KeystoneEndpointReadyCondition, condition.InitReason, ""), condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage), + condition.UnknownCondition(condition.TLSInputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), ) Log.Info(fmt.Sprintf("calling init %s", instance.Name)) instance.Status.Conditions.Init(&cl) @@ -289,6 +299,21 @@ func (r *BarbicanAPIReconciler) generateServiceConfigs( "EnableSecureRBAC": instance.Spec.EnableSecureRBAC, } + // 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", barbican.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 + return GenerateConfigsGeneric(ctx, h, instance, envVars, templateParameters, customData, labels, false) } @@ -400,7 +425,12 @@ func (r *BarbicanAPIReconciler) 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 { @@ -538,26 +568,60 @@ func (r *BarbicanAPIReconciler) reconcileNormal(ctx context.Context, instance *b // This seems like a great place to store things like HSM passwords Log.Info(fmt.Sprintf("[API] Got secrets '%s'", instance.Name)) + // - // create custom config for this barbican service + // TLS input validation // - err = r.generateServiceConfigs(ctx, helper, instance, &configVars) + // 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 != "" { + configVars[tls.CABundleKey] = env.SetValue(hash) + } + } + + // TODO(alee) should this validation occur in an if statement? what happens when tls is not enabled? + // 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.ServiceConfigReadyCondition, + condition.TLSInputReadyCondition, condition.ErrorReason, condition.SeverityWarning, - condition.ServiceConfigReadyErrorMessage, + condition.TLSInputErrorMessage, err.Error())) - return ctrl.Result{}, err + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil } + configVars[tls.TLSHashName] = env.SetValue(certsHash) + + // all cert input checks out so report InputReady + instance.Status.Conditions.MarkTrue(condition.TLSInputReadyCondition, condition.InputReadyMessage) - Log.Info(fmt.Sprintf("[API] Getting input hash '%s'", instance.Name)) // - // create hash over all the different input resources to identify if any those changed - // and a restart/recreate is required. + // create custom config for this barbican service // - inputHash, hashChanged, err := r.createHashOfInputHashes(ctx, instance, configVars) + err = r.generateServiceConfigs(ctx, helper, instance, &configVars) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.ServiceConfigReadyCondition, @@ -566,13 +630,11 @@ func (r *BarbicanAPIReconciler) reconcileNormal(ctx context.Context, instance *b condition.ServiceConfigReadyErrorMessage, err.Error())) return ctrl.Result{}, err - } else if hashChanged { - // Hash changed and instance status should be updated (which will be done by main defer func), - // so we need to return and reconcile again - return ctrl.Result{}, nil } + instance.Status.Conditions.MarkTrue(condition.ServiceConfigReadyCondition, condition.ServiceConfigReadyMessage) + // TODO(alee) Figure out how serviceLabels are used and what must be in them Log.Info(fmt.Sprintf("[API] Getting service labels '%s'", instance.Name)) serviceLabels := map[string]string{ common.AppSelector: fmt.Sprintf(barbican.ServiceName), @@ -634,9 +696,38 @@ func (r *BarbicanAPIReconciler) reconcileNormal(ctx context.Context, instance *b return ctrlResult, nil } + Log.Info(fmt.Sprintf("[API] Getting input hash '%s'", instance.Name)) + // + // create hash over all the different input resources to identify if any those changed + // and a restart/recreate is required. + // + inputHash, hashChanged, err := r.createHashOfInputHashes(ctx, instance, configVars) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.ServiceConfigReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.ServiceConfigReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } else if hashChanged { + // Hash changed and instance status should be updated (which will be done by main defer func), + // so we need to return and reconcile again + return ctrl.Result{}, nil + } + Log.Info(fmt.Sprintf("[API] Defining deployment '%s'", instance.Name)) // Define a new Deployment object - deplDef := barbicanapi.Deployment(instance, inputHash, serviceLabels, serviceAnnotations) + deplDef, err := barbicanapi.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 + } Log.Info(fmt.Sprintf("[API] Getting deployment '%s'", instance.Name)) depl := deployment.NewDeployment( deplDef, @@ -694,11 +785,96 @@ func (r *BarbicanAPIReconciler) reconcileNormal(ctx context.Context, instance *b // SetupWithManager sets up the controller with the Manager. func (r *BarbicanAPIReconciler) SetupWithManager(mgr ctrl.Manager) error { + // index passwordSecretField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &barbicanv1beta1.BarbicanAPI{}, passwordSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*barbicanv1beta1.BarbicanAPI) + if cr.Spec.Secret == "" { + return nil + } + return []string{cr.Spec.Secret} + }); err != nil { + return err + } + + // index caBundleSecretNameField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &barbicanv1beta1.BarbicanAPI{}, caBundleSecretNameField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*barbicanv1beta1.BarbicanAPI) + 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(), &barbicanv1beta1.BarbicanAPI{}, tlsAPIInternalField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*barbicanv1beta1.BarbicanAPI) + 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(), &barbicanv1beta1.BarbicanAPI{}, tlsAPIPublicField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*barbicanv1beta1.BarbicanAPI) + 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(&barbicanv1beta1.BarbicanAPI{}). Owns(&corev1.Service{}). Owns(&corev1.Secret{}). Owns(&keystonev1.KeystoneEndpoint{}). Owns(&appsv1.Deployment{}). + Watches( + &source.Kind{Type: &corev1.Secret{}}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). Complete(r) } + +func (r *BarbicanAPIReconciler) findObjectsForSrc(src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(context.Background()).WithName("Controllers").WithName("BarbicanAPI") + + for _, field := range apinWatchFields { + crList := &barbicanv1beta1.BarbicanAPIList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(field, src.GetName()), + Namespace: src.GetNamespace(), + } + err := r.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/barbicankeystonelistener_controller.go b/controllers/barbicankeystonelistener_controller.go index 12d9a31..400dd51 100644 --- a/controllers/barbicankeystonelistener_controller.go +++ b/controllers/barbicankeystonelistener_controller.go @@ -31,6 +31,7 @@ import ( "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/deployment" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" //"github.com/openstack-k8s-operators/lib-common/modules/common/endpoint" "github.com/openstack-k8s-operators/lib-common/modules/common/env" @@ -39,12 +40,19 @@ import ( 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/util" + "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" + "sigs.k8s.io/controller-runtime/pkg/source" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -123,21 +131,14 @@ func (r *BarbicanKeystoneListenerReconciler) Reconcile(ctx context.Context, req if instance.Status.Conditions == nil { instance.Status.Conditions = condition.Conditions{} cl := condition.CreateList( - //condition.UnknownCondition(condition.ExposeServiceReadyCondition, condition.InitReason, condition.ExposeServiceReadyInitMessage), condition.UnknownCondition(condition.InputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyInitMessage), condition.UnknownCondition(condition.DeploymentReadyCondition, condition.InitReason, condition.DeploymentReadyInitMessage), - // right now we have no dedicated KeystoneServiceReadyInitMessage and KeystoneEndpointReadyInitMessage - //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), ) - Log.Info(fmt.Sprintf("calling init %s", instance.Name)) instance.Status.Conditions.Init(&cl) - Log.Info(fmt.Sprintf("post init %s", instance.Name)) - // TODO: (alee) this is ssupposed to exit here - but then it never comes back! - // Register overall status immediately to have an early feedback e.g. in the cli return ctrl.Result{}, nil } Log.Info(fmt.Sprintf("post initiialize %s", instance.Name)) @@ -471,6 +472,40 @@ func (r *BarbicanKeystoneListenerReconciler) reconcileNormal(ctx context.Context // This seems like a great place to store things like HSM passwords Log.Info(fmt.Sprintf("[KeystoneListener] Got secrets '%s'", instance.Name)) + + // + // 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 != "" { + configVars[tls.CABundleKey] = env.SetValue(hash) + } + } + + // all cert input checks out so report InputReady + instance.Status.Conditions.MarkTrue(condition.TLSInputReadyCondition, condition.InputReadyMessage) + // // create custom config for this barbican service // @@ -631,11 +666,73 @@ func (r *BarbicanKeystoneListenerReconciler) reconcileNormal(ctx context.Context // SetupWithManager sets up the controller with the Manager. func (r *BarbicanKeystoneListenerReconciler) SetupWithManager(mgr ctrl.Manager) error { + // index passwordSecretField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &barbicanv1beta1.BarbicanKeystoneListener{}, passwordSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*barbicanv1beta1.BarbicanKeystoneListener) + if cr.Spec.Secret == "" { + return nil + } + return []string{cr.Spec.Secret} + }); err != nil { + return err + } + + // index caBundleSecretNameField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &barbicanv1beta1.BarbicanKeystoneListener{}, caBundleSecretNameField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*barbicanv1beta1.BarbicanKeystoneListener) + if cr.Spec.TLS.CaBundleSecretName == "" { + return nil + } + return []string{cr.Spec.TLS.CaBundleSecretName} + }); err != nil { + return err + } + return ctrl.NewControllerManagedBy(mgr). For(&barbicanv1beta1.BarbicanKeystoneListener{}). //Owns(&corev1.Service{}). //Owns(&corev1.Secret{}). Owns(&appsv1.Deployment{}). //Owns(&routev1.Route{}). + Watches( + &source.Kind{Type: &corev1.Secret{}}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). Complete(r) } + +func (r *BarbicanKeystoneListenerReconciler) findObjectsForSrc(src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(context.Background()).WithName("Controllers").WithName("BarbicanKeystoneListener") + + for _, field := range commonWatchFields { + crList := &barbicanv1beta1.BarbicanKeystoneListenerList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(field, src.GetName()), + Namespace: src.GetNamespace(), + } + err := r.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/barbicanworker_controller.go b/controllers/barbicanworker_controller.go index b857d30..3683f7c 100644 --- a/controllers/barbicanworker_controller.go +++ b/controllers/barbicanworker_controller.go @@ -29,6 +29,7 @@ import ( "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/deployment" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/env" "github.com/openstack-k8s-operators/lib-common/modules/common/helper" @@ -36,12 +37,19 @@ import ( 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/util" + "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" + "sigs.k8s.io/controller-runtime/pkg/source" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -124,6 +132,7 @@ func (r *BarbicanWorkerReconciler) Reconcile(ctx context.Context, req ctrl.Reque condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyInitMessage), condition.UnknownCondition(condition.DeploymentReadyCondition, condition.InitReason, condition.DeploymentReadyInitMessage), condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage), + condition.UnknownCondition(condition.TLSInputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), ) Log.Info(fmt.Sprintf("calling init %s", instance.Name)) instance.Status.Conditions.Init(&cl) @@ -331,6 +340,40 @@ func (r *BarbicanWorkerReconciler) reconcileNormal(ctx context.Context, instance // This seems like a great place to store things like HSM passwords Log.Info(fmt.Sprintf("[Worker] Got secrets '%s'", instance.Name)) + + // + // 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 != "" { + configVars[tls.CABundleKey] = env.SetValue(hash) + } + } + + // all cert input checks out so report InputReady + instance.Status.Conditions.MarkTrue(condition.TLSInputReadyCondition, condition.InputReadyMessage) + // // create custom config for this barbican service // @@ -491,11 +534,72 @@ func (r *BarbicanWorkerReconciler) reconcileNormal(ctx context.Context, instance // SetupWithManager sets up the controller with the Manager. func (r *BarbicanWorkerReconciler) SetupWithManager(mgr ctrl.Manager) error { + // index passwordSecretField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &barbicanv1beta1.BarbicanWorker{}, passwordSecretField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*barbicanv1beta1.BarbicanWorker) + if cr.Spec.Secret == "" { + return nil + } + return []string{cr.Spec.Secret} + }); err != nil { + return err + } + + // index caBundleSecretNameField + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &barbicanv1beta1.BarbicanWorker{}, caBundleSecretNameField, func(rawObj client.Object) []string { + // Extract the secret name from the spec, if one is provided + cr := rawObj.(*barbicanv1beta1.BarbicanWorker) + if cr.Spec.TLS.CaBundleSecretName == "" { + return nil + } + return []string{cr.Spec.TLS.CaBundleSecretName} + }); err != nil { + return err + } return ctrl.NewControllerManagedBy(mgr). For(&barbicanv1beta1.BarbicanWorker{}). //Owns(&corev1.Service{}). //Owns(&corev1.Secret{}). Owns(&appsv1.Deployment{}). //Owns(&routev1.Route{}). + Watches( + &source.Kind{Type: &corev1.Secret{}}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). Complete(r) } + +func (r *BarbicanWorkerReconciler) findObjectsForSrc(src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(context.Background()).WithName("Controllers").WithName("BarbicanWorker") + + for _, field := range commonWatchFields { + crList := &barbicanv1beta1.BarbicanWorkerList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(field, src.GetName()), + Namespace: src.GetNamespace(), + } + err := r.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/go.mod b/go.mod index 08d0a96..be1a969 100644 --- a/go.mod +++ b/go.mod @@ -16,9 +16,11 @@ require ( github.com/openstack-k8s-operators/lib-common/modules/test v0.3.1-0.20240117103205-2bd91a3da216 github.com/openstack-k8s-operators/mariadb-operator/api v0.3.1-0.20240124160436-36095347284f go.uber.org/zap v1.26.0 + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 k8s.io/api v0.26.13 k8s.io/apimachinery v0.27.1 k8s.io/client-go v0.26.13 + k8s.io/utils v0.0.0-20240102154912-e7106e64919e sigs.k8s.io/controller-runtime v0.14.7 ) @@ -59,7 +61,6 @@ require ( github.com/prometheus/procfs v0.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.20.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect @@ -78,7 +79,6 @@ require ( k8s.io/component-base v0.26.13 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a // 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.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/pkg/barbican/dbsync.go b/pkg/barbican/dbsync.go index c22c1c1..c8e5692 100644 --- a/pkg/barbican/dbsync.go +++ b/pkg/barbican/dbsync.go @@ -16,7 +16,6 @@ const ( // DbSyncJob func func DbSyncJob(instance *barbicanv1beta1.Barbican, labels map[string]string, annotations map[string]string) *batchv1.Job { - secretNames := []string{} var config0644AccessMode int32 = 0644 @@ -25,20 +24,22 @@ func DbSyncJob(instance *barbicanv1beta1.Barbican, labels map[string]string, ann // service, The two snippet files that it does need (DefaultsConfigFileName // and CustomConfigFileName) can be extracted from the top-level barbican // config-data secret. - dbSyncVolume := corev1.Volume{ - Name: "db-sync-config-data", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - DefaultMode: &config0644AccessMode, - SecretName: instance.Name + "-config-data", - Items: []corev1.KeyToPath{ - { - Key: DefaultsConfigFileName, - Path: DefaultsConfigFileName, - }, - { - Key: CustomConfigFileName, - Path: CustomConfigFileName, + dbSyncVolume := []corev1.Volume{ + { + Name: "db-sync-config-data", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + DefaultMode: &config0644AccessMode, + SecretName: instance.Name + "-config-data", + Items: []corev1.KeyToPath{ + { + Key: DefaultsConfigFileName, + Path: DefaultsConfigFileName, + }, + { + Key: CustomConfigFileName, + Path: CustomConfigFileName, + }, }, }, }, @@ -58,6 +59,13 @@ func DbSyncJob(instance *barbicanv1beta1.Barbican, labels map[string]string, ann ReadOnly: true, }, } + + // add CA cert if defined + if instance.Spec.BarbicanAPI.TLS.CaBundleSecretName != "" { + dbSyncVolume = append(dbSyncVolume, instance.Spec.BarbicanAPI.TLS.CreateVolume()) + dbSyncMounts = append(dbSyncMounts, instance.Spec.BarbicanAPI.TLS.CreateVolumeMounts(nil)...) + } + args := []string{"-c", DBSyncCommand} runAsUser := int64(0) @@ -105,7 +113,7 @@ func DbSyncJob(instance *barbicanv1beta1.Barbican, labels map[string]string, ann ServiceName, secretNames, DbsyncPropagation), - dbSyncVolume, + dbSyncVolume..., ) return job diff --git a/pkg/barbicanapi/deployment.go b/pkg/barbicanapi/deployment.go index f1d942e..828a8e6 100644 --- a/pkg/barbicanapi/deployment.go +++ b/pkg/barbicanapi/deployment.go @@ -4,6 +4,8 @@ import ( "fmt" "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" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -24,7 +26,7 @@ func Deployment( configHash string, labels map[string]string, annotations map[string]string, -) *appsv1.Deployment { +) (*appsv1.Deployment, error) { runAsUser := int64(0) var config0644AccessMode int32 = 0644 envVars := map[string]env.Setter{} @@ -52,6 +54,12 @@ func Deployment( } readinessProbe.HTTPGet = livenessProbe.HTTPGet + if instance.Spec.TLS.API.Enabled(service.EndpointPublic) { + livenessProbe.HTTPGet.Scheme = corev1.URISchemeHTTPS + readinessProbe.HTTPGet.Scheme = corev1.URISchemeHTTPS + } + readinessProbe.HTTPGet = livenessProbe.HTTPGet + apiVolumes := []corev1.Volume{ { Name: "config-data-custom", @@ -77,6 +85,31 @@ func Deployment( // logging apiVolumeMounts = append(apiVolumeMounts, barbican.GetLogVolumeMount()...) + // add CA cert if defined + if instance.Spec.TLS.CaBundleSecretName != "" { + apiVolumes = append(apiVolumes, instance.Spec.TLS.CreateVolume()) + apiVolumeMounts = append(apiVolumeMounts, 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 + } + apiVolumes = append(apiVolumes, svc.CreateVolume(endpt.String())) + apiVolumeMounts = append(apiVolumeMounts, svc.CreateVolumeMounts(endpt.String())...) + } + } + deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-api", instance.Name), @@ -151,5 +184,5 @@ func Deployment( barbican.BarbicanAPIPropagation), apiVolumes...) - return deployment + return deployment, nil } diff --git a/pkg/barbicankeystonelistener/deployment.go b/pkg/barbicankeystonelistener/deployment.go index 8d96c78..ecdb8c8 100644 --- a/pkg/barbicankeystonelistener/deployment.go +++ b/pkg/barbicankeystonelistener/deployment.go @@ -56,6 +56,12 @@ func Deployment( // logging keystoneListenerVolumeMounts = append(keystoneListenerVolumeMounts, barbican.GetLogVolumeMount()...) + // Add the CA bundle + if instance.Spec.TLS.CaBundleSecretName != "" { + keystoneListenerVolumes = append(keystoneListenerVolumes, instance.Spec.TLS.CreateVolume()) + keystoneListenerVolumeMounts = append(keystoneListenerVolumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...) + } + deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-keystone-listener", instance.Name), diff --git a/pkg/barbicanworker/deployment.go b/pkg/barbicanworker/deployment.go index 995b546..0f1e4a6 100644 --- a/pkg/barbicanworker/deployment.go +++ b/pkg/barbicanworker/deployment.go @@ -80,6 +80,12 @@ func Deployment( // logging workerVolumeMounts = append(workerVolumeMounts, barbican.GetLogVolumeMount()...) + // Add the CA bundle + if instance.Spec.TLS.CaBundleSecretName != "" { + workerVolumes = append(workerVolumes, instance.Spec.TLS.CreateVolume()) + workerVolumeMounts = append(workerVolumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...) + } + deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-worker", instance.Name), diff --git a/templates/barbican/config/10-barbican_wsgi_main.conf b/templates/barbican/config/10-barbican_wsgi_main.conf index adff73d..cfdc646 100644 --- a/templates/barbican/config/10-barbican_wsgi_main.conf +++ b/templates/barbican/config/10-barbican_wsgi_main.conf @@ -1,5 +1,8 @@ +{{ if (index . "VHosts") }} +{{ range $endpt, $vhost := .VHosts }} +# {{ $endpt }} vhost {{ $vhost.ServerName }} configuration - ServerName barbicanapi.openstack.svc + ServerName {{ $vhost.ServerName }} ## Vhost docroot DocumentRoot "/var/www/cgi-bin/barbican" @@ -16,11 +19,21 @@ ErrorLog "/var/log/barbican/error.log" ServerSignature Off CustomLog "/var/log/barbican/access.log" combined 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 WSGIApplicationGroup %{GLOBAL} - WSGIDaemonProcess barbican-api display-name=barbican_wsgi_main group=barbican processes=8 threads=1 user=barbican - WSGIProcessGroup barbican-api + WSGIDaemonProcess {{ $endpt }} display-name={{ $endpt }} group=barbican processes=8 threads=1 user=barbican + WSGIProcessGroup {{ $endpt }} WSGIScriptAlias / "/var/www/cgi-bin/barbican/main" +{{ end }} +{{ end }} diff --git a/templates/barbican/config/barbican-api-config.json b/templates/barbican/config/barbican-api-config.json index 415bf86..f545011 100644 --- a/templates/barbican/config/barbican-api-config.json +++ b/templates/barbican/config/barbican-api-config.json @@ -1,67 +1,89 @@ { - "command": "/usr/sbin/httpd -DFOREGROUND", - "config_files": [ - { - "source": "/var/lib/config-data/default/00-default.conf", - "dest": "/etc/barbican/barbican.conf.d/00-default.conf", - "owner": "barbican", - "perm": "0600" - }, - { - "source": "/var/lib/config-data/default/02-service.conf", - "dest": "/etc/barbican/barbican.conf.d/02-service.conf", - "owner": "barbican", - "perm": "0600", - "optional": true - }, - { - "source": "/var/lib/config-data/default/03-secrets.conf", - "dest": "/etc/barbican/barbican.conf.d/03-secrets.conf", - "owner": "barbican", - "perm": "0640", - "optional": true - }, - { - "source": "/var/lib/config-data/default/10-barbican_wsgi_main.conf", - "dest": "/etc/httpd/conf.d/10-barbican_wsgi_main.conf", - "owner": "root", - "perm": "0640", - "optional": true - }, - { - "source": "/var/lib/config-data/default/httpd.conf", - "dest": "/etc/httpd/conf/httpd.conf", - "owner": "root", - "perm": "0640", - "optional": true - }, - { - "source": "/var/lib/config-data/default/main", - "dest": "/var/www/cgi-bin/barbican/main", - "owner": "barbican", - "perm": "0640", - "optional": true - }, - { - "source": "/var/lib/config-data/default/mime.conf", - "dest": "/etc/httpd/conf.modules.d/mime.conf", - "owner": "root", - "perm": "0640", - "optional": true - }, - { - "source": "/var/lib/config-data/default/kolla_extend_start", - "dest": "/usr/local/bin/kolla_extend_start", - "owner": "root", - "perm": "0755", - "optional": true - } - ], - "permissions": [ - { - "path": "/var/log/barbican", - "owner": "barbican:barbican", - "recurse": true - } - ] + "command": "/usr/sbin/httpd -DFOREGROUND", + "config_files": [ + { + "source": "/var/lib/config-data/default/00-default.conf", + "dest": "/etc/barbican/barbican.conf.d/00-default.conf", + "owner": "barbican", + "perm": "0600" + }, + { + "source": "/var/lib/config-data/default/02-service.conf", + "dest": "/etc/barbican/barbican.conf.d/02-service.conf", + "owner": "barbican", + "perm": "0600", + "optional": true + }, + { + "source": "/var/lib/config-data/default/03-secrets.conf", + "dest": "/etc/barbican/barbican.conf.d/03-secrets.conf", + "owner": "barbican", + "perm": "0640", + "optional": true + }, + { + "source": "/var/lib/config-data/default/10-barbican_wsgi_main.conf", + "dest": "/etc/httpd/conf.d/10-barbican_wsgi_main.conf", + "owner": "root", + "perm": "0640", + "optional": true + }, + { + "source": "/var/lib/config-data/default/httpd.conf", + "dest": "/etc/httpd/conf/httpd.conf", + "owner": "root", + "perm": "0640", + "optional": true + }, + { + "source": "/var/lib/config-data/default/main", + "dest": "/var/www/cgi-bin/barbican/main", + "owner": "barbican", + "perm": "0640", + "optional": true + }, + { + "source": "/var/lib/config-data/default/mime.conf", + "dest": "/etc/httpd/conf.modules.d/mime.conf", + "owner": "root", + "perm": "0640", + "optional": true + }, + { + "source": "/var/lib/config-data/default/kolla_extend_start", + "dest": "/usr/local/bin/kolla_extend_start", + "owner": "root", + "perm": "0755", + "optional": true + }, + { + "source": "/var/lib/config-data/default/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": "root", + "perm": "0640", + "optional": true, + "merge": true + }, + { + "source": "/var/lib/config-data/tls/private/*", + "dest": "/etc/pki/tls/private/", + "owner": "root", + "perm": "0600", + "optional": true, + "merge": true + } + ], + "permissions": [ + { + "path": "/var/log/barbican", + "owner": "barbican:barbican", + "recurse": true + } + ] } diff --git a/templates/barbican/config/ssl.conf b/templates/barbican/config/ssl.conf new file mode 100644 index 0000000..e3da4ec --- /dev/null +++ b/templates/barbican/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/functional/barbican_controller_test.go b/tests/functional/barbican_controller_test.go index 62518a3..cb7aec1 100644 --- a/tests/functional/barbican_controller_test.go +++ b/tests/functional/barbican_controller_test.go @@ -1,62 +1,30 @@ -package functional_test +package functional import ( - "os" - . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + . "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers" + barbicanv1beta1 "github.com/openstack-k8s-operators/barbican-operator/api/v1beta1" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" - . "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" ) var _ = Describe("Barbican controller", func() { - - var barbicanName types.NamespacedName - var barbicanTransportURL types.NamespacedName - var dbSyncJobName types.NamespacedName - var barbicanConfigMapData types.NamespacedName - - BeforeEach(func() { - - barbicanName = types.NamespacedName{ - Name: "barbican", - Namespace: namespace, - } - barbicanTransportURL = types.NamespacedName{ - Name: "barbican-barbican-transport", - Namespace: namespace, - } - dbSyncJobName = types.NamespacedName{ - Name: "barbican-db-sync", - Namespace: namespace, - } - barbicanConfigMapData = types.NamespacedName{ - Name: "barbican-config-data", - Namespace: namespace, - } - - err := os.Setenv("OPERATOR_TEMPLATES", "../../templates") - Expect(err).NotTo(HaveOccurred()) - - }) - When("A Barbican instance is created", func() { BeforeEach(func() { - DeferCleanup(th.DeleteInstance, CreateBarbican(barbicanName, GetDefaultBarbicanSpec())) + DeferCleanup(th.DeleteInstance, CreateBarbican(barbicanTest.Instance, GetDefaultBarbicanSpec())) }) It("should have the Spec fields defaulted", func() { - Barbican := GetBarbican(barbicanName) + Barbican := GetBarbican(barbicanTest.Instance) Expect(Barbican.Spec.ServiceUser).Should(Equal("barbican")) Expect(Barbican.Spec.DatabaseInstance).Should(Equal("openstack")) Expect(Barbican.Spec.DatabaseUser).Should(Equal("barbican")) }) It("should have the Status fields initialized", func() { - Barbican := GetBarbican(barbicanName) + Barbican := GetBarbican(barbicanTest.Instance) Expect(Barbican.Status.Hash).To(BeEmpty()) Expect(Barbican.Status.BarbicanAPIReadyCount).To(Equal(int32(0))) Expect(Barbican.Status.BarbicanWorkerReadyCount).To(Equal(int32(0))) @@ -67,14 +35,14 @@ var _ = Describe("Barbican controller", func() { It("should have input not ready and unknown Conditions initialized", func() { th.ExpectCondition( - barbicanName, + barbicanTest.Instance, ConditionGetterFunc(BarbicanConditionGetter), condition.ReadyCondition, corev1.ConditionFalse, ) th.ExpectCondition( - barbicanName, + barbicanTest.Instance, ConditionGetterFunc(BarbicanConditionGetter), condition.InputReadyCondition, corev1.ConditionUnknown, @@ -87,7 +55,7 @@ var _ = Describe("Barbican controller", func() { condition.NetworkAttachmentsReadyCondition, } { th.ExpectCondition( - barbicanName, + barbicanTest.Instance, ConditionGetterFunc(BarbicanConditionGetter), cond, corev1.ConditionUnknown, @@ -98,126 +66,179 @@ var _ = Describe("Barbican controller", func() { // the reconciler loop adds the finalizer so we have to wait for // it to run Eventually(func() []string { - return GetBarbican(barbicanName).Finalizers + return GetBarbican(barbicanTest.Instance).Finalizers }, timeout, interval).Should(ContainElement("Barbican")) }) It("should not create a config map", func() { Eventually(func() []corev1.ConfigMap { - return th.ListConfigMaps(barbicanConfigMapData.Name).Items + return th.ListConfigMaps(barbicanTest.BarbicanConfigMapData.Name).Items }, timeout, interval).Should(BeEmpty()) }) }) When("Barbican DB is created", func() { BeforeEach(func() { - DeferCleanup(k8sClient.Delete, ctx, CreateBarbicanMessageBusSecret(barbicanName.Namespace, "rabbitmq-secret")) - DeferCleanup(th.DeleteInstance, CreateBarbican(barbicanName, GetDefaultBarbicanSpec())) - DeferCleanup(k8sClient.Delete, ctx, CreateKeystoneAPISecret(namespace, SecretName)) + DeferCleanup(k8sClient.Delete, ctx, CreateBarbicanMessageBusSecret(barbicanTest.Instance.Namespace, "rabbitmq-secret")) + DeferCleanup(th.DeleteInstance, CreateBarbican(barbicanTest.Instance, GetDefaultBarbicanSpec())) + DeferCleanup(k8sClient.Delete, ctx, CreateKeystoneAPISecret(barbicanTest.Instance.Namespace, SecretName)) DeferCleanup( mariadb.DeleteDBService, mariadb.CreateDBService( - namespace, - GetBarbican(barbicanName).Spec.DatabaseInstance, + barbicanTest.Instance.Namespace, + GetBarbican(barbicanTest.Instance).Spec.DatabaseInstance, corev1.ServiceSpec{ Ports: []corev1.ServicePort{{Port: 3306}}, }, ), ) - infra.SimulateTransportURLReady(barbicanTransportURL) - DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(barbicanName.Namespace)) - //DeferCleanup(infra.DeleteMemcached, infra.CreateMemcached(namespace, "memcached", memcachedSpec)) + infra.SimulateTransportURLReady(barbicanTest.BarbicanTransportURL) + DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(barbicanTest.Instance.Namespace)) }) It("Should set DBReady Condition and set DatabaseHostname Status when DB is Created", func() { - mariadb.SimulateMariaDBDatabaseCompleted(barbicanName) - th.SimulateJobSuccess(dbSyncJobName) - Barbican := GetBarbican(barbicanName) + mariadb.SimulateMariaDBDatabaseCompleted(barbicanTest.Instance) + th.SimulateJobSuccess(barbicanTest.BarbicanDBSync) + Barbican := GetBarbican(barbicanTest.Instance) Expect(Barbican.Status.DatabaseHostname).To(Equal("hostname-for-openstack")) th.ExpectCondition( - barbicanName, + barbicanTest.Instance, ConditionGetterFunc(BarbicanConditionGetter), - condition.InputReadyCondition, + condition.DBReadyCondition, corev1.ConditionTrue, ) th.ExpectCondition( - barbicanName, + barbicanTest.Instance, ConditionGetterFunc(BarbicanConditionGetter), condition.DBSyncReadyCondition, corev1.ConditionFalse, ) }) It("Should fail if db-sync job fails when DB is Created", func() { - mariadb.SimulateMariaDBDatabaseCompleted(barbicanName) - th.SimulateJobFailure(dbSyncJobName) + mariadb.SimulateMariaDBDatabaseCompleted(barbicanTest.Instance) + th.SimulateJobFailure(barbicanTest.BarbicanDBSync) th.ExpectCondition( - barbicanName, + barbicanTest.Instance, ConditionGetterFunc(BarbicanConditionGetter), condition.DBReadyCondition, corev1.ConditionTrue, ) th.ExpectCondition( - barbicanName, + barbicanTest.Instance, ConditionGetterFunc(BarbicanConditionGetter), condition.DBSyncReadyCondition, corev1.ConditionFalse, ) }) It("Does not create BarbicanAPI", func() { - BarbicanAPINotExists(barbicanName) + BarbicanAPINotExists(barbicanTest.Instance) }) It("Does not create BarbicanWorker", func() { - BarbicanWorkerNotExists(barbicanName) + BarbicanWorkerNotExists(barbicanTest.Instance) }) It("Does not create BarbicanKeystoneListener", func() { - BarbicanKeystoneListenerNotExists(barbicanName) + BarbicanKeystoneListenerNotExists(barbicanTest.Instance) }) }) When("DB sync is completed", func() { BeforeEach(func() { - DeferCleanup(k8sClient.Delete, ctx, CreateBarbicanMessageBusSecret(barbicanName.Namespace, "rabbitmq-secret")) - DeferCleanup(th.DeleteInstance, CreateBarbican(barbicanName, GetDefaultBarbicanSpec())) - DeferCleanup(k8sClient.Delete, ctx, CreateKeystoneAPISecret(namespace, SecretName)) + DeferCleanup(k8sClient.Delete, ctx, CreateBarbicanMessageBusSecret(barbicanTest.Instance.Namespace, "rabbitmq-secret")) + DeferCleanup(th.DeleteInstance, CreateBarbican(barbicanTest.Instance, GetDefaultBarbicanSpec())) + DeferCleanup(k8sClient.Delete, ctx, CreateKeystoneAPISecret(barbicanTest.Instance.Namespace, SecretName)) DeferCleanup( - k8sClient.Delete, ctx, CreateBarbicanSecret(barbicanName.Namespace, "test-osp-secret-berbican")) + k8sClient.Delete, ctx, CreateBarbicanSecret(barbicanTest.Instance.Namespace, "test-osp-secret-barbican")) DeferCleanup( mariadb.DeleteDBService, mariadb.CreateDBService( - namespace, - GetBarbican(barbicanName).Spec.DatabaseInstance, + barbicanTest.Instance.Namespace, + GetBarbican(barbicanTest.Instance).Spec.DatabaseInstance, corev1.ServiceSpec{ Ports: []corev1.ServicePort{{Port: 3306}}, }, ), ) - infra.SimulateTransportURLReady(barbicanTransportURL) - DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(barbicanName.Namespace)) - mariadb.SimulateMariaDBDatabaseCompleted(barbicanName) - th.SimulateJobSuccess(dbSyncJobName) + infra.SimulateTransportURLReady(barbicanTest.BarbicanTransportURL) + DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(barbicanTest.Instance.Namespace)) + mariadb.SimulateMariaDBDatabaseCompleted(barbicanTest.Instance) + th.SimulateJobSuccess(barbicanTest.BarbicanDBSync) }) It("should have db sync ready condition", func() { th.ExpectCondition( - barbicanName, + barbicanTest.Instance, ConditionGetterFunc(BarbicanConditionGetter), condition.ReadyCondition, corev1.ConditionFalse, ) th.ExpectCondition( - barbicanName, + barbicanTest.Instance, ConditionGetterFunc(BarbicanConditionGetter), barbicanv1beta1.BarbicanRabbitMQTransportURLReadyCondition, corev1.ConditionTrue, ) th.ExpectCondition( - barbicanName, + barbicanTest.Instance, ConditionGetterFunc(BarbicanConditionGetter), condition.DBSyncReadyCondition, corev1.ConditionTrue, ) }) }) + When("A Barbican with TLS is created", func() { + BeforeEach(func() { + DeferCleanup(th.DeleteInstance, CreateBarbican(barbicanTest.Instance, GetTLSBarbicanSpec())) + DeferCleanup(k8sClient.Delete, ctx, CreateBarbicanMessageBusSecret(barbicanTest.Instance.Namespace, barbicanTest.RabbitmqSecretName)) + DeferCleanup(th.DeleteInstance, CreateBarbicanAPI(barbicanTest.Instance, GetTLSBarbicanAPISpec())) + DeferCleanup(k8sClient.Delete, ctx, CreateKeystoneAPISecret(barbicanTest.Instance.Namespace, SecretName)) + DeferCleanup( + mariadb.DeleteDBService, + mariadb.CreateDBService( + barbicanTest.Instance.Namespace, + GetBarbican(barbicanTest.Instance).Spec.DatabaseInstance, + corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Port: 3306}}, + }, + ), + ) + infra.SimulateTransportURLReady(barbicanTest.BarbicanTransportURL) + DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(barbicanTest.Instance.Namespace)) + mariadb.SimulateMariaDBDatabaseCompleted(barbicanTest.Instance) + th.SimulateJobSuccess(barbicanTest.BarbicanDBSync) + }) + + It("Creates BarbicanAPI", func() { + DeferCleanup(k8sClient.Delete, ctx, th.CreateCABundleSecret(barbicanTest.CABundleSecret)) + DeferCleanup(k8sClient.Delete, ctx, th.CreateCertSecret(barbicanTest.InternalCertSecret)) + DeferCleanup(k8sClient.Delete, ctx, th.CreateCertSecret(barbicanTest.PublicCertSecret)) + keystone.SimulateKeystoneEndpointReady(barbicanTest.BarbicanKeystoneEndpoint) + + BarbicanAPIExists(barbicanTest.Instance) + + d := th.GetDeployment(barbicanTest.BarbicanAPI) + // Check the resulting deployment fields + Expect(int(*d.Spec.Replicas)).To(Equal(1)) + + Expect(d.Spec.Template.Spec.Volumes).To(HaveLen(6)) + Expect(d.Spec.Template.Spec.Containers).To(HaveLen(2)) + + // cert deployment volumes + th.AssertVolumeExists(barbicanTest.CABundleSecret.Name, d.Spec.Template.Spec.Volumes) + th.AssertVolumeExists(barbicanTest.InternalCertSecret.Name, d.Spec.Template.Spec.Volumes) + th.AssertVolumeExists(barbicanTest.PublicCertSecret.Name, d.Spec.Template.Spec.Volumes) + + // cert volumeMounts + container := d.Spec.Template.Spec.Containers[1] + th.AssertVolumeMountExists(barbicanTest.InternalCertSecret.Name, "tls.key", container.VolumeMounts) + th.AssertVolumeMountExists(barbicanTest.InternalCertSecret.Name, "tls.crt", container.VolumeMounts) + th.AssertVolumeMountExists(barbicanTest.PublicCertSecret.Name, "tls.key", container.VolumeMounts) + th.AssertVolumeMountExists(barbicanTest.PublicCertSecret.Name, "tls.crt", container.VolumeMounts) + th.AssertVolumeMountExists(barbicanTest.CABundleSecret.Name, "tls-ca-bundle.pem", container.VolumeMounts) + + Expect(container.ReadinessProbe.HTTPGet.Scheme).To(Equal(corev1.URISchemeHTTPS)) + Expect(container.LivenessProbe.HTTPGet.Scheme).To(Equal(corev1.URISchemeHTTPS)) + }) + }) }) diff --git a/tests/functional/barbican_test_data.go b/tests/functional/barbican_test_data.go new file mode 100644 index 0000000..6ec70b4 --- /dev/null +++ b/tests/functional/barbican_test_data.go @@ -0,0 +1,167 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package functional implements the envTest coverage for barbican-operator +package functional + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/types" +) + +type APIType string + +const ( + // BarbicanAPIInternal - + BarbicanAPITypeInternal APIType = "internal" + // BarbicanAPITypeExternal + BarbicanAPITypePublic APIType = "public" + // PublicCertSecretName - + PublicCertSecretName = "public-tls-certs" + // InternalCertSecretName - + InternalCertSecretName = "internal-tls-certs" + // CABundleSecretName - + CABundleSecretName = "combined-ca-bundle" +) + +// BarbicanTestData is the data structure used to provide input data to envTest +type BarbicanTestData struct { + BarbicanDatabaseUser string + BarbicanPassword string + BarbicanServiceUser string + ContainerImage string + DatabaseHostname string + DatabaseInstance string + RabbitmqClusterName string + RabbitmqSecretName string + Instance types.NamespacedName + Barbican types.NamespacedName + BarbicanDBSync types.NamespacedName + BarbicanAPI types.NamespacedName + BarbicanRole types.NamespacedName + BarbicanRoleBinding types.NamespacedName + BarbicanTransportURL types.NamespacedName + BarbicanSA types.NamespacedName + BarbicanKeystoneService types.NamespacedName + BarbicanKeystoneEndpoint types.NamespacedName + BarbicanServicePublic types.NamespacedName + BarbicanServiceInternal types.NamespacedName + BarbicanConfigSecret types.NamespacedName + BarbicanConfigScripts types.NamespacedName + BarbicanConfigMapData types.NamespacedName + BarbicanScheduler types.NamespacedName + InternalAPINAD types.NamespacedName + CABundleSecret types.NamespacedName + InternalCertSecret types.NamespacedName + PublicCertSecret types.NamespacedName +} + +// GetBarbicanTestData is a function that initialize the BarbicanTestData +// used in the test +func GetBarbicanTestData(barbicanName types.NamespacedName) BarbicanTestData { + m := barbicanName + return BarbicanTestData{ + Instance: m, + + Barbican: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: barbicanName.Name, + }, + BarbicanDBSync: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("%s-db-sync", barbicanName.Name), + }, + BarbicanAPI: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("%s-api-api", barbicanName.Name), + }, + BarbicanRole: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("barbican-%s-role", barbicanName.Name), + }, + BarbicanRoleBinding: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("barbican-%s-rolebinding", barbicanName.Name), + }, + BarbicanTransportURL: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("barbican-%s-transport", barbicanName.Name), + }, + BarbicanSA: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("barbican-%s", barbicanName.Name), + }, + BarbicanKeystoneService: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: barbicanName.Name, + }, + BarbicanKeystoneEndpoint: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("%s-api", barbicanName.Name), + }, + // Also used to identify BarbicanRoutePublic + BarbicanServicePublic: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("%s-public", barbicanName.Name), + }, + BarbicanServiceInternal: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("%s-internal", barbicanName.Name), + }, + BarbicanConfigSecret: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("%s-%s", barbicanName.Name, "config-data"), + }, + BarbicanConfigScripts: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("%s-%s", barbicanName.Name, "scripts"), + }, + BarbicanConfigMapData: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("%s-%s", barbicanName.Name, "config-data"), + }, + BarbicanScheduler: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: fmt.Sprintf("%s-scheduler", barbicanName.Name), + }, + InternalAPINAD: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: "internalapi", + }, + CABundleSecret: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: CABundleSecretName, + }, + InternalCertSecret: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: InternalCertSecretName, + }, + PublicCertSecret: types.NamespacedName{ + Namespace: barbicanName.Namespace, + Name: PublicCertSecretName, + }, + RabbitmqClusterName: "rabbitmq", + RabbitmqSecretName: "rabbitmq-secret", + BarbicanDatabaseUser: "barbican", + DatabaseInstance: "openstack", + // Password used for both db and service + BarbicanPassword: "12345678", + BarbicanServiceUser: "barbican", + ContainerImage: "test://barbican", + DatabaseHostname: "database-hostname", + } +} diff --git a/tests/functional/base_test.go b/tests/functional/base_test.go index b9309fe..d6d4afc 100644 --- a/tests/functional/base_test.go +++ b/tests/functional/base_test.go @@ -14,10 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package functional_test +package functional import ( "fmt" + + maps "golang.org/x/exp/maps" + . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" @@ -47,7 +50,6 @@ func GetDefaultBarbicanSpec() map[string]interface{} { } func CreateBarbican(name types.NamespacedName, spec map[string]interface{}) client.Object { - raw := map[string]interface{}{ "apiVersion": "barbican.openstack.org/v1beta1", "kind": "Barbican", @@ -117,3 +119,81 @@ func BarbicanKeystoneListenerNotExists(name types.NamespacedName) { g.Expect(k8s_errors.IsNotFound(err)).To(BeTrue()) }, timeout, interval).Should(Succeed()) } + +// ========== TLS Stuff ============== +func BarbicanAPIConditionGetter(name types.NamespacedName) condition.Conditions { + instance := GetBarbicanAPI(name) + return instance.Status.Conditions +} + +func BarbicanAPIExists(name types.NamespacedName) { + Consistently(func(g Gomega) { + instance := &barbicanv1.BarbicanAPI{} + err := k8sClient.Get(ctx, name, instance) + g.Expect(k8s_errors.IsNotFound(err)).To(BeFalse()) + }, timeout, interval).Should(Succeed()) +} + +func GetBarbicanAPI(name types.NamespacedName) *barbicanv1.BarbicanAPI { + instance := &barbicanv1.BarbicanAPI{} + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, name, instance)).Should(Succeed()) + }, timeout, interval).Should(Succeed()) + return instance +} + +func GetTLSBarbicanSpec() map[string]interface{} { + return map[string]interface{}{ + "databaseInstance": "openstack", + "secret": SecretName, + "barbicanAPI": GetTLSBarbicanAPISpec(), + } +} + +func GetTLSBarbicanAPISpec() map[string]interface{} { + spec := GetDefaultBarbicanAPISpec() + maps.Copy(spec, map[string]interface{}{ + "tls": map[string]interface{}{ + "api": map[string]interface{}{ + "internal": map[string]interface{}{ + "secretName": InternalCertSecretName, + }, + "public": map[string]interface{}{ + "secretName": PublicCertSecretName, + }, + }, + "caBundleSecretName": CABundleSecretName, + }, + }) + return spec +} + +func GetDefaultBarbicanAPISpec() map[string]interface{} { + return map[string]interface{}{ + "secret": SecretName, + "replicas": 1, + "databaseHostname": barbicanTest.DatabaseHostname, + "databaseInstance": barbicanTest.DatabaseInstance, + "containerImage": barbicanTest.ContainerImage, + "serviceAccount": barbicanTest.BarbicanSA.Name, + "transportURLSecret": barbicanTest.RabbitmqSecretName, + } +} + +func CreateBarbicanAPI(name types.NamespacedName, spec map[string]interface{}) client.Object { + // we get the parent CR and set ownership to the barbicanAPI CR + raw := map[string]interface{}{ + "apiVersion": "barbican.openstack.org/v1beta1", + "kind": "BarbicanAPI", + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "keystoneEndpoint": "true", + }, + "name": name.Name, + "namespace": name.Namespace, + }, + "spec": spec, + } + + return th.CreateUnstructured(raw) +} diff --git a/tests/functional/suite_test.go b/tests/functional/suite_test.go index a65d955..5638b5a 100644 --- a/tests/functional/suite_test.go +++ b/tests/functional/suite_test.go @@ -1,4 +1,4 @@ -package functional_test +package functional import ( "context" @@ -15,6 +15,7 @@ import ( . "github.com/onsi/gomega" "go.uber.org/zap/zapcore" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" @@ -44,17 +45,19 @@ import ( // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. var ( - cfg *rest.Config - k8sClient client.Client - testEnv *envtest.Environment - ctx context.Context - cancel context.CancelFunc - logger logr.Logger - th *common_test.TestHelper - keystone *keystone_test.TestHelper - mariadb *mariadb_test.TestHelper - infra *infra_test.TestHelper - namespace string + cfg *rest.Config + k8sClient client.Client + testEnv *envtest.Environment + ctx context.Context + cancel context.CancelFunc + logger logr.Logger + th *common_test.TestHelper + keystone *keystone_test.TestHelper + mariadb *mariadb_test.TestHelper + infra *infra_test.TestHelper + namespace string + barbicanName types.NamespacedName + barbicanTest BarbicanTestData ) const ( @@ -138,6 +141,7 @@ var _ = BeforeSuite(func() { err = appsv1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) + logger = ctrl.Log.WithName("---Test---") //+kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) @@ -170,11 +174,6 @@ var _ = BeforeSuite(func() { kclient, err := kubernetes.NewForConfig(cfg) Expect(err).ToNot(HaveOccurred(), "failed to create kclient") - err = (&barbicanv1.Barbican{}).SetupWebhookWithManager(k8sManager) - Expect(err).NotTo(HaveOccurred()) - - barbicanv1.SetupDefaults() - err = (&controllers.BarbicanReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), @@ -182,6 +181,18 @@ var _ = BeforeSuite(func() { }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) + err = (&controllers.BarbicanAPIReconciler{ + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + Kclient: kclient, + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + barbicanv1.SetupDefaults() + + err = (&barbicanv1.Barbican{}).SetupWebhookWithManager(k8sManager) + Expect(err).NotTo(HaveOccurred()) + go func() { defer GinkgoRecover() err = k8sManager.Start(ctx) @@ -216,5 +227,12 @@ var _ = BeforeEach(func() { th.CreateNamespace(namespace) // We still request the delete of the Namespace to properly cleanup if // we run the test in an existing cluster. + barbicanName = types.NamespacedName{ + Namespace: namespace, + Name: "barbican", + } + + barbicanTest = GetBarbicanTestData(barbicanName) + DeferCleanup(th.DeleteNamespace, namespace) })