From 97b194513e9ca2d223555d324ab6ce32c7574e37 Mon Sep 17 00:00:00 2001 From: Nimer Naamneh Date: Mon, 22 Jul 2024 03:29:04 +0300 Subject: [PATCH] add support for providing custom certs for OLS and Custom ca for UI --- api/v1alpha1/olsconfig_types.go | 4 ++ .../bases/ols.openshift.io_olsconfigs.yaml | 4 ++ hack/custom_certs/cr-with-custom-certs.yaml | 32 +++++++++++++ hack/custom_certs/generate-certs.sh | 46 +++++++++++++++++++ internal/controller/ols_app_server_assets.go | 18 +++++--- .../ols_app_server_reconciliator.go | 14 ++++-- .../controller/ols_console_reconciliator.go | 4 +- internal/controller/ols_console_ui_assets.go | 5 ++ 8 files changed, 114 insertions(+), 13 deletions(-) create mode 100644 hack/custom_certs/cr-with-custom-certs.yaml create mode 100755 hack/custom_certs/generate-certs.sh diff --git a/api/v1alpha1/olsconfig_types.go b/api/v1alpha1/olsconfig_types.go index 991eab92..1b9c1e45 100644 --- a/api/v1alpha1/olsconfig_types.go +++ b/api/v1alpha1/olsconfig_types.go @@ -80,6 +80,8 @@ type OLSSpec struct { // User data collection switches // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="User Data Collection" UserDataCollection UserDataCollectionSpec `json:"userDataCollection,omitempty"` + + UseUserProvidedTLSCerts bool `json:"useUserProvidedTLSCerts,omitempty"` // Additional CA certificates for TLS communication between OLS service and LLM Provider // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Additional CA Configmap",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:advanced"} AdditionalCAConfigMapRef *corev1.LocalObjectReference `json:"additionalCAConfigMapRef,omitempty"` @@ -124,6 +126,8 @@ type ConsoleContainerConfig struct { Tolerations []corev1.Toleration `json:"tolerations,omitempty"` // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Node Selector",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:nodeSelector"} NodeSelector map[string]string `json:"nodeSelector,omitempty"` + + CAcertificate string `json:"caCertificate,omitempty"` } // +kubebuilder:validation:Enum=redis diff --git a/config/crd/bases/ols.openshift.io_olsconfigs.yaml b/config/crd/bases/ols.openshift.io_olsconfigs.yaml index 7314270d..4ccce27e 100644 --- a/config/crd/bases/ols.openshift.io_olsconfigs.yaml +++ b/config/crd/bases/ols.openshift.io_olsconfigs.yaml @@ -286,6 +286,8 @@ spec: console: description: Console container settings. properties: + caCertificate: + type: string nodeSelector: additionalProperties: type: string @@ -483,6 +485,8 @@ spec: type: string type: object type: array + useUserProvidedTLSCerts: + type: boolean userDataCollection: description: User data collection switches properties: diff --git a/hack/custom_certs/cr-with-custom-certs.yaml b/hack/custom_certs/cr-with-custom-certs.yaml new file mode 100644 index 00000000..9f7d20c6 --- /dev/null +++ b/hack/custom_certs/cr-with-custom-certs.yaml @@ -0,0 +1,32 @@ +apiVersion: ols.openshift.io/v1alpha1 +kind: OLSConfig +metadata: + name: cluster + namespace: openshift-lightspeed +spec: + llm: + providers: + - credentialsSecretRef: + name: bam-api-keys + type: bam + models: + - name: ibm/granite-13b-chat-v2 + name: bam + url: https://bam-api.res.ibm.com + ols: + conversationCache: + redis: + maxMemory: 2000mb + maxMemoryPolicy: allkeys-lru + type: redis + defaultModel: ibm/granite-13b-chat-v2 + defaultProvider: bam + logLevel: INFO + useUserProvidedTLSCerts: true + deployment: + replicas: 1 + console: + caCertificate: | + -----BEGIN CERTIFICATE----- + your-certificate content, syntax sensitive + -----END CERTIFICATE----- diff --git a/hack/custom_certs/generate-certs.sh b/hack/custom_certs/generate-certs.sh new file mode 100755 index 00000000..d846766f --- /dev/null +++ b/hack/custom_certs/generate-certs.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# Define variables +CERT_DIR="./certs" +CA_KEY="$CERT_DIR/ca.key" +CA_CERT="$CERT_DIR/ca.crt" +PRIVATE_KEY="$CERT_DIR/tls.key" +CERTIFICATE="$CERT_DIR/tls.crt" +DAYS_VALID=365 +SECRET_NAME="lightspeed-tls" +NAMESPACE="openshift-lightspeed" + +# Create directory for certificates if it doesn't exist +mkdir -p "$CERT_DIR" + +# Generate CA private key and self-signed CA certificate +openssl req -x509 -newkey rsa:4096 -sha256 -days "$DAYS_VALID" -nodes \ + -keyout "$CA_KEY" -out "$CA_CERT" -subj "/CN=MyCA" \ + -addext "subjectAltName=DNS:MyCA" + +echo "CA certificate and private key have been generated in $CERT_DIR" + +# Generate private key and certificate signing request (CSR) for the server +openssl req -new -newkey rsa:4096 -nodes -keyout "$PRIVATE_KEY" -out "$CERT_DIR/server.csr" \ + -subj "/CN=lightspeed-app-server" -addext "subjectAltName=DNS:lightspeed-app-server,DNS:lightspeed-app-server.openshift-lightspeed.svc.cluster.local,IP:127.0.0.1,IP:::1" + +# Sign the server certificate with the CA certificate +openssl x509 -req -in "$CERT_DIR/server.csr" -CA "$CA_CERT" -CAkey "$CA_KEY" -CAcreateserial \ + -out "$CERTIFICATE" -days "$DAYS_VALID" -sha256 -extfile <(echo "subjectAltName=DNS:lightspeed-app-server,DNS:lightspeed-app-server.openshift-lightspeed.svc.cluster.local,IP:127.0.0.1,IP:::1") + +echo "Server certificate signed by CA has been generated in $CERT_DIR" + +# Generate the Kubernetes Secret YAML manifest for the TLS certificate and key for the ols-server +cat < "$CERT_DIR/$SECRET_NAME.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: $SECRET_NAME + namespace: $NAMESPACE +type: kubernetes.io/tls +data: + tls.crt: $(base64 < "$CERTIFICATE") + tls.key: $(base64 < "$PRIVATE_KEY") +EOF + +echo "Kubernetes Secret manifest for TLS has been generated at $CERT_DIR/$SECRET_NAME.yaml" \ No newline at end of file diff --git a/internal/controller/ols_app_server_assets.go b/internal/controller/ols_app_server_assets.go index de0a7d0c..c07bd377 100644 --- a/internal/controller/ols_app_server_assets.go +++ b/internal/controller/ols_app_server_assets.go @@ -320,14 +320,20 @@ func (r *OLSConfigReconciler) getAdditionalCAFileNames(cr *olsv1alpha1.OLSConfig } func (r *OLSConfigReconciler) generateService(cr *olsv1alpha1.OLSConfig) (*corev1.Service, error) { + annotations := map[string]string{} + + // Check if the flag for user-provided TLS certs is set + if !cr.Spec.OLSConfig.UseUserProvidedTLSCerts { + // Add the service-served certs annotations only if the flag is not set + annotations[ServingCertSecretAnnotationKey] = OLSCertsSecretName + } + service := corev1.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: OLSAppServerServiceName, - Namespace: r.Options.Namespace, - Labels: generateAppServerSelectorLabels(), - Annotations: map[string]string{ - ServingCertSecretAnnotationKey: OLSCertsSecretName, - }, + Name: OLSAppServerServiceName, + Namespace: r.Options.Namespace, + Labels: generateAppServerSelectorLabels(), + Annotations: annotations, }, Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ diff --git a/internal/controller/ols_app_server_reconciliator.go b/internal/controller/ols_app_server_reconciliator.go index 3a23f65b..ec197380 100644 --- a/internal/controller/ols_app_server_reconciliator.go +++ b/internal/controller/ols_app_server_reconciliator.go @@ -303,11 +303,15 @@ func (r *OLSConfigReconciler) reconcileService(ctx context.Context, cr *olsv1alp return fmt.Errorf("%s: %w", ErrGetAPIServiceAccount, err) } - if serviceEqual(foundService, service) && - foundService.ObjectMeta.Annotations != nil && - foundService.ObjectMeta.Annotations[ServingCertSecretAnnotationKey] == service.ObjectMeta.Annotations[ServingCertSecretAnnotationKey] { - r.logger.Info("OLS service unchanged, reconciliation skipped", "service", service.Name) - return nil + if serviceEqual(foundService, service) && foundService.ObjectMeta.Annotations != nil { + if cr.Spec.OLSConfig.UseUserProvidedTLSCerts { + r.logger.Info("OLS service unchanged, reconciliation skipped", "service", service.Name) + return nil + + } else if foundService.ObjectMeta.Annotations[ServingCertSecretAnnotationKey] == service.ObjectMeta.Annotations[ServingCertSecretAnnotationKey] { + r.logger.Info("OLS service unchanged, reconciliation skipped", "service", service.Name) + return nil + } } err = r.Update(ctx, service) diff --git a/internal/controller/ols_console_reconciliator.go b/internal/controller/ols_console_reconciliator.go index 53695838..94d0ac4d 100644 --- a/internal/controller/ols_console_reconciliator.go +++ b/internal/controller/ols_console_reconciliator.go @@ -19,7 +19,7 @@ import ( olsv1alpha1 "github.com/openshift/lightspeed-operator/api/v1alpha1" ) -func (r *OLSConfigReconciler) reconcileConsoleUI(ctx context.Context, olsconfig *olsv1alpha1.OLSConfig) error { +func (r *OLSConfigReconciler) reconcileConsoleUI(ctx context.Context, cr *olsv1alpha1.OLSConfig) error { r.logger.Info("reconcileConsoleUI starts") tasks := []ReconcileTask{ { @@ -49,7 +49,7 @@ func (r *OLSConfigReconciler) reconcileConsoleUI(ctx context.Context, olsconfig } for _, task := range tasks { - err := task.Task(ctx, olsconfig) + err := task.Task(ctx, cr) if err != nil { r.logger.Error(err, "reconcileConsoleUI error", "task", task.Name) return fmt.Errorf("failed to %s: %w", task.Name, err) diff --git a/internal/controller/ols_console_ui_assets.go b/internal/controller/ols_console_ui_assets.go index 3a227b06..780512fc 100644 --- a/internal/controller/ols_console_ui_assets.go +++ b/internal/controller/ols_console_ui_assets.go @@ -234,6 +234,11 @@ func (r *OLSConfigReconciler) generateConsoleUIPlugin(cr *olsv1alpha1.OLSConfig) }, } + // Conditionally add the CA certificate if provided in the CRD + if cr.Spec.OLSConfig.DeploymentConfig.ConsoleContainer.CAcertificate != "" { + plugin.Spec.Proxy[0].CACertificate = cr.Spec.OLSConfig.DeploymentConfig.ConsoleContainer.CAcertificate + } + if err := controllerutil.SetControllerReference(cr, plugin, r.Scheme); err != nil { return nil, err }