Skip to content

chore: use centralised TLS Profile#3234

Draft
OpinionatedHeron wants to merge 7 commits into
redhat-developer:mainfrom
OpinionatedHeron:tls
Draft

chore: use centralised TLS Profile#3234
OpinionatedHeron wants to merge 7 commits into
redhat-developer:mainfrom
OpinionatedHeron:tls

Conversation

@OpinionatedHeron

Copy link
Copy Markdown
Member

Description

Implemented centralized OpenShift TLS security profile support for the RHDH operator (controller-runtime path via github.com/openshift/controller-runtime-common), so the operator’s TLS servers honor apiservers.config.openshift.io/cluster.

Which issue(s) does this PR fix or relate to

PR acceptance criteria

  • Tests
  • Documentation

Building Container Images for Testing

Need to test container images from this PR?

For Maintainers: To trigger a test image build, review the code and comment /build-images.
This always builds the HEAD of the PR branch.

For Contributors: Ask a maintainer to run /build-images.

Images will be built and pushed to Quay with links posted in comments.

Signed-off-by: Leanne Ahern <lahern@redhat.com>
Signed-off-by: Leanne Ahern <lahern@redhat.com>
…ices

Signed-off-by: Leanne Ahern <lahern@redhat.com>
@OpinionatedHeron
OpinionatedHeron requested a review from a team as a code owner July 21, 2026 14:20
@rhdh-qodo-merge

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Context used
⚠️ Tickets: not configured — ticket URL found in PR but could not be fetched — check ticket provider credentials
✅ Compliance rules (platform): 18 rules
✅ Cross-repo context
  Not relevant to this PR: redhat-developer/rhdh
  Not relevant to this PR: redhat-developer/rhdh-plugins

Grey Divider


Action required

1. TLS fallback hard-exits 🐞 Bug ☼ Reliability
Description
cmd/main.go advertises an Intermediate fallback when TLS profile lookup/fetch fails, but it
terminates the process if tlspkg.GetTLSProfileSpec(nil) returns an error, preventing any fallback
and blocking operator startup in that case.
Code

cmd/main.go[R109-115]

+	// Fetch the TLS profile from apiservers.config.openshift.io/cluster.
+	// Fall back to Intermediate on non-OpenShift clusters (or if the fetch fails).
+	tlsSecurityProfileSpec, err := tlspkg.GetTLSProfileSpec(nil)
+	if err != nil {
+		setupLog.Error(err, "unable to get default TLS profile")
+		os.Exit(1)
+	}
Relevance

⭐⭐ Medium

No historical evidence on preferring fallback vs hard-exit for TLS default profile errors in main
startup.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code comment says it should fall back to Intermediate, but the error branch exits the process
immediately, so the fallback cannot occur when the default-profile call errors.

cmd/main.go[109-132]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`cmd/main.go` intends to fall back to an Intermediate TLS profile when OpenShift TLS profile discovery fails, but currently calls `os.Exit(1)` if `tlspkg.GetTLSProfileSpec(nil)` returns an error. This bypasses the fallback behavior entirely.

### Issue Context
The code path logs and comments about using an "Intermediate fallback" for non-OpenShift clusters or fetch failures, but it still hard-exits before the fetch/fallback code can run.

### Fix Focus Areas
- cmd/main.go[109-132]

### Suggested fix
- Replace the `os.Exit(1)` branch with:
 - a log that you’re falling back, and
 - an explicit initialization of `tlsSecurityProfileSpec` to the Intermediate profile (or another safe default), then continue startup.
- Ensure the log messages match the actual behavior (i.e., only claim "Intermediate fallback" when you actually set it).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. TLS profile fetch unguarded 📘 Rule violation ≡ Correctness
Description
cmd/main.go calls tlspkg.FetchAPIServerTLSProfile/FetchAPIServerTLSAdherencePolicy before any
explicit check that the OpenShift apiservers.config.openshift.io API is available. On
non-OpenShift clusters this performs requests against a potentially-missing API and relies on error
fallback instead of guarding access up front as required.
Code

cmd/main.go[R107-132]

