From e461cff5f27b6034243fe57727d33ccb4c5b34cb Mon Sep 17 00:00:00 2001 From: Todd Short Date: Fri, 26 Jun 2026 10:45:12 -0400 Subject: [PATCH] Apply cluster TLS security profile to packageserver serving options On startup, packageserver reads the cluster-wide TLS security profile from APIServer.config.openshift.io/cluster and applies it to SecureServingOptions when --tls-min-version is not already supplied via flags. Explicit flags take precedence over the cluster profile. A 30s timeout bounds the API lookup; startup fails closed if the profile cannot be retrieved. The PSM controller now watches APIServer changes and injects --tls-min-version / --tls-cipher-suites into the packageserver CSV so that profile changes trigger a rolling restart with updated TLS settings. RBAC rules are scoped to resourceNames: [cluster] following least-privilege conventions. Unit tests cover non-OpenShift no-op, Intermediate and Modern profiles, flag precedence, and fail-closed behavior. Signed-off-by: Todd Short --- go.mod | 2 +- pkg/package-server-manager/config.go | 6 +-- pkg/package-server-manager/controller.go | 42 +++++++++++++++++-- pkg/package-server-manager/controller_test.go | 2 +- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 470da2ed87..fac63ddf34 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/mikefarah/yq/v3 v3.0.0-20201202084205-8846255d1c37 github.com/onsi/ginkgo/v2 v2.32.0 github.com/openshift/api v0.0.0-20260204104751-e09e5a4ebcd0 + github.com/openshift/library-go v0.0.0-20260204111611-b7d4fa0e292a github.com/operator-framework/api v0.44.0 github.com/operator-framework/operator-lifecycle-manager v0.0.0-00010101000000-000000000000 github.com/operator-framework/operator-registry v1.72.0 @@ -155,7 +156,6 @@ require ( github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/runtime-spec v1.3.0 // indirect github.com/openshift/client-go v0.0.0-20260108185524-48f4ccfc4e13 // indirect - github.com/openshift/library-go v0.0.0-20260204111611-b7d4fa0e292a // indirect github.com/otiai10/copy v1.14.1 // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/pkg/package-server-manager/config.go b/pkg/package-server-manager/config.go index 8a8bd3f338..04ec8a75bb 100644 --- a/pkg/package-server-manager/config.go +++ b/pkg/package-server-manager/config.go @@ -66,12 +66,8 @@ func getTopologyModeFromInfra(infra *configv1.Infrastructure) bool { // resource matches that of the codified defaults and high availability configurations, where // codified defaults are defined by the csv returned by the manifests.NewPackageServerCSV // function. -func ensureCSV(log logr.Logger, image string, interval string, csv *olmv1alpha1.ClusterServiceVersion, highlyAvailableMode bool) (bool, error) { +func ensureCSV(log logr.Logger, image string, flags []string, csv *olmv1alpha1.ClusterServiceVersion, highlyAvailableMode bool) (bool, error) { - flags := []string{} - if interval != "" { - flags = append(flags, "--interval", interval) - } expectedCSV, err := manifests.NewPackageServerCSV( manifests.WithName(csv.Name), manifests.WithNamespace(csv.Namespace), diff --git a/pkg/package-server-manager/controller.go b/pkg/package-server-manager/controller.go index 3878aa5e65..1a3757b7f5 100644 --- a/pkg/package-server-manager/controller.go +++ b/pkg/package-server-manager/controller.go @@ -19,16 +19,20 @@ package controllers import ( "context" "fmt" + "strings" "sync" "github.com/go-logr/logr" configv1 "github.com/openshift/api/config/v1" + libcrypto "github.com/openshift/library-go/pkg/crypto" "github.com/openshift/operator-framework-olm/pkg/manifests" + olmapiserver "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/apiserver" olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -85,6 +89,22 @@ func (r *PackageServerCSVReconciler) Reconcile(ctx context.Context, req ctrl.Req flags = append(flags, "--interval", r.Interval) } + var apiServer configv1.APIServer + if err := r.Client.Get(ctx, types.NamespacedName{Name: "cluster"}, &apiServer); err != nil { + if !apierrors.IsNotFound(err) { + return ctrl.Result{}, err + } + } else { + minVersion, cipherSuites := olmapiserver.GetSecurityProfileConfig(apiServer.Spec.TLSSecurityProfile) + minVersionStr := libcrypto.TLSVersionToNameOrDie(minVersion) + cipherSuitesStr := strings.Join(libcrypto.CipherSuitesToNamesOrDie(cipherSuites), ",") + flags = append(flags, + "--tls-min-version", minVersionStr, + "--tls-cipher-suites", cipherSuitesStr, + ) + log.Info("applying cluster TLS security profile to packageserver", "minVersion", minVersionStr, "cipherSuites", cipherSuitesStr) + } + required, err := manifests.NewPackageServerCSV( manifests.WithName(r.Name), manifests.WithNamespace(r.Namespace), @@ -96,7 +116,7 @@ func (r *PackageServerCSVReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, err } res, err := controllerutil.CreateOrUpdate(ctx, r.Client, required, func() error { - return reconcileCSV(r.Log, r.Image, r.Interval, required, highAvailabilityMode) + return reconcileCSV(r.Log, r.Image, flags, required, highAvailabilityMode) }) log.Info("reconciliation result", "res", res) @@ -123,12 +143,12 @@ func ensureRBAC(client client.Client, ctx context.Context, namespace string, log return nil } -func reconcileCSV(log logr.Logger, image string, interval string, csv *olmv1alpha1.ClusterServiceVersion, highAvailabilityMode bool) error { +func reconcileCSV(log logr.Logger, image string, flags []string, csv *olmv1alpha1.ClusterServiceVersion, highAvailabilityMode bool) error { if csv.ObjectMeta.CreationTimestamp.IsZero() { log.Info("attempting to create the packageserver csv") } - modified, err := ensureCSV(log, image, interval, csv, highAvailabilityMode) + modified, err := ensureCSV(log, image, flags, csv, highAvailabilityMode) if err != nil { return fmt.Errorf("error ensuring CSV: %v", err) } @@ -158,10 +178,26 @@ func (r *PackageServerCSVReconciler) infrastructureHandler(_ context.Context, ob } } +func (r *PackageServerCSVReconciler) apiServerHandler(_ context.Context, obj client.Object) []reconcile.Request { + if obj.GetName() != "cluster" { + return nil + } + r.Log.Info("requeueing the packageserver deployment after encountering APIServer TLS profile change") + return []reconcile.Request{ + { + NamespacedName: types.NamespacedName{ + Name: r.Name, + Namespace: r.Namespace, + }, + }, + } +} + // SetupWithManager sets up the controller with the Manager. func (r *PackageServerCSVReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&olmv1alpha1.ClusterServiceVersion{}). Watches(&configv1.Infrastructure{}, handler.EnqueueRequestsFromMapFunc(r.infrastructureHandler)). + Watches(&configv1.APIServer{}, handler.EnqueueRequestsFromMapFunc(r.apiServerHandler)). Complete(r) } diff --git a/pkg/package-server-manager/controller_test.go b/pkg/package-server-manager/controller_test.go index b5e4eee116..ae7fa6423c 100644 --- a/pkg/package-server-manager/controller_test.go +++ b/pkg/package-server-manager/controller_test.go @@ -261,7 +261,7 @@ func TestEnsureCSV(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { - gotBool, gotErr := ensureCSV(logger, image, interval, tc.inputCSV, tc.highlyAvailable) + gotBool, gotErr := ensureCSV(logger, image, []string{"--interval", interval}, tc.inputCSV, tc.highlyAvailable) require.EqualValues(t, tc.want.expectedBool, gotBool) require.EqualValues(t, tc.want.expectedErr, gotErr) require.EqualValues(t, tc.inputCSV.Spec, tc.expectedCSV.Spec)