From 88816d6fee8fd30b141148af50d39c706b9a8b36 Mon Sep 17 00:00:00 2001 From: David Kwon Date: Wed, 19 Aug 2026 16:09:41 -0400 Subject: [PATCH 01/11] Honor cluster TLS profile only when tlsAdherence policy requires it Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: David Kwon --- TLS_ADHERENCE_TEST_PLAN.md | 139 +++++++++++++++ .../workspace/devworkspace_controller.go | 1 + deploy/deployment/kubernetes/combined.yaml | 8 + ...workspace-controller-role.ClusterRole.yaml | 8 + deploy/deployment/openshift/combined.yaml | 8 + ...workspace-controller-role.ClusterRole.yaml | 8 + deploy/templates/components/rbac/role.yaml | 8 + go.mod | 11 +- go.sum | 161 ++---------------- main.go | 24 ++- pkg/tlssetup/server_tls.go | 141 +++++++++++++++ pkg/tlssetup/server_tls_test.go | 88 ++++++++++ webhook/main.go | 25 ++- 13 files changed, 469 insertions(+), 161 deletions(-) create mode 100644 TLS_ADHERENCE_TEST_PLAN.md create mode 100644 pkg/tlssetup/server_tls.go create mode 100644 pkg/tlssetup/server_tls_test.go diff --git a/TLS_ADHERENCE_TEST_PLAN.md b/TLS_ADHERENCE_TEST_PLAN.md new file mode 100644 index 000000000..28c9a4353 --- /dev/null +++ b/TLS_ADHERENCE_TEST_PLAN.md @@ -0,0 +1,139 @@ +# TLS Adherence Feature Test Plan + +Tests that DevWorkspace Operator honors the cluster TLS profile when `tlsAdherence: StrictAllComponents` is set. + +## Prerequisites + +```bash +# Verify OpenShift cluster and DWO installation +oc get deployment -n openshift-operators devworkspace-controller-manager +oc get deployment -n openshift-operators devworkspace-webhook-server +``` + +## Test 1: Default Behavior (No Adherence Policy) + +By default, `tlsAdherence` is not set and DWO uses Go's default TLS config. + +```bash +# Check current policy (should be empty) +oc get apiserver cluster -o jsonpath='{.spec.tlsAdherence}{"\n"}' + +# Check controller logs +CONTROLLER_POD=$(oc get pods -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller -o jsonpath='{.items[0].metadata.name}') +oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep -i "tls" +``` + +**Expected**: Log shows `"using Go default TLS configuration"` with empty or no policy. + +## Test 2: Enable StrictAllComponents + +Enable strict adherence and verify DWO applies the cluster TLS profile. + +```bash +# Set StrictAllComponents with a TLS profile +oc patch apiserver cluster --type=merge -p '{"spec":{"tlsSecurityProfile":{"type":"Intermediate","intermediate":{}},"tlsAdherence":"StrictAllComponents"}}' + +# Delete controller pod to pick up new policy +oc delete pod -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller + +# Wait for new pod +sleep 10 + +# Check logs +CONTROLLER_POD=$(oc get pods -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller -o jsonpath='{.items[0].metadata.name}') +oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep -A3 "Applying cluster TLS profile" +``` + +**Expected**: Log shows: +``` +"Applying cluster TLS profile to metrics and webhook servers" + minTLSVersion="VersionTLS12" + adherencePolicy="StrictAllComponents" +``` + +## Test 3: Profile Change Detection + +Verify controller restarts when TLS profile changes. + +```bash +# Change to a different profile (e.g., Modern) +oc patch apiserver cluster --type=merge -p '{"spec":{"tlsSecurityProfile":{"type":"Modern","modern":{}}}}' + +# Wait for automatic restart +sleep 20 + +# Get new pod and check logs +CONTROLLER_POD=$(oc get pods -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller -o jsonpath='{.items[0].metadata.name}') +oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep -A3 "Applying cluster TLS profile" +``` + +**Expected**: Log shows `minTLSVersion="VersionTLS13"` (Modern profile) and a restart message like `"TLS security profile changed; initiating graceful restart"`. + +## Test 4: Policy Change Detection + +Verify controller restarts when adherence policy changes. + +```bash +# Change policy to LegacyAdheringComponentsOnly (does not honor profile) +oc patch apiserver cluster --type=merge -p '{"spec":{"tlsAdherence":"LegacyAdheringComponentsOnly"}}' + +# Wait for automatic restart +sleep 20 + +# Check logs +CONTROLLER_POD=$(oc get pods -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller -o jsonpath='{.items[0].metadata.name}') +oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep -i "tls" +``` + +**Expected**: Log shows `"using Go default TLS configuration"` with `policy="LegacyAdheringComponentsOnly"` and a restart message like `"TLS adherence policy changed; initiating graceful restart"`. + +## Test 5: Smoke Test + +Verify controller functions correctly with TLS adherence enabled. + +```bash +# Re-enable StrictAllComponents +oc patch apiserver cluster --type=merge -p '{"spec":{"tlsAdherence":"StrictAllComponents"}}' + +# Wait for automatic restart +sleep 20 + +# Create test workspace +cat < 0 { + log.Info("TLS profile contains ciphers unsupported by Go; they will be ignored", + "unsupportedCiphers", unsupported) + } + + result.TLSOpts = []func(*tls.Config){tlsConfigFn} + + log.Info("Applying cluster TLS profile to metrics and webhook servers", + "minTLSVersion", profile.MinTLSVersion, + "cipherCount", len(profile.Ciphers), + "adherencePolicy", adherence) + + return result, nil +} + +// RegisterSecurityProfileWatcher watches the APIServer TLS profile and adherence policy. +// Calls onCancel to trigger restart when either changes. No-op on non-OpenShift. +func RegisterSecurityProfileWatcher(mgr manager.Manager, serverTLS ServerTLS, onCancel context.CancelFunc, log logr.Logger) error { + if !infrastructure.IsOpenShift() { + return nil + } + + // Only set up the watcher if we successfully fetched the initial profile + if len(serverTLS.TLSOpts) == 0 { + log.Info("Skipping TLS profile watcher (profile not applied)") + return nil + } + + watcher := &ostls.SecurityProfileWatcher{ + Client: mgr.GetClient(), + InitialTLSProfileSpec: serverTLS.InitialTLSProfileSpec, + InitialTLSAdherencePolicy: serverTLS.InitialTLSAdherencePolicy, + OnProfileChange: func(_ context.Context, old, new configv1.TLSProfileSpec) { + log.Info("TLS security profile changed; initiating graceful restart", + "oldMinTLSVersion", old.MinTLSVersion, + "newMinTLSVersion", new.MinTLSVersion) + onCancel() + }, + OnAdherencePolicyChange: func(_ context.Context, old, new configv1.TLSAdherencePolicy) { + log.Info("TLS adherence policy changed; initiating graceful restart", + "old", old, + "new", new) + onCancel() + }, + } + + return watcher.SetupWithManager(mgr) +} diff --git a/pkg/tlssetup/server_tls_test.go b/pkg/tlssetup/server_tls_test.go new file mode 100644 index 000000000..b1289097e --- /dev/null +++ b/pkg/tlssetup/server_tls_test.go @@ -0,0 +1,88 @@ +// +// Copyright (c) 2019-2026 Red Hat, Inc. +// 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 tlssetup + +import ( + "testing" + + configv1 "github.com/openshift/api/config/v1" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/devfile/devworkspace-operator/pkg/infrastructure" +) + +func TestShouldHonorClusterTLSProfile(t *testing.T) { + tests := []struct { + name string + adherence configv1.TLSAdherencePolicy + expected bool + }{ + { + name: "Empty policy should not honor cluster TLS profile", + adherence: "", + expected: false, + }, + { + name: "LegacyAdheringComponentsOnly should not honor cluster TLS profile", + adherence: configv1.TLSAdherencePolicyLegacyAdheringComponentsOnly, + expected: false, + }, + { + name: "StrictAllComponents should honor cluster TLS profile", + adherence: configv1.TLSAdherencePolicyStrictAllComponents, + expected: true, + }, + { + name: "Unknown policy should honor cluster TLS profile for forward compatibility", + adherence: configv1.TLSAdherencePolicy("UnknownFuturePolicy"), + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ShouldHonorClusterTLSProfile(tt.adherence) + if got != tt.expected { + t.Errorf("ShouldHonorClusterTLSProfile(%v) = %v, expected %v", tt.adherence, got, tt.expected) + } + }) + } +} + +func TestRegisterSecurityProfileWatcher_NonOpenShift(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.Kubernetes) + defer infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + + log := zap.New(zap.UseDevMode(true)) + + // On non-OpenShift, should be a no-op and return nil + err := RegisterSecurityProfileWatcher(nil, ServerTLS{}, nil, log) + if err != nil { + t.Errorf("RegisterSecurityProfileWatcher() on Kubernetes should be no-op, got error = %v", err) + } +} + +func TestRegisterSecurityProfileWatcher_NoTLSOpts(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defer infrastructure.InitializeForTesting(infrastructure.Kubernetes) + + log := zap.New(zap.UseDevMode(true)) + + // When TLSOpts is empty (profile not applied), should skip watcher setup and return nil + err := RegisterSecurityProfileWatcher(nil, ServerTLS{}, nil, log) + if err != nil { + t.Errorf("RegisterSecurityProfileWatcher() with empty TLSOpts should skip setup, got error = %v", err) + } +} diff --git a/webhook/main.go b/webhook/main.go index 6dd976f54..f38973c27 100644 --- a/webhook/main.go +++ b/webhook/main.go @@ -20,9 +20,7 @@ import ( "flag" "fmt" "os" - "os/signal" "runtime" - "syscall" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" @@ -34,6 +32,7 @@ import ( "github.com/devfile/devworkspace-operator/pkg/cache" "github.com/devfile/devworkspace-operator/pkg/config" "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/devfile/devworkspace-operator/pkg/tlssetup" "github.com/devfile/devworkspace-operator/version" "github.com/devfile/devworkspace-operator/webhook/server" "github.com/devfile/devworkspace-operator/webhook/workspace" @@ -89,6 +88,13 @@ func main() { os.Exit(1) } + serverTLS, err := tlssetup.BuildServerTLSOptions( + context.Background(), cfg, scheme, log) + if err != nil { + log.Error(err, "failed to build TLS options for servers") + os.Exit(1) + } + namespace, err := infrastructure.GetWatchNamespace() if err != nil { log.Error(err, "Failed to get watch namespace") @@ -105,6 +111,7 @@ func main() { CertDir: server.WebhookServerCertDir, Port: server.WebhookServerPort, Host: server.WebhookServerHost, + TLSOpts: serverTLS.TLSOpts, }) // Create a new Cmd to provide shared dependencies and start components @@ -114,6 +121,7 @@ func main() { BindAddress: metricsAddr, FilterProvider: filters.WithAuthenticationAndAuthorization, SecureServing: true, + TLSOpts: serverTLS.TLSOpts, }, WebhookServer: webhookServer, HealthProbeBindAddress: ":6789", @@ -130,8 +138,15 @@ func main() { os.Exit(1) } - var shutdownChan = make(chan os.Signal, 1) - signal.Notify(shutdownChan, syscall.SIGTERM) + // On OpenShift, watch cluster TLS profile and restart if it changes. + signalCtx := signals.SetupSignalHandler() + ctx, cancelCtx := context.WithCancel(signalCtx) + defer cancelCtx() + + if err := tlssetup.RegisterSecurityProfileWatcher(mgr, serverTLS, cancelCtx, log); err != nil { + log.Error(err, "unable to set up TLS security profile watcher") + os.Exit(1) + } // Setup health check if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { @@ -146,7 +161,7 @@ func main() { } log.Info("Starting manager") - if err := mgr.Start(signals.SetupSignalHandler()); err != nil { + if err := mgr.Start(ctx); err != nil { log.Error(err, "Manager exited non-zero") os.Exit(1) } From 69ae0890662ed1a1a139854165186386d3de710a Mon Sep 17 00:00:00 2001 From: David Kwon Date: Wed, 26 Aug 2026 10:46:18 -0400 Subject: [PATCH 02/11] Set up TLS profile watcher even when adherence policy is legacy Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: David Kwon --- pkg/tlssetup/server_tls.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/tlssetup/server_tls.go b/pkg/tlssetup/server_tls.go index 15533d315..826f19ff9 100644 --- a/pkg/tlssetup/server_tls.go +++ b/pkg/tlssetup/server_tls.go @@ -37,6 +37,8 @@ type ServerTLS struct { TLSOpts []func(*tls.Config) InitialTLSProfileSpec configv1.TLSProfileSpec InitialTLSAdherencePolicy configv1.TLSAdherencePolicy + // profileFetched is true when the initial profile was successfully retrieved from OpenShift. + profileFetched bool } // ShouldHonorClusterTLSProfile returns true when the component must honor the cluster @@ -81,6 +83,7 @@ func BuildServerTLSOptions(ctx context.Context, cfg *rest.Config, scheme *k8srun result.InitialTLSProfileSpec = profile result.InitialTLSAdherencePolicy = adherence + result.profileFetched = true // Check if we should honor the cluster TLS profile if !ShouldHonorClusterTLSProfile(adherence) { @@ -114,8 +117,8 @@ func RegisterSecurityProfileWatcher(mgr manager.Manager, serverTLS ServerTLS, on } // Only set up the watcher if we successfully fetched the initial profile - if len(serverTLS.TLSOpts) == 0 { - log.Info("Skipping TLS profile watcher (profile not applied)") + if !serverTLS.profileFetched { + log.Info("Skipping TLS profile watcher (profile fetch failed)") return nil } From f7d0ab4fe7ddfc21d7664a1b65578dba54f5a00c Mon Sep 17 00:00:00 2001 From: David Kwon Date: Wed, 26 Aug 2026 11:35:13 -0400 Subject: [PATCH 03/11] Simplify TLS setup to fall back to Go defaults on errors, restrict apiserver RBAC Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: David Kwon --- controllers/workspace/devworkspace_controller.go | 2 +- main.go | 6 +----- pkg/tlssetup/server_tls.go | 15 ++++++++------- webhook/main.go | 6 +----- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index 1e3f721a3..4cc887d70 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -96,7 +96,7 @@ type DevWorkspaceReconciler struct { // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles;clusterrolebindings,verbs=get;list;watch;create;update // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles;rolebindings,verbs=get;list;watch;create;update;delete // +kubebuilder:rbac:groups=oauth.openshift.io,resources=oauthclients,verbs=get;list;watch;create;update;patch;delete;deletecollection -// +kubebuilder:rbac:groups=config.openshift.io,resources=apiservers,verbs=get;list;watch +// +kubebuilder:rbac:groups=config.openshift.io,resources=apiservers,verbs=get;list;watch,resourceNames=cluster // +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;create // +kubebuilder:rbac:groups=config.openshift.io,resources=proxies,verbs=get,resourceNames=cluster // +kubebuilder:rbac:groups=apps,resourceNames=devworkspace-controller,resources=deployments/finalizers,verbs=update diff --git a/main.go b/main.go index 878210a7b..d2dc89947 100644 --- a/main.go +++ b/main.go @@ -117,12 +117,8 @@ func main() { setupLog.Error(err, "failed to initialized Kubernetes objects decoder") } - serverTLS, err := tlssetup.BuildServerTLSOptions( + serverTLS := tlssetup.BuildServerTLSOptions( context.Background(), ctrl.GetConfigOrDie(), scheme, setupLog) - if err != nil { - setupLog.Error(err, "failed to build TLS options for servers") - os.Exit(1) - } cacheFunc, err := cache.GetCacheFunc() if err != nil { diff --git a/pkg/tlssetup/server_tls.go b/pkg/tlssetup/server_tls.go index 826f19ff9..17e86b668 100644 --- a/pkg/tlssetup/server_tls.go +++ b/pkg/tlssetup/server_tls.go @@ -55,30 +55,31 @@ func ShouldHonorClusterTLSProfile(adherence configv1.TLSAdherencePolicy) bool { // BuildServerTLSOptions fetches TLS settings from the OpenShift API server. // Only applies the cluster profile when the tlsAdherence policy requires it. -func BuildServerTLSOptions(ctx context.Context, cfg *rest.Config, scheme *k8sruntime.Scheme, log logr.Logger) (ServerTLS, error) { +// Falls back to Go TLS defaults on fetch failure. +func BuildServerTLSOptions(ctx context.Context, cfg *rest.Config, scheme *k8sruntime.Scheme, log logr.Logger) ServerTLS { var result ServerTLS if !infrastructure.IsOpenShift() { log.Info("Not running on OpenShift; using Go default TLS configuration") - return result, nil + return result } bootstrapClient, err := client.New(cfg, client.Options{Scheme: scheme}) if err != nil { log.Error(err, "Failed to create bootstrap client for TLS profile fetch; using Go default TLS configuration") - return result, nil + return result } profile, err := ostls.FetchAPIServerTLSProfile(ctx, bootstrapClient) if err != nil { log.Error(err, "Failed to fetch TLS profile from APIServer; using Go default TLS configuration") - return result, nil + return result } adherence, err := ostls.FetchAPIServerTLSAdherencePolicy(ctx, bootstrapClient) if err != nil { log.Error(err, "Failed to fetch TLS adherence policy from APIServer; using Go default TLS configuration") - return result, nil + return result } result.InitialTLSProfileSpec = profile @@ -89,7 +90,7 @@ func BuildServerTLSOptions(ctx context.Context, cfg *rest.Config, scheme *k8srun if !ShouldHonorClusterTLSProfile(adherence) { log.Info("TLS adherence policy does not require strict adherence; using Go default TLS configuration", "policy", adherence) - return result, nil + return result } // Apply the cluster TLS profile @@ -106,7 +107,7 @@ func BuildServerTLSOptions(ctx context.Context, cfg *rest.Config, scheme *k8srun "cipherCount", len(profile.Ciphers), "adherencePolicy", adherence) - return result, nil + return result } // RegisterSecurityProfileWatcher watches the APIServer TLS profile and adherence policy. diff --git a/webhook/main.go b/webhook/main.go index f38973c27..b6175b281 100644 --- a/webhook/main.go +++ b/webhook/main.go @@ -88,12 +88,8 @@ func main() { os.Exit(1) } - serverTLS, err := tlssetup.BuildServerTLSOptions( + serverTLS := tlssetup.BuildServerTLSOptions( context.Background(), cfg, scheme, log) - if err != nil { - log.Error(err, "failed to build TLS options for servers") - os.Exit(1) - } namespace, err := infrastructure.GetWatchNamespace() if err != nil { From b5a3f1ec47753ab162af3483a8dcf4362a032894 Mon Sep 17 00:00:00 2001 From: David Kwon Date: Wed, 26 Aug 2026 15:01:17 -0400 Subject: [PATCH 04/11] Add BuildServerTLSOptions tests and scope APIServer RBAC to cluster resource Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: David Kwon --- TLS_ADHERENCE_TEST_PLAN.md | 78 ++++++- deploy/deployment/kubernetes/combined.yaml | 2 + ...workspace-controller-role.ClusterRole.yaml | 2 + deploy/deployment/openshift/combined.yaml | 2 + ...workspace-controller-role.ClusterRole.yaml | 2 + deploy/templates/components/rbac/role.yaml | 2 + main.go | 2 +- pkg/tlssetup/server_tls.go | 15 +- pkg/tlssetup/server_tls_test.go | 214 +++++++++++++++++- webhook/main.go | 7 +- 10 files changed, 314 insertions(+), 12 deletions(-) diff --git a/TLS_ADHERENCE_TEST_PLAN.md b/TLS_ADHERENCE_TEST_PLAN.md index 28c9a4353..d73c64c61 100644 --- a/TLS_ADHERENCE_TEST_PLAN.md +++ b/TLS_ADHERENCE_TEST_PLAN.md @@ -25,6 +25,21 @@ oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep **Expected**: Log shows `"using Go default TLS configuration"` with empty or no policy. +**Example output (no adherence policy set)**: +```json +{"level":"info","ts":"2026-08-25T21:08:11Z","logger":"setup","msg":"TLS adherence policy does not require strict adherence; using Go default TLS configuration","policy":"LegacyAdheringComponentsOnly"} +{"level":"info","ts":"2026-08-25T21:08:13Z","logger":"setup","msg":"Skipping TLS profile watcher (profile not applied)"} +``` + +
+Example output + +```json +{"level":"info","ts":"2026-08-25T18:56:07Z","logger":"setup","msg":"TLS adherence policy does not require strict adherence; using Go default TLS configuration","policy":""} +{"level":"info","ts":"2026-08-25T18:56:08Z","logger":"setup","msg":"Skipping TLS profile watcher (profile not applied)"} +``` +
+ ## Test 2: Enable StrictAllComponents Enable strict adherence and verify DWO applies the cluster TLS profile. @@ -51,6 +66,19 @@ oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep adherencePolicy="StrictAllComponents" ``` +**Example output**: +```json +{"level":"info","ts":"2026-08-25T21:07:24Z","logger":"setup","msg":"Applying cluster TLS profile to metrics and webhook servers","minTLSVersion":"VersionTLS12","cipherCount":9,"adherencePolicy":"StrictAllComponents"} +``` + +
+Example output + +```json +{"level":"info","ts":"2026-08-25T19:46:44Z","logger":"setup","msg":"Applying cluster TLS profile to metrics and webhook servers","minTLSVersion":"VersionTLS12","cipherCount":9,"adherencePolicy":"StrictAllComponents"} +``` +
+ ## Test 3: Profile Change Detection Verify controller restarts when TLS profile changes. @@ -69,6 +97,19 @@ oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep **Expected**: Log shows `minTLSVersion="VersionTLS13"` (Modern profile) and a restart message like `"TLS security profile changed; initiating graceful restart"`. +**Example output**: +```json +{"level":"info","ts":"2026-08-25T21:10:18Z","logger":"setup","msg":"Applying cluster TLS profile to metrics and webhook servers","minTLSVersion":"VersionTLS13","cipherCount":3,"adherencePolicy":"StrictAllComponents"} +``` + +
+Example output + +```json +{"level":"info","ts":"2026-08-25T19:55:36Z","logger":"setup","msg":"Applying cluster TLS profile to metrics and webhook servers","minTLSVersion":"VersionTLS13","cipherCount":3,"adherencePolicy":"StrictAllComponents"} +``` +
+ ## Test 4: Policy Change Detection Verify controller restarts when adherence policy changes. @@ -87,6 +128,21 @@ oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep **Expected**: Log shows `"using Go default TLS configuration"` with `policy="LegacyAdheringComponentsOnly"` and a restart message like `"TLS adherence policy changed; initiating graceful restart"`. +**Example output**: +```json +{"level":"info","ts":"2026-08-25T21:08:11Z","logger":"setup","msg":"TLS adherence policy does not require strict adherence; using Go default TLS configuration","policy":"LegacyAdheringComponentsOnly"} +{"level":"info","ts":"2026-08-25T21:08:13Z","logger":"setup","msg":"Skipping TLS profile watcher (profile not applied)"} +``` + +
+Example output + +```json +{"level":"info","ts":"2026-08-25T20:04:27Z","logger":"setup","msg":"TLS adherence policy does not require strict adherence; using Go default TLS configuration","policy":"LegacyAdheringComponentsOnly"} +{"level":"info","ts":"2026-08-25T20:04:28Z","logger":"setup","msg":"Skipping TLS profile watcher (profile not applied)"} +``` +
+ ## Test 5: Smoke Test Verify controller functions correctly with TLS adherence enabled. @@ -95,8 +151,11 @@ Verify controller functions correctly with TLS adherence enabled. # Re-enable StrictAllComponents oc patch apiserver cluster --type=merge -p '{"spec":{"tlsAdherence":"StrictAllComponents"}}' -# Wait for automatic restart -sleep 20 +# Delete controller pod to pick up new policy (watcher isn't running from Test 4) +oc delete pod -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller + +# Wait for new pod +sleep 10 # Create test workspace cat < 0 { + tlsConfig := &tls.Config{} + result.TLSOpts[0](tlsConfig) + if tlsConfig.MinVersion != tls.VersionTLS12 { + t.Errorf("Expected MinVersion TLS12, got %v", tlsConfig.MinVersion) + } + } +} + +func TestBuildServerTLSOptions_OpenShift_EmptyAdherence(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defer infrastructure.InitializeForTesting(infrastructure.Kubernetes) + + log := zap.New(zap.UseDevMode(true)) + ctx := context.Background() + + scheme := runtime.NewScheme() + if err := configv1.AddToScheme(scheme); err != nil { + t.Fatalf("Failed to add configv1 to scheme: %v", err) + } + + apiServer := &configv1.APIServer{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + Spec: configv1.APIServerSpec{ + TLSSecurityProfile: &configv1.TLSSecurityProfile{ + Type: configv1.TLSProfileCustomType, + Custom: &configv1.CustomTLSProfile{ + TLSProfileSpec: configv1.TLSProfileSpec{ + MinTLSVersion: configv1.VersionTLS12, + }, + }, + }, + // TLSAdherence not set (empty) + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(apiServer). + Build() + + result := BuildServerTLSOptions(ctx, nil, scheme, log, fakeClient) + + if !result.profileFetched { + t.Errorf("Expected profileFetched=true, got false") + } + if result.TLSOpts != nil { + t.Errorf("Expected nil TLSOpts with empty adherence policy, got %v", result.TLSOpts) + } +} + +func TestBuildServerTLSOptions_OpenShift_NoAPIServer(t *testing.T) { + infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) + defer infrastructure.InitializeForTesting(infrastructure.Kubernetes) + + log := zap.New(zap.UseDevMode(true)) + ctx := context.Background() + + scheme := runtime.NewScheme() + if err := configv1.AddToScheme(scheme); err != nil { + t.Fatalf("Failed to add configv1 to scheme: %v", err) + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(scheme). + Build() + + result := BuildServerTLSOptions(ctx, nil, scheme, log, fakeClient) + + if result.profileFetched { + t.Errorf("Expected profileFetched=false when APIServer resource is missing, got true") + } + if result.TLSOpts != nil { + t.Errorf("Expected nil TLSOpts on fetch failure, got %v", result.TLSOpts) + } +} diff --git a/webhook/main.go b/webhook/main.go index b6175b281..6d86997f7 100644 --- a/webhook/main.go +++ b/webhook/main.go @@ -37,6 +37,7 @@ import ( "github.com/devfile/devworkspace-operator/webhook/server" "github.com/devfile/devworkspace-operator/webhook/workspace" + configv1 "github.com/openshift/api/config/v1" k8sruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -65,6 +66,10 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(dwv1.AddToScheme(scheme)) utilruntime.Must(dwv2.AddToScheme(scheme)) + + if infrastructure.IsOpenShift() { + utilruntime.Must(configv1.AddToScheme(scheme)) + } } func main() { @@ -89,7 +94,7 @@ func main() { } serverTLS := tlssetup.BuildServerTLSOptions( - context.Background(), cfg, scheme, log) + context.Background(), cfg, scheme, log, nil) namespace, err := infrastructure.GetWatchNamespace() if err != nil { From 67b606c9dac8bdb4db9a4ae278d5ef1732f999b8 Mon Sep 17 00:00:00 2001 From: David Kwon Date: Wed, 26 Aug 2026 15:22:24 -0400 Subject: [PATCH 05/11] Regenerate OLM bundle CSV to add apiservers RBAC permissions Signed-off-by: David Kwon --- .../devworkspace-operator.clusterserviceversion.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/deploy/bundle/manifests/devworkspace-operator.clusterserviceversion.yaml b/deploy/bundle/manifests/devworkspace-operator.clusterserviceversion.yaml index 181ceb452..f10d41253 100644 --- a/deploy/bundle/manifests/devworkspace-operator.clusterserviceversion.yaml +++ b/deploy/bundle/manifests/devworkspace-operator.clusterserviceversion.yaml @@ -215,6 +215,16 @@ spec: - patch - update - watch + - apiGroups: + - config.openshift.io + resourceNames: + - cluster + resources: + - apiservers + verbs: + - get + - list + - watch - apiGroups: - config.openshift.io resourceNames: From f25f74806bebd9137518a778345f738bc070d552 Mon Sep 17 00:00:00 2001 From: David Kwon Date: Wed, 26 Aug 2026 15:56:42 -0400 Subject: [PATCH 06/11] Remove TLS adherence test plan Signed-off-by: David Kwon --- TLS_ADHERENCE_TEST_PLAN.md | 211 ------------------------------------- 1 file changed, 211 deletions(-) delete mode 100644 TLS_ADHERENCE_TEST_PLAN.md diff --git a/TLS_ADHERENCE_TEST_PLAN.md b/TLS_ADHERENCE_TEST_PLAN.md deleted file mode 100644 index d73c64c61..000000000 --- a/TLS_ADHERENCE_TEST_PLAN.md +++ /dev/null @@ -1,211 +0,0 @@ -# TLS Adherence Feature Test Plan - -Tests that DevWorkspace Operator honors the cluster TLS profile when `tlsAdherence: StrictAllComponents` is set. - -## Prerequisites - -```bash -# Verify OpenShift cluster and DWO installation -oc get deployment -n openshift-operators devworkspace-controller-manager -oc get deployment -n openshift-operators devworkspace-webhook-server -``` - -## Test 1: Default Behavior (No Adherence Policy) - -By default, `tlsAdherence` is not set and DWO uses Go's default TLS config. - -```bash -# Check current policy (should be empty) -oc get apiserver cluster -o jsonpath='{.spec.tlsAdherence}{"\n"}' - -# Check controller logs -CONTROLLER_POD=$(oc get pods -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller -o jsonpath='{.items[0].metadata.name}') -oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep -i "tls" -``` - -**Expected**: Log shows `"using Go default TLS configuration"` with empty or no policy. - -**Example output (no adherence policy set)**: -```json -{"level":"info","ts":"2026-08-25T21:08:11Z","logger":"setup","msg":"TLS adherence policy does not require strict adherence; using Go default TLS configuration","policy":"LegacyAdheringComponentsOnly"} -{"level":"info","ts":"2026-08-25T21:08:13Z","logger":"setup","msg":"Skipping TLS profile watcher (profile not applied)"} -``` - -
-Example output - -```json -{"level":"info","ts":"2026-08-25T18:56:07Z","logger":"setup","msg":"TLS adherence policy does not require strict adherence; using Go default TLS configuration","policy":""} -{"level":"info","ts":"2026-08-25T18:56:08Z","logger":"setup","msg":"Skipping TLS profile watcher (profile not applied)"} -``` -
- -## Test 2: Enable StrictAllComponents - -Enable strict adherence and verify DWO applies the cluster TLS profile. - -```bash -# Set StrictAllComponents with a TLS profile -oc patch apiserver cluster --type=merge -p '{"spec":{"tlsSecurityProfile":{"type":"Intermediate","intermediate":{}},"tlsAdherence":"StrictAllComponents"}}' - -# Delete controller pod to pick up new policy -oc delete pod -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller - -# Wait for new pod -sleep 10 - -# Check logs -CONTROLLER_POD=$(oc get pods -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller -o jsonpath='{.items[0].metadata.name}') -oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep -A3 "Applying cluster TLS profile" -``` - -**Expected**: Log shows: -``` -"Applying cluster TLS profile to metrics and webhook servers" - minTLSVersion="VersionTLS12" - adherencePolicy="StrictAllComponents" -``` - -**Example output**: -```json -{"level":"info","ts":"2026-08-25T21:07:24Z","logger":"setup","msg":"Applying cluster TLS profile to metrics and webhook servers","minTLSVersion":"VersionTLS12","cipherCount":9,"adherencePolicy":"StrictAllComponents"} -``` - -
-Example output - -```json -{"level":"info","ts":"2026-08-25T19:46:44Z","logger":"setup","msg":"Applying cluster TLS profile to metrics and webhook servers","minTLSVersion":"VersionTLS12","cipherCount":9,"adherencePolicy":"StrictAllComponents"} -``` -
- -## Test 3: Profile Change Detection - -Verify controller restarts when TLS profile changes. - -```bash -# Change to a different profile (e.g., Modern) -oc patch apiserver cluster --type=merge -p '{"spec":{"tlsSecurityProfile":{"type":"Modern","modern":{}}}}' - -# Wait for automatic restart -sleep 20 - -# Get new pod and check logs -CONTROLLER_POD=$(oc get pods -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller -o jsonpath='{.items[0].metadata.name}') -oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep -A3 "Applying cluster TLS profile" -``` - -**Expected**: Log shows `minTLSVersion="VersionTLS13"` (Modern profile) and a restart message like `"TLS security profile changed; initiating graceful restart"`. - -**Example output**: -```json -{"level":"info","ts":"2026-08-25T21:10:18Z","logger":"setup","msg":"Applying cluster TLS profile to metrics and webhook servers","minTLSVersion":"VersionTLS13","cipherCount":3,"adherencePolicy":"StrictAllComponents"} -``` - -
-Example output - -```json -{"level":"info","ts":"2026-08-25T19:55:36Z","logger":"setup","msg":"Applying cluster TLS profile to metrics and webhook servers","minTLSVersion":"VersionTLS13","cipherCount":3,"adherencePolicy":"StrictAllComponents"} -``` -
- -## Test 4: Policy Change Detection - -Verify controller restarts when adherence policy changes. - -```bash -# Change policy to LegacyAdheringComponentsOnly (does not honor profile) -oc patch apiserver cluster --type=merge -p '{"spec":{"tlsAdherence":"LegacyAdheringComponentsOnly"}}' - -# Wait for automatic restart -sleep 20 - -# Check logs -CONTROLLER_POD=$(oc get pods -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller -o jsonpath='{.items[0].metadata.name}') -oc logs -n openshift-operators $CONTROLLER_POD -c devworkspace-controller | grep -i "tls" -``` - -**Expected**: Log shows `"using Go default TLS configuration"` with `policy="LegacyAdheringComponentsOnly"` and a restart message like `"TLS adherence policy changed; initiating graceful restart"`. - -**Example output**: -```json -{"level":"info","ts":"2026-08-25T21:08:11Z","logger":"setup","msg":"TLS adherence policy does not require strict adherence; using Go default TLS configuration","policy":"LegacyAdheringComponentsOnly"} -{"level":"info","ts":"2026-08-25T21:08:13Z","logger":"setup","msg":"Skipping TLS profile watcher (profile not applied)"} -``` - -
-Example output - -```json -{"level":"info","ts":"2026-08-25T20:04:27Z","logger":"setup","msg":"TLS adherence policy does not require strict adherence; using Go default TLS configuration","policy":"LegacyAdheringComponentsOnly"} -{"level":"info","ts":"2026-08-25T20:04:28Z","logger":"setup","msg":"Skipping TLS profile watcher (profile not applied)"} -``` -
- -## Test 5: Smoke Test - -Verify controller functions correctly with TLS adherence enabled. - -```bash -# Re-enable StrictAllComponents -oc patch apiserver cluster --type=merge -p '{"spec":{"tlsAdherence":"StrictAllComponents"}}' - -# Delete controller pod to pick up new policy (watcher isn't running from Test 4) -oc delete pod -n openshift-operators -l app.kubernetes.io/name=devworkspace-controller - -# Wait for new pod -sleep 10 - -# Create test workspace -cat < Date: Wed, 26 Aug 2026 17:08:04 -0400 Subject: [PATCH 07/11] Split APIServer RBAC into separate rules for get vs list/watch since resourceNames restriction blocks list/watch verbs but works correctly for get Signed-off-by: David Kwon --- controllers/workspace/devworkspace_controller.go | 3 ++- deploy/deployment/kubernetes/combined.yaml | 4 +--- .../objects/devworkspace-controller-role.ClusterRole.yaml | 4 +--- deploy/deployment/openshift/combined.yaml | 4 +--- .../objects/devworkspace-controller-role.ClusterRole.yaml | 4 +--- deploy/templates/components/rbac/role.yaml | 4 +--- 6 files changed, 7 insertions(+), 16 deletions(-) diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index 4cc887d70..2015c8534 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -96,7 +96,8 @@ type DevWorkspaceReconciler struct { // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles;clusterrolebindings,verbs=get;list;watch;create;update // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles;rolebindings,verbs=get;list;watch;create;update;delete // +kubebuilder:rbac:groups=oauth.openshift.io,resources=oauthclients,verbs=get;list;watch;create;update;patch;delete;deletecollection -// +kubebuilder:rbac:groups=config.openshift.io,resources=apiservers,verbs=get;list;watch,resourceNames=cluster +// +kubebuilder:rbac:groups=config.openshift.io,resources=apiservers,verbs=get,resourceNames=cluster +// +kubebuilder:rbac:groups=config.openshift.io,resources=apiservers,verbs=list;watch // +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;create // +kubebuilder:rbac:groups=config.openshift.io,resources=proxies,verbs=get,resourceNames=cluster // +kubebuilder:rbac:groups=apps,resourceNames=devworkspace-controller,resources=deployments/finalizers,verbs=update diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index db1d558c4..267420c6a 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -27926,12 +27926,9 @@ rules: - watch - apiGroups: - config.openshift.io - resourceNames: - - cluster resources: - apiservers verbs: - - get - list - watch - apiGroups: @@ -27939,6 +27936,7 @@ rules: resourceNames: - cluster resources: + - apiservers - proxies verbs: - get diff --git a/deploy/deployment/kubernetes/objects/devworkspace-controller-role.ClusterRole.yaml b/deploy/deployment/kubernetes/objects/devworkspace-controller-role.ClusterRole.yaml index 4dbef45c5..22d965689 100644 --- a/deploy/deployment/kubernetes/objects/devworkspace-controller-role.ClusterRole.yaml +++ b/deploy/deployment/kubernetes/objects/devworkspace-controller-role.ClusterRole.yaml @@ -137,12 +137,9 @@ rules: - watch - apiGroups: - config.openshift.io - resourceNames: - - cluster resources: - apiservers verbs: - - get - list - watch - apiGroups: @@ -150,6 +147,7 @@ rules: resourceNames: - cluster resources: + - apiservers - proxies verbs: - get diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index e13c71d68..ee5d7a8e6 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -27926,12 +27926,9 @@ rules: - watch - apiGroups: - config.openshift.io - resourceNames: - - cluster resources: - apiservers verbs: - - get - list - watch - apiGroups: @@ -27939,6 +27936,7 @@ rules: resourceNames: - cluster resources: + - apiservers - proxies verbs: - get diff --git a/deploy/deployment/openshift/objects/devworkspace-controller-role.ClusterRole.yaml b/deploy/deployment/openshift/objects/devworkspace-controller-role.ClusterRole.yaml index 4dbef45c5..22d965689 100644 --- a/deploy/deployment/openshift/objects/devworkspace-controller-role.ClusterRole.yaml +++ b/deploy/deployment/openshift/objects/devworkspace-controller-role.ClusterRole.yaml @@ -137,12 +137,9 @@ rules: - watch - apiGroups: - config.openshift.io - resourceNames: - - cluster resources: - apiservers verbs: - - get - list - watch - apiGroups: @@ -150,6 +147,7 @@ rules: resourceNames: - cluster resources: + - apiservers - proxies verbs: - get diff --git a/deploy/templates/components/rbac/role.yaml b/deploy/templates/components/rbac/role.yaml index 7e31c2f5e..e660f82fc 100644 --- a/deploy/templates/components/rbac/role.yaml +++ b/deploy/templates/components/rbac/role.yaml @@ -135,12 +135,9 @@ rules: - watch - apiGroups: - config.openshift.io - resourceNames: - - cluster resources: - apiservers verbs: - - get - list - watch - apiGroups: @@ -148,6 +145,7 @@ rules: resourceNames: - cluster resources: + - apiservers - proxies verbs: - get From 947ae66f3edfb3d04b0228666d7c241d5db0ce76 Mon Sep 17 00:00:00 2001 From: David Kwon Date: Wed, 26 Aug 2026 17:33:27 -0400 Subject: [PATCH 08/11] Change formating, rerun make generate_all Signed-off-by: David Kwon --- ...kspace-operator.clusterserviceversion.yaml | 4 +--- webhook/main.go | 24 +++++++++---------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/deploy/bundle/manifests/devworkspace-operator.clusterserviceversion.yaml b/deploy/bundle/manifests/devworkspace-operator.clusterserviceversion.yaml index f10d41253..4c2e0a960 100644 --- a/deploy/bundle/manifests/devworkspace-operator.clusterserviceversion.yaml +++ b/deploy/bundle/manifests/devworkspace-operator.clusterserviceversion.yaml @@ -217,12 +217,9 @@ spec: - watch - apiGroups: - config.openshift.io - resourceNames: - - cluster resources: - apiservers verbs: - - get - list - watch - apiGroups: @@ -230,6 +227,7 @@ spec: resourceNames: - cluster resources: + - apiservers - proxies verbs: - get diff --git a/webhook/main.go b/webhook/main.go index 6d86997f7..763526b86 100644 --- a/webhook/main.go +++ b/webhook/main.go @@ -22,21 +22,8 @@ import ( "os" "runtime" - "sigs.k8s.io/controller-runtime/pkg/metrics/filters" - - metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - "sigs.k8s.io/controller-runtime/pkg/webhook" - dwv1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha1" dwv2 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" - "github.com/devfile/devworkspace-operator/pkg/cache" - "github.com/devfile/devworkspace-operator/pkg/config" - "github.com/devfile/devworkspace-operator/pkg/infrastructure" - "github.com/devfile/devworkspace-operator/pkg/tlssetup" - "github.com/devfile/devworkspace-operator/version" - "github.com/devfile/devworkspace-operator/webhook/server" - "github.com/devfile/devworkspace-operator/webhook/workspace" - configv1 "github.com/openshift/api/config/v1" k8sruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -48,6 +35,17 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/manager/signals" + "sigs.k8s.io/controller-runtime/pkg/metrics/filters" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/devfile/devworkspace-operator/pkg/cache" + "github.com/devfile/devworkspace-operator/pkg/config" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/devfile/devworkspace-operator/pkg/tlssetup" + "github.com/devfile/devworkspace-operator/version" + "github.com/devfile/devworkspace-operator/webhook/server" + "github.com/devfile/devworkspace-operator/webhook/workspace" ) var ( From a585a179b0ad71ada6dcfd2a3562c04d03b1a642 Mon Sep 17 00:00:00 2001 From: David Kwon Date: Wed, 26 Aug 2026 18:03:51 -0400 Subject: [PATCH 09/11] Update github.com/openshift/library-go Signed-off-by: David Kwon --- go.mod | 41 +++++++++++++++++----------- go.sum | 85 ++++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 81 insertions(+), 45 deletions(-) diff --git a/go.mod b/go.mod index 349b432ad..a12f9b18a 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/kevinburke/ssh_config v1.2.0 github.com/onsi/ginkgo/v2 v2.28.1 github.com/onsi/gomega v1.39.1 - github.com/openshift/api v0.0.0-20260317165824-54a3998d81eb + github.com/openshift/api v0.0.0-20260805215214-cfb63858e9d7 github.com/openshift/controller-runtime-common v0.0.0-20260428152732-64ee174f5e2e github.com/operator-framework/operator-lib v0.11.0 github.com/prometheus/client_golang v1.23.2 @@ -23,11 +23,11 @@ require ( golang.org/x/crypto v0.54.0 golang.org/x/mod v0.38.0 golang.org/x/net v0.57.0 - k8s.io/api v0.36.0 - k8s.io/apiextensions-apiserver v0.36.0 - k8s.io/apimachinery v0.36.0 - k8s.io/client-go v0.36.0 - k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 + k8s.io/api v0.36.2 + k8s.io/apiextensions-apiserver v0.36.2 + k8s.io/apimachinery v0.36.2 + k8s.io/client-go v0.36.2 + k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 sigs.k8s.io/controller-runtime v0.24.1 sigs.k8s.io/yaml v1.6.0 ) @@ -59,7 +59,18 @@ require ( github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/swag v0.25.4 // indirect + github.com/go-openapi/swag/cmdutils v0.25.4 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/fileutils v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/mangling v0.25.4 // indirect + github.com/go-openapi/swag/netutils v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/cel-go v0.29.0 // indirect @@ -70,15 +81,13 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/openshift/library-go v0.0.0-20260213153706-03f1709971c5 // indirect + github.com/openshift/library-go v0.0.0-20260826200314-c1c4c5daeed6 // indirect github.com/pjbgf/sha1cd v0.6.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -87,7 +96,7 @@ require ( github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/spf13/cobra v1.10.2 // indirect - github.com/spf13/pflag v1.0.9 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect @@ -109,7 +118,7 @@ require ( golang.org/x/sys v0.47.0 // indirect golang.org/x/term v0.45.0 // indirect golang.org/x/text v0.40.0 // indirect - golang.org/x/time v0.14.0 // indirect + golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.47.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect @@ -120,11 +129,11 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.36.0 // indirect - k8s.io/component-base v0.36.0 // indirect + k8s.io/apiserver v0.36.2 // indirect + k8s.io/component-base v0.36.2 // indirect k8s.io/klog/v2 v2.140.0 // indirect - k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect - k8s.io/streaming v0.36.0 // indirect + k8s.io/kube-openapi v0.0.0-20260519202549-bbf5c5577288 // indirect + k8s.io/streaming v0.36.2 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect diff --git a/go.sum b/go.sum index 6d81fb003..03c148fe5 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,36 @@ github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kO github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= +github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= +github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= +github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= +github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= +github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= +github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= +github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= @@ -113,7 +141,6 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= @@ -135,7 +162,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= @@ -159,12 +185,12 @@ github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= -github.com/openshift/api v0.0.0-20260317165824-54a3998d81eb h1:iwBR3mzmyE3EMFx7R3CQ9lOccTS0dNht8TW82aGITg0= -github.com/openshift/api v0.0.0-20260317165824-54a3998d81eb/go.mod h1:pyVjK0nZ4sRs4fuQVQ4rubsJdahI1PB94LnQ8sGdvxo= +github.com/openshift/api v0.0.0-20260805215214-cfb63858e9d7 h1:Z6p+yoWjFXbfnN2zPdQ8SRXPKDs9QtT3P8hN3SP2zuw= +github.com/openshift/api v0.0.0-20260805215214-cfb63858e9d7/go.mod h1:k6qH5QOVa5GDln2VVm8Jz4NV3Z7R2SATHFLwGS6Wh3M= github.com/openshift/controller-runtime-common v0.0.0-20260428152732-64ee174f5e2e h1:k89oIo2EjX0PRSdi1kesktCyWp50SC9WwKurvupvRGs= github.com/openshift/controller-runtime-common v0.0.0-20260428152732-64ee174f5e2e/go.mod h1:XGabTMnNbz0M5Oa7IbscZp/jmcc7aHobvOCUWwkzKvM= -github.com/openshift/library-go v0.0.0-20260213153706-03f1709971c5 h1:9Pe6iVOMjt9CdA/vaKBNUSoEIjIe1po5Ha3ABRYXLJI= -github.com/openshift/library-go v0.0.0-20260213153706-03f1709971c5/go.mod h1:K3FoNLgNBFYbFuG+Kr8usAnQxj1w84XogyUp2M8rK8k= +github.com/openshift/library-go v0.0.0-20260826200314-c1c4c5daeed6 h1:pCKUs2jUKIvnKuMmIFC/dlblpsK+C1+Oke5CQVvWwXI= +github.com/openshift/library-go v0.0.0-20260826200314-c1c4c5daeed6/go.mod h1:IrZbEK+wVUMEd+aXzYR2DCCh0p5IaQ7DvycYGP7qIYM= github.com/operator-framework/operator-lib v0.11.0 h1:eYzqpiOfq9WBI4Trddisiq/X9BwCisZd3rIzmHRC9Z8= github.com/operator-framework/operator-lib v0.11.0/go.mod h1:RpyKhFAoG6DmKTDIwMuO6pI3LRc8IE9rxEYWy476o6g= github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU= @@ -194,8 +220,9 @@ github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnB github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -279,8 +306,8 @@ golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= @@ -313,26 +340,26 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.36.0 h1:SgqDhZzHdOtMk40xVSvCXkP9ME0H05hPM3p9AB1kL80= -k8s.io/api v0.36.0/go.mod h1:m1LVrGPNYax5NBHdO+QuAedXyuzTt4RryI/qnmNvs34= -k8s.io/apiextensions-apiserver v0.36.0 h1:Wt7E8J+VBCbj4FjiBfDTK/neXDDjyJVJc7xfuOHImZ0= -k8s.io/apiextensions-apiserver v0.36.0/go.mod h1:kGDjH0msuiIB3tgsYRV0kS9GqpMYMUsQ3GHv7TApyug= -k8s.io/apimachinery v0.36.0 h1:jZyPzhd5Z+3h9vJLt0z9XdzW9VzNzWAUw+P1xZ9PXtQ= -k8s.io/apimachinery v0.36.0/go.mod h1:FklypaRJt6n5wUIwWXIP6GJlIpUizTgfo1T/As+Tyxc= -k8s.io/apiserver v0.36.0 h1:Jg5OFAENUACByUCg15CmhZAYrr5ZyJ+jodyA1mHl3YE= -k8s.io/apiserver v0.36.0/go.mod h1:mHvwdHf+qKEm+1/hYm756SV+oREOKSPnsjagOpx6Vho= -k8s.io/client-go v0.36.0 h1:pOYi7C4RHChYjMiHpZSpSbIM6ZxVbRXBy7CuiIwqA3c= -k8s.io/client-go v0.36.0/go.mod h1:ZKKcpwF0aLYfkHFCjillCKaTK/yBkEDHTDXCFY6AS9Y= -k8s.io/component-base v0.36.0 h1:hFjEktssxiJhrK1zfybkH4kJOi8iZuF+mIDCqS5+jRo= -k8s.io/component-base v0.36.0/go.mod h1:JZvIfcNHk+uck+8LhJzhSBtydWXaZNQwX2OdL+Mnwsk= +k8s.io/api v0.36.2 h1:TF6YDLIzKfccK7cq9YpTcGX8TJmEkHVRv78DM51fRYY= +k8s.io/api v0.36.2/go.mod h1:F4LbMO4brjZYh7yFkXWhynSvtB7YauxV4c+HHkNRGNg= +k8s.io/apiextensions-apiserver v0.36.2 h1:3O5gqOj/dt2XWWbpMe+TXWpE9yU6pjM/tXxtHHJT/K4= +k8s.io/apiextensions-apiserver v0.36.2/go.mod h1:cL1tBWe8XSaP1H30iWKGo7hf6iAUUUJPEU70dskmAnA= +k8s.io/apimachinery v0.36.2 h1:0PE/W/WNy1UX61NLbXY5TMbJ6UwLL6E6lAPkYrKFxbQ= +k8s.io/apimachinery v0.36.2/go.mod h1:fvf/HOLXq9RId0rnDIbN1OEBvHXdQbLMM8nu0LcBUf4= +k8s.io/apiserver v0.36.2 h1:6vMnkmHZPeBloNkHUhmZYq7Ylv8WIB8xjyEl+eSt26E= +k8s.io/apiserver v0.36.2/go.mod h1:9PoQ2ikCytrZyZg11mGhLEF5m8Rgsb5FJmYJ4Wvnl1k= +k8s.io/client-go v0.36.2 h1:bfgxmFKc9CgqsgX4xKLAAdmTQlWee7Ob/HlDOrJ5TBI= +k8s.io/client-go v0.36.2/go.mod h1:1vgO4OAlfPnoLcb+Rze2GF5rAr14w8qjrYMoyXJzQj0= +k8s.io/component-base v0.36.2 h1:Z0VH80O7Ng0HDZnZj3WRR3urEGa0kTwmO8CwEwjVK1w= +k8s.io/component-base v0.36.2/go.mod h1:mGfFOA7Gwpdm1VW2cwSQYbiDIlz8GD2WGwH88QSeCyA= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= -k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg= -k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= -k8s.io/streaming v0.36.0 h1:agnTxU+NFulUrtYzXUGKO3ndEa8jKwht1Kwn9nu9x+4= -k8s.io/streaming v0.36.0/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s= -k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= -k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= +k8s.io/kube-openapi v0.0.0-20260519202549-bbf5c5577288 h1:A7Lby6ekC6nv+6oO38huCMFBRP0Os+tIeq1GkwxOQes= +k8s.io/kube-openapi v0.0.0-20260519202549-bbf5c5577288/go.mod h1:V/QaCUYDa+0QpcHhVVc5l99Uz56wEMEXBSj9oCDkNDY= +k8s.io/streaming v0.36.2 h1:NSKthPPg9UFSKsRauVJUVGH2Dvn8fhKmY4qrMkw/p98= +k8s.io/streaming v0.36.2/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s= +k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 h1:jVkFFVfXdXP74B/zbO3hM3hpSFD0xvhQ5U686DPurkE= +k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3/go.mod h1:M2s5JB1lIYP3jzZdorPLHXIPJzt9vv2muW5a6L9DtNM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.24.1 h1:miPEwrmirImAvgME1L9qebGHrOnGJoVmVdtOU9fRfo4= From a2df59934d41d62205d95426c890b6faaaba4448 Mon Sep 17 00:00:00 2001 From: David Kwon Date: Thu, 27 Aug 2026 11:19:01 -0400 Subject: [PATCH 10/11] Update test name Signed-off-by: David Kwon --- pkg/tlssetup/server_tls_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/tlssetup/server_tls_test.go b/pkg/tlssetup/server_tls_test.go index b1b041469..79fee0728 100644 --- a/pkg/tlssetup/server_tls_test.go +++ b/pkg/tlssetup/server_tls_test.go @@ -78,7 +78,7 @@ func TestRegisterSecurityProfileWatcher_NonOpenShift(t *testing.T) { } } -func TestRegisterSecurityProfileWatcher_NoTLSOpts(t *testing.T) { +func TestRegisterSecurityProfileWatcher_ProfileNotFetched(t *testing.T) { infrastructure.InitializeForTesting(infrastructure.OpenShiftv4) defer infrastructure.InitializeForTesting(infrastructure.Kubernetes) From ddf36f8611cafdabad0511f0d0e684ee74da6be0 Mon Sep 17 00:00:00 2001 From: David Kwon Date: Mon, 31 Aug 2026 15:17:06 -0400 Subject: [PATCH 11/11] Return errors from TLS profile fetch instead of silently degrading Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: David Kwon --- main.go | 6 +++++- pkg/tlssetup/server_tls.go | 19 ++++++++----------- pkg/tlssetup/server_tls_test.go | 29 +++++++++++++++++++---------- webhook/main.go | 6 +++++- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/main.go b/main.go index a5ef492cb..d88741259 100644 --- a/main.go +++ b/main.go @@ -117,8 +117,12 @@ func main() { setupLog.Error(err, "failed to initialized Kubernetes objects decoder") } - serverTLS := tlssetup.BuildServerTLSOptions( + serverTLS, err := tlssetup.BuildServerTLSOptions( context.Background(), ctrl.GetConfigOrDie(), scheme, setupLog, nil) + if err != nil { + setupLog.Error(err, "failed to build TLS options from cluster TLS profile") + os.Exit(1) + } cacheFunc, err := cache.GetCacheFunc() if err != nil { diff --git a/pkg/tlssetup/server_tls.go b/pkg/tlssetup/server_tls.go index 8ab265b5c..569115e3b 100644 --- a/pkg/tlssetup/server_tls.go +++ b/pkg/tlssetup/server_tls.go @@ -55,14 +55,14 @@ func ShouldHonorClusterTLSProfile(adherence configv1.TLSAdherencePolicy) bool { // BuildServerTLSOptions fetches TLS settings from the OpenShift API server. // Only applies the cluster profile when the tlsAdherence policy requires it. -// Falls back to Go TLS defaults on fetch failure. +// Returns an error if running on OpenShift but profile fetch fails. // If bootstrapClient is nil, creates a new client from cfg and scheme. -func BuildServerTLSOptions(ctx context.Context, cfg *rest.Config, scheme *k8sruntime.Scheme, log logr.Logger, bootstrapClient client.Client) ServerTLS { +func BuildServerTLSOptions(ctx context.Context, cfg *rest.Config, scheme *k8sruntime.Scheme, log logr.Logger, bootstrapClient client.Client) (ServerTLS, error) { var result ServerTLS if !infrastructure.IsOpenShift() { log.Info("Not running on OpenShift; using Go default TLS configuration") - return result + return result, nil } // Create bootstrap client if not provided (production path) @@ -70,21 +70,18 @@ func BuildServerTLSOptions(ctx context.Context, cfg *rest.Config, scheme *k8srun var err error bootstrapClient, err = client.New(cfg, client.Options{Scheme: scheme}) if err != nil { - log.Error(err, "Failed to create bootstrap client for TLS profile fetch; using Go default TLS configuration") - return result + return result, err } } profile, err := ostls.FetchAPIServerTLSProfile(ctx, bootstrapClient) if err != nil { - log.Error(err, "Failed to fetch TLS profile from APIServer; using Go default TLS configuration") - return result + return result, err } adherence, err := ostls.FetchAPIServerTLSAdherencePolicy(ctx, bootstrapClient) if err != nil { - log.Error(err, "Failed to fetch TLS adherence policy from APIServer; using Go default TLS configuration") - return result + return result, err } result.InitialTLSProfileSpec = profile @@ -95,7 +92,7 @@ func BuildServerTLSOptions(ctx context.Context, cfg *rest.Config, scheme *k8srun if !ShouldHonorClusterTLSProfile(adherence) { log.Info("TLS adherence policy does not require strict adherence; using Go default TLS configuration", "policy", adherence) - return result + return result, nil } // Apply the cluster TLS profile @@ -112,7 +109,7 @@ func BuildServerTLSOptions(ctx context.Context, cfg *rest.Config, scheme *k8srun "cipherCount", len(profile.Ciphers), "adherencePolicy", adherence) - return result + return result, nil } // RegisterSecurityProfileWatcher watches the APIServer TLS profile and adherence policy. diff --git a/pkg/tlssetup/server_tls_test.go b/pkg/tlssetup/server_tls_test.go index 79fee0728..153a62f85 100644 --- a/pkg/tlssetup/server_tls_test.go +++ b/pkg/tlssetup/server_tls_test.go @@ -97,7 +97,10 @@ func TestBuildServerTLSOptions_NonOpenShift(t *testing.T) { log := zap.New(zap.UseDevMode(true)) ctx := context.Background() - result := BuildServerTLSOptions(ctx, nil, nil, log, nil) + result, err := BuildServerTLSOptions(ctx, nil, nil, log, nil) + if err != nil { + t.Fatalf("Unexpected error on non-OpenShift: %v", err) + } if result.TLSOpts != nil { t.Errorf("Expected nil TLSOpts on non-OpenShift, got %v", result.TLSOpts) @@ -145,7 +148,10 @@ func TestBuildServerTLSOptions_OpenShift_LegacyAdherence(t *testing.T) { WithObjects(apiServer). Build() - result := BuildServerTLSOptions(ctx, nil, scheme, log, fakeClient) + result, err := BuildServerTLSOptions(ctx, nil, scheme, log, fakeClient) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } if !result.profileFetched { t.Errorf("Expected profileFetched=true, got false") @@ -201,7 +207,10 @@ func TestBuildServerTLSOptions_OpenShift_StrictAdherence(t *testing.T) { WithObjects(apiServer). Build() - result := BuildServerTLSOptions(ctx, nil, scheme, log, fakeClient) + result, err := BuildServerTLSOptions(ctx, nil, scheme, log, fakeClient) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } if !result.profileFetched { t.Errorf("Expected profileFetched=true, got false") @@ -261,7 +270,10 @@ func TestBuildServerTLSOptions_OpenShift_EmptyAdherence(t *testing.T) { WithObjects(apiServer). Build() - result := BuildServerTLSOptions(ctx, nil, scheme, log, fakeClient) + result, err := BuildServerTLSOptions(ctx, nil, scheme, log, fakeClient) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } if !result.profileFetched { t.Errorf("Expected profileFetched=true, got false") @@ -287,12 +299,9 @@ func TestBuildServerTLSOptions_OpenShift_NoAPIServer(t *testing.T) { WithScheme(scheme). Build() - result := BuildServerTLSOptions(ctx, nil, scheme, log, fakeClient) + _, err := BuildServerTLSOptions(ctx, nil, scheme, log, fakeClient) - if result.profileFetched { - t.Errorf("Expected profileFetched=false when APIServer resource is missing, got true") - } - if result.TLSOpts != nil { - t.Errorf("Expected nil TLSOpts on fetch failure, got %v", result.TLSOpts) + if err == nil { + t.Errorf("Expected error when APIServer resource is missing, got nil") } } diff --git a/webhook/main.go b/webhook/main.go index 763526b86..665f00ac4 100644 --- a/webhook/main.go +++ b/webhook/main.go @@ -91,8 +91,12 @@ func main() { os.Exit(1) } - serverTLS := tlssetup.BuildServerTLSOptions( + serverTLS, err := tlssetup.BuildServerTLSOptions( context.Background(), cfg, scheme, log, nil) + if err != nil { + log.Error(err, "failed to build TLS options from cluster TLS profile") + os.Exit(1) + } namespace, err := infrastructure.GetWatchNamespace() if err != nil {