+	restConfig := ctrl.GetConfigOrDie()
+
+	// Fetch the TLS profile from apiservers.config.openshift.io/cluster.
+	// Fall back to Intermediate on non-OpenShift clusters (or if the fetch fails).
+	tlsSecurityProfileSpec, err := tlspkg.GetTLSProfileSpec(nil)
+	if err != nil {
+		setupLog.Error(err, "unable to get default TLS profile")
+		os.Exit(1)
+	}
+	tlsAdherence := configv1.TLSAdherencePolicyNoOpinion
+
+	k8sClient, err := client.New(restConfig, client.Options{Scheme: scheme})
+	if err != nil {
+		setupLog.Info("unable to create client for TLS profile fetch, using Intermediate fallback", "error", err)
+	} else {
+		if profile, fetchErr := tlspkg.FetchAPIServerTLSProfile(ctx, k8sClient); fetchErr != nil {
+			setupLog.Info("unable to get TLS profile from API server, using Intermediate fallback", "error", fetchErr)
+		} else {
+			tlsSecurityProfileSpec = profile
+		}
+		if adherence, fetchErr := tlspkg.FetchAPIServerTLSAdherencePolicy(ctx, k8sClient); fetchErr != nil {
+			setupLog.Info("unable to get TLS adherence policy from API server", "error", fetchErr)
+		} else {
+			tlsAdherence = adherence
+		}
+	}
Relevance

⭐⭐⭐ High

Team favored explicit CRD/API guards; kept ServiceMonitor CRD existence check, rejected relying on
fetch errors.

PR-#1374

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 11 requires an explicit CRD/API existence guard before accessing optional kinds.
The added code performs FetchAPIServerTLSProfile and FetchAPIServerTLSAdherencePolicy calls
prior to any OpenShift/CRD availability check (platform detection happens later), so access is not
properly guarded.

Rule 11: Guard reconciliation logic on optional CRD existence
cmd/main.go[107-132]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`cmd/main.go` fetches the OpenShift APIServer TLS profile/adherence policy before verifying that the OpenShift `apiservers.config.openshift.io` API/CRD is installed. The compliance rule requires guarding access to optional CRDs/APIs with an explicit availability check before making client calls.

## Issue Context
The platform detection (`plf, err := controller.DetectPlatform()`) and OpenShift-only gating is currently done later (for the watcher), but the initial fetch happens unconditionally.

## Fix Focus Areas
- cmd/main.go[107-132]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Mock Apply panics 🐞 Bug ☼ Reliability
Description
MockClient.Apply unconditionally panics, so any unit test (or test helper) that starts using
controller-runtime v0.23+ Apply semantics will crash instead of failing with a usable error.
Code

internal/controller/mock_client.go[R128-131]

+// Apply was added to client.Writer in controller-runtime v0.23.
+func (m MockClient) Apply(_ context.Context, _ runtime.ApplyConfiguration, _ ...client.ApplyOption) error {
+	panic(implementMe)
+}
Relevance

⭐⭐⭐ High

Team accepted removing panics in core code; likely expects MockClient.Apply to return error not
panic.

PR-#1949

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR adds a new Apply method that panics, and the mock client is actively used as the reconciler
client in unit tests, so invoking Apply would currently crash the tests.

internal/controller/mock_client.go[125-135]
internal/controller/monitor_test.go[20-30]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`MockClient.Apply(...)` was added to satisfy controller-runtime v0.23’s `client.Writer` interface, but it panics. This turns any future use of server-side apply in unit tests into a hard crash.

### Issue Context
The mock client is used in controller unit tests as the reconciler client.

### Fix Focus Areas
- internal/controller/mock_client.go[128-135]

### Suggested fix
- Replace the `panic(implementMe)` with a deterministic, non-panicking behavior:
 - Prefer returning a descriptive error like `fmt.Errorf("mock Apply not implemented")`.
 - Alternatively, implement minimal Apply semantics backed by the in-memory store (if tests are expected to use Apply soon).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@rhdh-qodo-merge

Copy link
Copy Markdown

PR Summary by Qodo

Adopt OpenShift centralized TLS profile for operator servers

✨ Enhancement ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Align operator TLS settings with OpenShift APIServer/cluster TLS profile.
• Restart operator on TLS profile/adherence changes to reapply server config.
• Update RBAC/CSV and Go deps to support TLS profile watch + fetch.
Diagram

graph TD
  A["Operator main"] --> B["Create REST config"] --> C["Create k8s client"] --> D["Fetch TLS profile"] --> E[("APIServer: cluster")] --> F["Build tls.Config"] --> G["Start ctrl.Manager"] --> H["Serve HTTPS endpoints"]
  G --> I["SecurityProfileWatcher"] --> J{"Profile changed?"} --> K["cancel ctx"] --> L["Pod restarts"]
  I -."watch".-> E

  subgraph Legend
    direction LR
    _svc["Component"] ~~~ _api[("Cluster API resource")] ~~~ _dec{"Decision"}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Manual APIServer watch + TLS config mapping
  • ➕ Avoids adding controller-runtime-common + transitive deps
  • ➕ Full control over logging, fallback behavior, and watcher semantics
  • ➖ Reimplements OpenShift-standard behavior already available in shared library
  • ➖ Higher maintenance burden; more risk of subtle divergence from OpenShift expectations
2. Dynamic TLS reload without restart
  • ➕ No downtime or reconciliation interruption during TLS policy changes
  • ➕ Avoids relying on pod restart behavior
  • ➖ Harder to implement correctly across all controller-runtime servers
  • ➖ May require deeper hooks into controller-runtime/http servers and careful concurrency handling
3. Only apply TLS profile when on OpenShift (no fallback)
  • ➕ Simpler logic and clearer contract
  • ➕ Avoids creating extra clients on non-OpenShift clusters
  • ➖ Makes non-OpenShift behavior less predictable; reduces portability
  • ➖ Harder for downstreams running on vanilla Kubernetes to reason about TLS defaults

Recommendation: Current approach (use openshift/controller-runtime-common for profile fetch + SecurityProfileWatcher and restart-on-change) is the best tradeoff: it standardizes behavior with other OpenShift operators and keeps the TLS mapping logic centralized. The restart-on-change strategy is operationally simple and reliable for ensuring all TLS listeners pick up the new configuration, at the cost of a brief manager restart.

Files changed (15) +143 / -97

Enhancement (1) +68 / -3
main.goFetch OpenShift TLS profile and restart manager on profile changes +68/-3

Fetch OpenShift TLS profile and restart manager on profile changes

• Adds OpenShift config APIs to the scheme, fetches the APIServer/cluster TLS profile (with Intermediate fallback), and builds tls.Config options for controller-runtime servers. Registers a SecurityProfileWatcher that cancels the manager context on profile/adherence changes to force a pod restart and reload TLS settings.

cmd/main.go

Refactor (5) +5 / -5
zz_generated.deepcopy.goNormalize generated import formatting +1/-1

Normalize generated import formatting

• Adjusts the generated deepcopy file import to use the standard metav1 import style (no alias). This is a no-op behaviorally.

api/v1alpha1/zz_generated.deepcopy.go

zz_generated.deepcopy.goNormalize generated import formatting +1/-1

Normalize generated import formatting

• Updates the generated deepcopy import to remove an unnecessary alias. No functional changes.

api/v1alpha2/zz_generated.deepcopy.go

zz_generated.deepcopy.goNormalize generated import formatting +1/-1

Normalize generated import formatting

• Updates the generated deepcopy import to remove an unnecessary alias. No functional changes.

api/v1alpha3/zz_generated.deepcopy.go

zz_generated.deepcopy.goNormalize generated import formatting +1/-1

Normalize generated import formatting

• Updates the generated deepcopy import to remove an unnecessary alias. No functional changes.

api/v1alpha4/zz_generated.deepcopy.go

zz_generated.deepcopy.goNormalize generated import formatting +1/-1

Normalize generated import formatting

• Updates the generated deepcopy import to remove an unnecessary alias. No functional changes.

api/v1alpha5/zz_generated.deepcopy.go

Tests (1) +5 / -0
mock_client.goAdd MockClient.Apply for controller-runtime v0.23 interface compatibility +5/-0

Add MockClient.Apply for controller-runtime v0.23 interface compatibility

• Implements the new client.Writer Apply method required by controller-runtime v0.23 by adding a stub that panics (consistent with other unneeded mock methods). This keeps compilation/tests working after the dependency bump.

internal/controller/mock_client.go

Other (8) +65 / -89
backstage-operator.clusterserviceversion.yamlAdvertise TLS profile support and grant APIServer watch permissions +5/-2

Advertise TLS profile support and grant APIServer watch permissions

• Marks the operator as supporting OpenShift TLS profiles and refreshes createdAt. Extends RBAC to include config.openshift.io apiservers with list/watch to enable TLS profile watching.

bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml

backstage-operator.clusterserviceversion.yamlEnable tls-profiles feature flag in CSV base +1/-1

Enable tls-profiles feature flag in CSV base

• Sets features.operators.openshift.io/tls-profiles to true so OLM metadata reflects centralized TLS profile support.

config/manifests/rhdh/bases/backstage-operator.clusterserviceversion.yaml

role.yamlExtend RBAC for APIServer TLS profile watching +3/-0

Extend RBAC for APIServer TLS profile watching

• Adds config.openshift.io apiservers resource and grants get/list/watch on ingresses and apiservers. This enables reading and watching the cluster TLS profile source.

config/rbac/role.yaml

install.yamlPropagate RBAC updates to Backstage.io dist install manifest +3/-0

Propagate RBAC updates to Backstage.io dist install manifest

• Updates the generated install manifest to include apiservers and list/watch verbs, matching the controller RBAC needs for TLS profile watching.

dist/backstage.io/install.yaml

install.yamlPropagate RBAC updates to RHDH dist install manifest +3/-0

Propagate RBAC updates to RHDH dist install manifest

• Updates the generated install manifest to include apiservers and list/watch verbs, matching the controller RBAC needs for TLS profile watching.

dist/rhdh/install.yaml

go.modAdd controller-runtime-common and bump controller-runtime/k8s dependency set +18/-26

Add controller-runtime-common and bump controller-runtime/k8s dependency set

• Introduces github.com/openshift/controller-runtime-common for TLS profile support and upgrades controller-runtime to v0.23.3 with aligned k8s.io replaces pinned to v0.35.4. Updates related indirect dependencies accordingly.

go.mod

go.sumUpdate dependency checksums for TLS profile support and CR upgrades +30/-59

Update dependency checksums for TLS profile support and CR upgrades

• Refreshes go.sum to reflect newly added OpenShift libraries and upgraded controller-runtime/k8s module versions.

go.sum

backstage_controller.goExpand kubebuilder RBAC markers for OpenShift config resources +2/-1

Expand kubebuilder RBAC markers for OpenShift config resources

• Updates kubebuilder RBAC annotations to allow list/watch on ingresses and adds apiservers get/list/watch. These markers feed generated RBAC used by the operator to watch TLS profile changes.

internal/controller/backstage_controller.go

@rhdh-qodo-merge rhdh-qodo-merge Bot added documentation Improvements or additions to documentation enhancement New feature or request labels Jul 21, 2026
@OpinionatedHeron
OpinionatedHeron marked this pull request as draft July 21, 2026 14:28
Signed-off-by: Leanne Ahern <lahern@redhat.com>
Signed-off-by: Leanne Ahern <lahern@redhat.com>
@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 20.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.44%. Comparing base (cde4968) to head (6e9983e).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
internal/controller/mock_client.go 0.00% 2 Missing ⚠️
internal/controller/backstage_controller.go 0.00% 1 Missing ⚠️
internal/controller/plugin-deps.go 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3234      +/-   ##
==========================================
- Coverage   63.49%   63.44%   -0.06%     
==========================================
  Files          38       38              
  Lines        2356     2358       +2     
==========================================
  Hits         1496     1496              
- Misses        711      713       +2     
  Partials      149      149              
Flag Coverage Δ
nightly ?
unittests 63.44% <20.00%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
internal/controller/monitor.go 94.87% <100.00%> (ø)
internal/controller/backstage_controller.go 0.00% <0.00%> (ø)
internal/controller/plugin-deps.go 0.00% <0.00%> (ø)
internal/controller/mock_client.go 36.00% <0.00%> (-0.99%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@openshift-ci

openshift-ci Bot commented Jul 25, 2026

Copy link
Copy Markdown

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progress documentation Improvements or additions to documentation enhancement New feature or request needs-rebase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant