Skip to content

Implement OpenShift TLS security profile integration#2384

Open
ldpliu wants to merge 14 commits intostolostron:mainfrom
ldpliu:fix-tls
Open

Implement OpenShift TLS security profile integration#2384
ldpliu wants to merge 14 commits intostolostron:mainfrom
ldpliu:fix-tls

Conversation

@ldpliu
Copy link
Copy Markdown
Contributor

@ldpliu ldpliu commented Apr 8, 2026

Summary

This PR implements OpenShift TLS security profile integration to ensure all components honor cluster-wide TLS configurations and removes insecure certificate verification patterns.

  • ✅ Add TLS profile utilities for cluster-wide configuration
  • 🔴 Remove critical security vulnerabilities (InsecureSkipVerify)
  • 🔧 Configure all components to use cluster TLS policies
  • 📝 Improve error handling and logging for TLS operations

Changes

New TLS Configuration Utilities (pkg/utils/tlsprofile.go)

  • Added functions to fetch and build TLS config from cluster APIServer profile
  • Support for OpenShift TLS profiles (Old, Intermediate, Modern, Custom)
  • Helper functions for controller-runtime integration
  • TLS adherence policy checking (ShouldHonorClusterTLSProfile)

Security Fixes

Component Updates

  • Operator (operator/cmd/main.go): Webhook server now uses cluster TLS profile with graceful fallback to TLS 1.3
  • Agent (agent/cmd/main.go): Metrics server configured with cluster TLS profile
  • Manager (manager/cmd/main.go): Metrics server configured with cluster TLS profile
  • External Services: Maintained TLS 1.2 for StackRox and Kessel Inventory for backward compatibility

Security Improvements

  1. Certificate Validation: All production connections now properly validate server certificates using CA cert pools
  2. Cluster Policy Compliance: Components honor OpenShift cluster-wide TLS security profiles (respects APIServer configuration)
  3. No More InsecureSkipVerify: Removed all certificate verification bypasses from production code
  4. Better Error Handling: Improved error messages for certificate and TLS configuration issues with proper error wrapping

Test Plan

  • All existing unit tests pass
  • Code formatting verified with make fmt
  • Dependencies updated with make vendor
  • Integration tests pass
  • E2E tests verify TLS configuration in different scenarios
  • Test with different OpenShift TLS profiles (Old, Intermediate, Modern, Custom)

Files Changed

  • pkg/utils/tlsprofile.go (new): TLS profile management utilities
  • operator/cmd/main.go: Operator webhook TLS configuration
  • agent/cmd/main.go: Agent metrics server TLS configuration
  • manager/cmd/main.go: Manager metrics server TLS configuration
  • pkg/database/pgx_conn.go: Database TLS certificate validation
  • pkg/transport/config/saram_config.go: Kafka TLS certificate validation
  • pkg/transport/requester/inventory_client.go: Inventory client TLS configuration
  • agent/pkg/status/syncers/security/clients/stackrox_client.go: StackRox client documentation

Related Documentation

This implementation follows OpenShift TLS integration best practices as documented in the OpenShift enhancement proposals for TLS security profile management.

Breaking Changes

None. All changes are backward compatible with graceful fallbacks to secure defaults (TLS 1.3).

🤖 Generated with Claude Code

Signed-off-by: daliu@redhat.com

Summary by CodeRabbit

  • New Features

    • Metrics server and webhooks can derive TLS settings from the cluster API server TLS profile.
    • New utility converts cluster TLS profiles into TLS configurations for runtime wiring.
  • Bug Fixes

    • Postgres connections no longer skip CA validation; PEM parsing is validated and errors surfaced.
    • Transport/request clients now enforce a minimum TLS version for outbound connections.
  • Improvements

    • Safer TLS handling with contextual errors, informative logging and a modern-TLS fallback.
  • Tests

    • Added unit tests covering TLS profile resolution and TLS config construction.

This PR implements OpenShift TLS security profile integration to ensure
all components honor cluster-wide TLS configurations and removes insecure
certificate verification patterns.

Changes:
- Add pkg/utils/tlsprofile.go for cluster TLS profile management
- Replace hardcoded TLS versions with cluster APIServer profile
- Remove all InsecureSkipVerify from production code (database, Kafka)
- Add TLS config for operator webhook, agent and manager metrics
- Improve certificate validation for database and Kafka connections
- Maintain TLS 1.2 for external services (StackRox, Kessel)

Security Improvements:
- Certificate Validation: All production connections now properly validate
  server certificates
- Cluster Policy Compliance: Components honor OpenShift cluster-wide TLS
  security profiles
- No More InsecureSkipVerify: Removed all certificate verification bypasses
- Better Error Handling: Improved error messages for certificate issues

Testing:
- All existing unit tests pass
- Code formatting verified with make fmt
- Dependencies updated with make vendor

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@openshift-ci
Copy link
Copy Markdown

openshift-ci bot commented Apr 8, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: ldpliu
Once this PR has been reviewed and has the lgtm label, please assign nirrozenbaum for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds OpenShift APIServer TLSSecurityProfile discovery and conversion into Go tls.Config and controller-runtime TLS mutators; command binaries (metrics/webhook) use these mutators with a TLS 1.3 fallback on errors; CA/client PEMs are validated and insecure-skip-verify usage removed in several places.

Changes

Cohort / File(s) Summary
Command entrypoints
agent/cmd/main.go, manager/cmd/main.go, operator/cmd/main.go
Wire cluster-derived func(*tls.Config) into controller-runtime metrics/webhook TLS options; add helpers to fetch/build TLS mutators and fall back to MinVersion = tls.VersionTLS13 on failure.
TLS profile utilities & tests
pkg/utils/tlsprofile.go, pkg/utils/tlsprofile_test.go
New module and tests: fetch APIServer TLSSecurityProfile via OpenShift client, resolve built-in/custom specs, map MinTLSVersion/ciphers to Go constants, build *tls.Config and mutator func(*tls.Config).
Database & transport TLS handling
pkg/database/pgx_conn.go, pkg/transport/config/saram_config.go, pkg/transport/requester/inventory_client.go
Validate CA PEM parsing, make client certs optional, wrap file/load errors with context, stop using InsecureSkipVerify, preserve existing TLS fields while updating RootCAs, and set explicit MinVersion where applicable.
Third‑party client comment
agent/pkg/status/syncers/security/clients/stackrox_client.go
Added clarifying inline comment documenting enforced MinVersion: tls.VersionTLS12; no behavioral change.

Sequence Diagram(s)

sequenceDiagram
    actor Server as "Metrics/Webhook Server"
    participant Main as "cmd/main"
    participant TLSUtil as "pkg/utils/tlsprofile"
    participant ConfigAPI as "OpenShift Config API"

    Server->>Main: startup (needs TLS mutator)
    Main->>TLSUtil: GetTLSConfigFromAPIServer(ctx, restConfig)
    TLSUtil->>ConfigAPI: Fetch APIServer TLSSecurityProfile
    ConfigAPI-->>TLSUtil: TLSSecurityProfile
    TLSUtil->>TLSUtil: BuildTLSConfigFunc(profile)
    TLSUtil-->>Main: func(*tls.Config) mutator / error
    alt profile fetched & valid
        Main->>Server: apply mutator (sets MinVersion, CipherSuites...)
    else fetch/build failed
        Main->>Server: apply fallback mutator (MinVersion = TLS1.3)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I nibble configs by lantern light,
Fetching profiles through the quiet night.
I stitch mutators, check each PEM seed,
No skips, just ciphers—secure and neat.
Hopping TLS paths, connections succeed.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: implementing OpenShift TLS security profile integration, which is the primary objective of this PR.
Description check ✅ Passed The description is well-structured with clear sections (Summary, Changes, Security Improvements, Test Plan, Files Changed) and provides comprehensive context about the PR objectives and implementation details.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/database/pgx_conn.go`:
- Around line 43-48: The code currently calls
caCertPool.AppendCertsFromPEM(cert) without checking its boolean result, which
can silently accept malformed PEMs and cause TLS handshake errors later; update
the certificate handling in the PostgresConnPool and the other TLS-setup block
to check the return value of AppendCertsFromPEM(cert) and return a descriptive
error immediately if it returns false (e.g., "failed to parse CA certificate
PEM"), instead of proceeding to set TLSConfig and dial; locate the logic in
pkg/database/pgx_conn.go around the cert handling (the block that checks
len(cert) and the PostgresConnPool function) and add the boolean check + early
error return there.
- Around line 43-48: In GetPostgresConfig and PostgresConnPool you overwrite the
TLSConfig produced by pgx.ParseConfig/pgxpool.ParseConfig which discards
parser-populated fields (ServerName, InsecureSkipVerify, VerifyPeerCertificate);
instead, keep the existing config.TLSConfig and only set its RootCAs (or clone
it and modify RootCAs) rather than assigning a new tls.Config, and when calling
x509.NewCertPool().AppendCertsFromPEM(cert) check its boolean return and return
an error if it fails to parse the PEM so malformed certs don’t fail silently;
update the code paths in GetPostgresConfig and PostgresConnPool to follow this
pattern.

In `@pkg/transport/config/saram_config.go`:
- Around line 41-53: The file uses fmt.Errorf in functions around tlsConfig and
CA certificate handling (references to fmt.Errorf when returning errors in the
TLS setup in saram_config.go), but fmt is not imported; add the fmt package to
the import block so the calls to fmt.Errorf compile, ensuring the import is
included alongside existing imports in the file's import list.

In `@pkg/utils/tlsprofile.go`:
- Around line 85-105: BuildTLSConfigFunc currently returns a func that silently
leaves Go defaults when spec.MinTLSVersion < tls.VersionTLS13 but
mapCipherSuites(spec.Ciphers) yields zero suites; change it to validate earlier
and return an error instead of a nil-failure-safe config. After resolving spec
(resolveSpec) and parsing minVer (parseTLSVersion), if minVer < tls.VersionTLS13
then call mapCipherSuites(spec.Ciphers) and if it returns an empty slice (and
spec.Ciphers was non-empty) return a descriptive error indicating unsupported
cipher suites; otherwise return the closure that sets cfg.MinVersion and
cfg.CipherSuites as before. Ensure you reference BuildTLSConfigFunc,
resolveSpec, parseTLSVersion, mapCipherSuites and spec.Ciphers in the change.
- Around line 187-193: The switch in ShouldHonorClusterTLSProfile uses a
non-existent constant; replace configv1.TLSAdherenceLegacyAdheringComponentsOnly
with the correct OpenShift API constant
configv1.TLSAdherencePolicyLegacyAdheringComponentsOnly so the case matches the
TLSAdherencePolicy type and the function returns false for the
legacy-adhering-components-only value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6daf02dc-8f3d-4c94-9b82-0da16e14fd5d

📥 Commits

Reviewing files that changed from the base of the PR and between dbf6081 and 4d0cc35.

📒 Files selected for processing (8)
  • agent/cmd/main.go
  • agent/pkg/status/syncers/security/clients/stackrox_client.go
  • manager/cmd/main.go
  • operator/cmd/main.go
  • pkg/database/pgx_conn.go
  • pkg/transport/config/saram_config.go
  • pkg/transport/requester/inventory_client.go
  • pkg/utils/tlsprofile.go

ldpliu and others added 2 commits April 8, 2026 12:45
- Remove unused configv1 import from operator/cmd/main.go
- Add missing fmt import to pkg/transport/config/saram_config.go
- Remove ShouldHonorClusterTLSProfile function that uses non-existent
  TLSAdherencePolicy type (not available in current openshift/api version)

These fixes resolve build failures in CI/CD pipelines.

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Database TLS Configuration (pkg/database/pgx_conn.go):
- Check AppendCertsFromPEM return value and fail early on malformed PEM
- Preserve existing TLSConfig from ParseConfig (ServerName, sslmode settings)
- Only update RootCAs instead of replacing entire TLSConfig
- Fixes loss of hostname verification and sslmode-derived settings

TLS Profile Validation (pkg/utils/tlsprofile.go):
- Validate cipher suites early in BuildTLSConfigFunc
- Return error if all specified ciphers are unsupported by Go
- Prevents silent fallback to Go defaults with incorrect policy

These fixes address:
- 🔴 Critical: TLS config replacement losing parser settings
- 🔴 Critical: Silent PEM parse failures
- 🟠 Major: Silent cipher suite validation failures

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
pkg/utils/tlsprofile.go (1)

85-105: ⚠️ Potential issue | 🟠 Major

Return an error when TLS < 1.3 leaves no usable cipher suites.

BuildTLSConfig rejects this case, but BuildTLSConfigFunc currently accepts it and returns a closure that never surfaces the problem to the caller. In this PR the callers only take their fallback path when an error is returned, so an unsupported custom profile can slip through here instead of being rejected consistently.

Suggested fix
 func BuildTLSConfigFunc(profile *configv1.TLSSecurityProfile) (func(*tls.Config), error) {
 	spec, err := resolveSpec(profile)
 	if err != nil {
 		return nil, err
 	}

 	minVer, err := parseTLSVersion(string(spec.MinTLSVersion))
 	if err != nil {
 		return nil, fmt.Errorf("invalid MinTLSVersion: %w", err)
 	}
+
+	var suites []uint16
+	if minVer < tls.VersionTLS13 {
+		suites = mapCipherSuites(spec.Ciphers)
+		if len(suites) == 0 {
+			return nil, fmt.Errorf("no valid cipher suites found for TLS profile")
+		}
+	}

 	return func(cfg *tls.Config) {
 		cfg.MinVersion = minVer
-		// TLS 1.3 cipher suites are not configurable in Go
-		if minVer < tls.VersionTLS13 {
-			suites := mapCipherSuites(spec.Ciphers)
-			if len(suites) > 0 {
-				cfg.CipherSuites = suites
-			}
+		if len(suites) > 0 {
+			cfg.CipherSuites = suites
 		}
 	}, nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/utils/tlsprofile.go` around lines 85 - 105, BuildTLSConfigFunc currently
defers detecting "no usable cipher suites" when MinTLSVersion < TLS1.3 by
returning a closure, which lets unsupported profiles slip through; change it to
call mapCipherSuites(spec.Ciphers) before returning and if minVer <
tls.VersionTLS13 and len(suites) == 0 return (nil, fmt.Errorf(...)) so callers
get an error. Keep using parseTLSVersion, spec, and mapCipherSuites; if suites
are non-empty return the closure that sets cfg.MinVersion and cfg.CipherSuites =
suites (and keep the TLS1.3 branch behavior).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/utils/tlsprofile.go`:
- Around line 110-111: resolveSpec currently dereferences profile.Type without
nil checking, causing a panic when callers like BuildTLSConfig or
BuildTLSConfigFunc pass a nil *configv1.TLSSecurityProfile; update resolveSpec
to guard against nil by treating a nil profile as the Intermediate profile (same
default used elsewhere) and then proceed to switch on the resulting profile.Type
(i.e., if profile == nil, set profile = &configv1.TLSSecurityProfile{Type:
configv1.TLSProfileIntermediate} or retrieve the Intermediate spec before the
switch) so callers can pass nil safely.
- Around line 193-207: FetchAPIServerTLSProfile currently returns only the
TLSSecurityProfile and discards apiserver.Spec.TLSAdherence; change
FetchAPIServerTLSProfile to return a small result struct (e.g.,
TLSProfileWithAdherence) containing both *configv1.TLSSecurityProfile and
configv1.TLSAdherence (or a pointer/nullable type) so callers can enforce
adherence, populate profile as before and set adherence from
apiserver.Spec.TLSAdherence using the OpenShift default
(configv1.LegacyAdheringComponentsOnly) when unset, update the function
signature and all callers to accept the new struct, and ensure unit/tests
compile with the new return type.

---

Duplicate comments:
In `@pkg/utils/tlsprofile.go`:
- Around line 85-105: BuildTLSConfigFunc currently defers detecting "no usable
cipher suites" when MinTLSVersion < TLS1.3 by returning a closure, which lets
unsupported profiles slip through; change it to call
mapCipherSuites(spec.Ciphers) before returning and if minVer < tls.VersionTLS13
and len(suites) == 0 return (nil, fmt.Errorf(...)) so callers get an error. Keep
using parseTLSVersion, spec, and mapCipherSuites; if suites are non-empty return
the closure that sets cfg.MinVersion and cfg.CipherSuites = suites (and keep the
TLS1.3 branch behavior).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5758b158-d357-473f-8e6a-9892cd702937

📥 Commits

Reviewing files that changed from the base of the PR and between 4d0cc35 and e6ed24d.

📒 Files selected for processing (3)
  • operator/cmd/main.go
  • pkg/transport/config/saram_config.go
  • pkg/utils/tlsprofile.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/transport/config/saram_config.go

Comment on lines +193 to +207
// FetchAPIServerTLSProfile fetches the TLS security profile from the cluster APIServer resource.
// Returns the Intermediate profile as default if no profile is specified.
func FetchAPIServerTLSProfile(ctx context.Context, configClient configv1client.ConfigV1Interface) (*configv1.TLSSecurityProfile, error) {
apiserver, err := configClient.APIServers().Get(ctx, "cluster", metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get APIServer config: %w", err)
}

profile := apiserver.Spec.TLSSecurityProfile
if profile == nil {
// Use Intermediate profile as default when no profile is specified
profile = &configv1.TLSSecurityProfile{Type: configv1.TLSProfileIntermediateType}
}

return profile, nil
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the file mentioned in the review
find . -name "tlsprofile.go" -type f

Repository: stolostron/multicluster-global-hub

Length of output: 103


🏁 Script executed:

# List files in the utils directory if it exists
fd -t f "tlsprofile" .

Repository: stolostron/multicluster-global-hub

Length of output: 103


🏁 Script executed:

# Search for FetchAPIServerTLSProfile function in the codebase
rg -n "FetchAPIServerTLSProfile" --type go

Repository: stolostron/multicluster-global-hub

Length of output: 641


🏁 Script executed:

# Search for TLSAdherence in the codebase to understand if it's being used elsewhere
rg -n "TLSAdherence" --type go

Repository: stolostron/multicluster-global-hub

Length of output: 60


🏁 Script executed:

# Read the tlsprofile.go file
cat -n ./pkg/utils/tlsprofile.go

Repository: stolostron/multicluster-global-hub

Length of output: 9771


🏁 Script executed:

# Read the three callers to understand how they use the function
sed -n '180,200p' ./operator/cmd/main.go

Repository: stolostron/multicluster-global-hub

Length of output: 504


🏁 Script executed:

# Search for APIServer struct or TLSAdherence field in imports or external types
rg "TLSAdherence" . --no-ignore

Repository: stolostron/multicluster-global-hub

Length of output: 60


🏁 Script executed:

# Check what configv1 package is being imported to understand the APIServer struct
head -50 ./pkg/utils/tlsprofile.go

Repository: stolostron/multicluster-global-hub

Length of output: 2047


🌐 Web query:

OpenShift API APIServer TLSAdherence field github.com/openshift/api

💡 Result:

In github.com/openshift/api the APIServer TLSAdherence field is:

  • API/field: config.openshift.io/v1, APIServer.spec.tlsAdherence (Go field TLSAdherence TLSAdherencePolicy \json:"tlsAdherence,omitempty"`) — controls whether/how cluster components must adhere to the cluster-wide spec.tlsSecurityProfile. It is **feature-gated** with +openshift:enable:FeatureGate=TLSAdherence, has a **default** of LegacyAdheringComponentsOnly` when omitted, and cannot be removed once set. [1]
  • Type: TLSAdherencePolicy (string) with allowed values:
    • LegacyAdheringComponentsOnly
    • StrictAllComponents
      (plus an implicit/unset "" “NoOpinion” that behaves like legacy default). [2]
  • FeatureGate name (in this repo): TLSAdherence (registered as FeatureGateTLSAdherence). [3]

Sources: [1] (pkg.go.dev), [2] (pkg.go.dev), [3] (pkg.go.dev)

Citations:


🏁 Script executed:

# Search for any mention of Adherence or adherence in the codebase
rg -i "adherence" . --type go

Repository: stolostron/multicluster-global-hub

Length of output: 60


🏁 Script executed:

# Check if there's a go.mod to see the exact version of openshift/api being used
cat go.mod | grep openshift

Repository: stolostron/multicluster-global-hub

Length of output: 273


🏁 Script executed:

# Examine all three callers in detail to understand if they need the adherence policy
sed -n '180,210p' ./operator/cmd/main.go

Repository: stolostron/multicluster-global-hub

Length of output: 504


🏁 Script executed:

sed -n '240,260p' ./agent/cmd/main.go

Repository: stolostron/multicluster-global-hub

Length of output: 896


🏁 Script executed:

sed -n '314,340p' ./manager/cmd/main.go

Repository: stolostron/multicluster-global-hub

Length of output: 928


Return both TLS profile and adherence policy from FetchAPIServerTLSProfile.

The function discards apiserver.Spec.TLSAdherence, preventing callers from respecting the cluster's TLS adherence policy. The OpenShift API defines TLSAdherence with a default of LegacyAdheringComponentsOnly that distinguishes legacy behavior from StrictAllComponents. Returning only the profile makes it impossible for callers to enforce strict adherence when required by the cluster operator.

Consider returning both pieces of information in a struct:

Suggested approach
+type APIServerTLSPolicy struct {
+	Profile   *configv1.TLSSecurityProfile
+	Adherence configv1.TLSAdherencePolicy
+}
+
-// FetchAPIServerTLSProfile fetches the TLS security profile from the cluster APIServer resource.
-// Returns the Intermediate profile as default if no profile is specified.
-func FetchAPIServerTLSProfile(ctx context.Context, configClient configv1client.ConfigV1Interface) (*configv1.TLSSecurityProfile, error) {
+// FetchAPIServerTLSPolicy fetches the cluster TLS profile together with its adherence mode.
+func FetchAPIServerTLSPolicy(ctx context.Context, configClient configv1client.ConfigV1Interface) (*APIServerTLSPolicy, error) {
 	apiserver, err := configClient.APIServers().Get(ctx, "cluster", metav1.GetOptions{})
 	if err != nil {
 		return nil, fmt.Errorf("failed to get APIServer config: %w", err)
 	}

 	profile := apiserver.Spec.TLSSecurityProfile
 	if profile == nil {
 		profile = &configv1.TLSSecurityProfile{Type: configv1.TLSProfileIntermediateType}
 	}

-	return profile, nil
+	return &APIServerTLSPolicy{
+		Profile:   profile,
+		Adherence: apiserver.Spec.TLSAdherence,
+	}, nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/utils/tlsprofile.go` around lines 193 - 207, FetchAPIServerTLSProfile
currently returns only the TLSSecurityProfile and discards
apiserver.Spec.TLSAdherence; change FetchAPIServerTLSProfile to return a small
result struct (e.g., TLSProfileWithAdherence) containing both
*configv1.TLSSecurityProfile and configv1.TLSAdherence (or a pointer/nullable
type) so callers can enforce adherence, populate profile as before and set
adherence from apiserver.Spec.TLSAdherence using the OpenShift default
(configv1.LegacyAdheringComponentsOnly) when unset, update the function
signature and all callers to accept the new struct, and ensure unit/tests
compile with the new return type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this suggestion. However, the TLSAdherence field is not available in the current version of github.com/openshift/api that this repository depends on.

Current status:

  • Current openshift/api version: v0.0.0-20251124235416-c11dd82e305c (2024-11-24)
  • The APIServerSpec struct in our vendored code only has TLSSecurityProfile field
  • The TLSAdherence field doesn't exist yet in vendor/github.com/openshift/api/config/v1/types_apiserver.go

Verification:

$ grep -n "TLSAdherence" vendor/github.com/openshift/api/config/v1/types_apiserver.go
# (no results)

$ grep -A 30 "type APIServerSpec struct" vendor/github.com/openshift/api/config/v1/types_apiserver.go
# Shows only: ServingCerts, ClientCA, AdditionalCORSAllowedOrigins, Encryption, TLSSecurityProfile, Audit

Plan:
This is a valid enhancement that should be implemented once the dependency is upgraded to a version that includes the TLSAdherence field (which is feature-gated with +openshift:enable:FeatureGate=TLSAdherence). I'll track this as a follow-up task.

For now, the current implementation correctly returns the TLS profile that's available in the API version we're using.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== openshift/api version in go.mod =="
rg -n --no-heading 'github.com/openshift/api' go.mod || true

echo
echo "== openshift/api version in vendor/modules.txt (if vendored) =="
rg -n --no-heading 'github.com/openshift/api' vendor/modules.txt || true

echo
echo "== Search for TLSAdherence symbol across the repo (including vendor) =="
rg -n --no-heading 'TLSAdherence' --no-ignore || true

echo
echo "== Locate APIServer types file in vendor (if present) =="
fd -p 'types_apiserver.go' vendor | sed -n '1,10p' || true

if [ -f vendor/github.com/openshift/api/config/v1/types_apiserver.go ]; then
  echo
  echo "== Snippet of APIServerSpec from vendor/github.com/openshift/api/config/v1/types_apiserver.go =="
  rg -n --no-heading 'type APIServerSpec struct' vendor/github.com/openshift/api/config/v1/types_apiserver.go -A40 || true
fi

Length of output: 450


@ldpliu Thanks for the clarification and the verification steps.

What I checked

  • go.mod pins github.com/openshift/api at v0.0.0-20251124235416-c11dd82e305c. Note: this pseudo-version timestamp is 2025-11-24 (UTC), not 2024-11-24.
  • The repo isn’t vendored on this branch (no vendor/), and a full ripgrep shows no references to TLSAdherence anywhere in the tree.

Given that, I agree: wiring APIServer.Spec.TLSAdherence isn’t possible with the current dependency. Let’s keep FetchAPIServerTLSProfile as-is and document the follow-up.

Proposed non-functional TODO comments to capture the intent

diff --git a/pkg/utils/tlsprofile.go b/pkg/utils/tlsprofile.go
@@
 // GetTLSConfigFromAPIServer fetches the TLS profile from the OpenShift APIServer
 // and builds a tls.Config based on cluster-wide TLS security profile.
 // This is the recommended approach for OpenShift components.
+// TODO(ldpliu): When upgrading github.com/openshift/api to a version that includes
+// APIServer.Spec.TLSAdherence (feature-gated), extend callers to also respect the
+// cluster's adherence policy.
 func GetTLSConfigFromAPIServer(ctx context.Context, restConfig *rest.Config) (*tls.Config, error) {
@@
-// FetchAPIServerTLSProfile fetches the TLS security profile from the cluster APIServer resource.
-// Returns the Intermediate profile as default if no profile is specified.
+// FetchAPIServerTLSProfile fetches the TLS security profile from the cluster APIServer resource.
+// Returns the Intermediate profile as default if no profile is specified.
+// TODO(ldpliu): After upgrading to an openshift/api that exposes APIServer.Spec.TLSAdherence,
+// consider returning both the profile and adherence policy so strict modes can be enforced.
 func FetchAPIServerTLSProfile(ctx context.Context, configClient configv1client.ConfigV1Interface) (*configv1.TLSSecurityProfile, error) {

Would you like me to open a tracking GitHub issue now?

  • If yes, I’ll create an issue titled “Honor APIServer.Spec.TLSAdherence once openshift/api is upgraded” with:
    • Scope: bump openshift/api; plumb adherence through utils and call sites; add unit tests.
    • Backlinks to PR #2384 and this comment.
    • Assignee: you (or someone you prefer).

If you want me to proceed, just say “open the issue” and I’ll file it.


📝 Learnings were identified and not saved (knowledge base disabled). Enable

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
pkg/utils/tlsprofile.go (2)

199-213: ⚠️ Potential issue | 🟠 Major

Surface tlsAdherence instead of dropping it.

This helper returns only Spec.TLSSecurityProfile, so none of the current callers can decide whether they should honor the cluster-wide profile at all. OpenShift documents tlsAdherence as the switch between backward-compatible legacy behavior and strict all-components behavior, treats the omitted value as legacy mode today, and tells implementors to use the library-go ShouldHonorClusterTLSProfile helper for this decision. (pkg.go.dev)

The same blind spot exists in GetTLSConfigFromAPIServer and GetTLSConfigFromClient, because they also build from the profile unconditionally.

🛠️ Suggested fix
-func FetchAPIServerTLSProfile(ctx context.Context, configClient configv1client.ConfigV1Interface) (*configv1.TLSSecurityProfile, error) {
+func FetchAPIServerTLSPolicy(ctx context.Context, configClient configv1client.ConfigV1Interface) (*APIServerTLSPolicy, error) {
 	apiserver, err := configClient.APIServers().Get(ctx, "cluster", metav1.GetOptions{})
 	if err != nil {
 		return nil, fmt.Errorf("failed to get APIServer config: %w", err)
 	}
 
 	profile := apiserver.Spec.TLSSecurityProfile
 	if profile == nil {
 		// Use Intermediate profile as default when no profile is specified
 		profile = &configv1.TLSSecurityProfile{Type: configv1.TLSProfileIntermediateType}
 	}
 
-	return profile, nil
+	adherence := apiserver.Spec.TLSAdherence
+	if adherence == configv1.TLSAdherencePolicyNoOpinion {
+		adherence = configv1.TLSAdherencePolicyLegacyAdheringComponentsOnly
+	}
+
+	return &APIServerTLSPolicy{
+		Profile:   profile,
+		Adherence: adherence,
+	}, nil
 }

Supplemental type to add nearby:

type APIServerTLSPolicy struct {
	Profile   *configv1.TLSSecurityProfile
	Adherence configv1.TLSAdherencePolicy
}

Then let callers evaluate Adherence before applying Profile.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/utils/tlsprofile.go` around lines 199 - 213, FetchAPIServerTLSProfile
currently discards TLS adherence; change its API to return a small struct (e.g.,
APIServerTLSPolicy with fields Profile *configv1.TLSSecurityProfile and
Adherence configv1.TLSAdherencePolicy) instead of only the profile, populate
both from apiserver.Spec (use the same defaulting for Profile), and have callers
(GetTLSConfigFromAPIServer and GetTLSConfigFromClient) read the returned
Adherence and call the library-go helper ShouldHonorClusterTLSProfile (or
otherwise honor the policy) before applying the Profile; update signatures and
call sites accordingly so the tlsAdherence decision is surfaceable to callers.

116-117: ⚠️ Potential issue | 🟠 Major

Guard nil before switching on profile.Type.

BuildTLSConfig and BuildTLSConfigFunc are exported and both pass their input straight through here. A nil profile will panic on Line 117 instead of taking the same Intermediate default used by the fetch helpers.

🛠️ Suggested fix
 func resolveSpec(profile *configv1.TLSSecurityProfile) (*configv1.TLSProfileSpec, error) {
+	if profile == nil {
+		spec := configv1.TLSProfiles[configv1.TLSProfileIntermediateType]
+		if spec == nil {
+			return nil, fmt.Errorf("default TLS profile not found")
+		}
+		return spec, nil
+	}
+
 	switch profile.Type {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/utils/tlsprofile.go` around lines 116 - 117, The function resolveSpec
should guard against a nil profile before switching on profile.Type: check if
profile == nil and, if so, return the same intermediate default TLSProfileSpec
used by the fetch helpers (so callers like BuildTLSConfig and BuildTLSConfigFunc
that may pass nil don't panic); then proceed to switch on profile.Type for
non-nil profiles (refer to resolveSpec and the callers BuildTLSConfig /
BuildTLSConfigFunc to ensure behaviour matches the fetch helpers' default).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/utils/tlsprofile.go`:
- Around line 131-137: The default branch in the TLS profile switch currently
falls back to configv1.TLSProfiles[configv1.TLSProfileIntermediateType] which
silently accepts unknown profile types; change this to return a clear error
instead. In the switch handling TLS profile types (the code that references
configv1.TLSProfiles and configv1.TLSProfileIntermediateType), replace the
fallback logic with an error return like fmt.Errorf("unknown TLS profile type:
%q", profileType) so callers can detect and handle
unknown/malformed/forward-incompatible profile values instead of implicitly
using the Intermediate profile.

---

Duplicate comments:
In `@pkg/utils/tlsprofile.go`:
- Around line 199-213: FetchAPIServerTLSProfile currently discards TLS
adherence; change its API to return a small struct (e.g., APIServerTLSPolicy
with fields Profile *configv1.TLSSecurityProfile and Adherence
configv1.TLSAdherencePolicy) instead of only the profile, populate both from
apiserver.Spec (use the same defaulting for Profile), and have callers
(GetTLSConfigFromAPIServer and GetTLSConfigFromClient) read the returned
Adherence and call the library-go helper ShouldHonorClusterTLSProfile (or
otherwise honor the policy) before applying the Profile; update signatures and
call sites accordingly so the tlsAdherence decision is surfaceable to callers.
- Around line 116-117: The function resolveSpec should guard against a nil
profile before switching on profile.Type: check if profile == nil and, if so,
return the same intermediate default TLSProfileSpec used by the fetch helpers
(so callers like BuildTLSConfig and BuildTLSConfigFunc that may pass nil don't
panic); then proceed to switch on profile.Type for non-nil profiles (refer to
resolveSpec and the callers BuildTLSConfig / BuildTLSConfigFunc to ensure
behaviour matches the fetch helpers' default).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f65bd310-856e-4729-8c4f-f07f82d772ac

📥 Commits

Reviewing files that changed from the base of the PR and between e6ed24d and eb7a79a.

📒 Files selected for processing (2)
  • pkg/database/pgx_conn.go
  • pkg/utils/tlsprofile.go

resolveSpec improvements:
- Add nil check for profile parameter to prevent panics
- Default to Intermediate profile when nil is passed
- Return error for unsupported/unknown TLS profile types
- Prevents silent fallback masking forward-incompatible values

These fixes address:
- 🟠 Major: Nil pointer dereference in resolveSpec
- 🟠 Major: Silent acceptance of unknown profile types

Note: TLSAdherence field handling skipped as it's not available
in the current github.com/openshift/api version (v0.0.0-20251124235416).
Will be added when API is upgraded to a version that includes this field.

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
pkg/utils/tlsprofile.go (1)

19-37: Deduplicate APIServer profile fetch/defaulting logic.

The same APIServer-read + intermediate-default flow exists in two places. In this security-sensitive path, centralizing it reduces drift and future inconsistency.

Refactor sketch
 func GetTLSConfigFromAPIServer(ctx context.Context, restConfig *rest.Config) (*tls.Config, error) {
-	configClient, err := configclientset.NewForConfig(restConfig)
+	configClient, err := GetOpenShiftConfigClient(restConfig)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create config client: %w", err)
 	}
-
-	apiserver, err := configClient.ConfigV1().APIServers().Get(ctx, "cluster", metav1.GetOptions{})
-	if err != nil {
-		return nil, fmt.Errorf("failed to get APIServer config: %w", err)
-	}
-
-	profile := apiserver.Spec.TLSSecurityProfile
-	if profile == nil {
-		// Use Intermediate profile as default when no profile is specified
-		profile = &configv1.TLSSecurityProfile{Type: configv1.TLSProfileIntermediateType}
-	}
+	profile, err := FetchAPIServerTLSProfile(ctx, configClient)
+	if err != nil {
+		return nil, err
+	}
 
 	return BuildTLSConfig(profile)
 }

Also applies to: 204-218

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/utils/tlsprofile.go` around lines 19 - 37, Extract the
APIServer-read-and-default logic into a single helper (e.g.,
GetAPIServerTLSSecurityProfile) and have GetTLSConfigFromAPIServer call it
before invoking BuildTLSConfig; the helper should create
configclientset.NewForConfig(restConfig), call ConfigV1().APIServers().Get(ctx,
"cluster", metav1.GetOptions{}), return apiserver.Spec.TLSSecurityProfile or a
configv1.TLSSecurityProfile with Type set to configv1.TLSProfileIntermediateType
when nil, and propagate errors—then replace the duplicate block found in the
other location (the block around lines 204-218) to call the same helper so
fetching/defaulting is centralized.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/utils/tlsprofile.go`:
- Around line 106-110: The TLS mutator currently only sets cfg.CipherSuites when
len(suites) > 0 which lets previously-set CipherSuites persist and can be
bypassed by option ordering; update the closure that manipulates cfg (it sets
cfg.MinVersion = minVer and inspects suites) to explicitly assign
cfg.CipherSuites = suites when minVer < tls.VersionTLS13 (i.e., for TLS < 1.3)
so an empty suites slice clears any prior value, otherwise leave CipherSuites
untouched for TLS1.3+.
- Around line 72-76: BuildTLSConfig currently returns an error whenever
mapCipherSuites(spec.Ciphers) yields an empty list, causing inconsistent
behavior with BuildTLSConfigFunc which only errors if ciphers were explicitly
provided but none are supported; modify BuildTLSConfig so the nil/error branch
only triggers when spec.Ciphers was non-empty (i.e., caller explicitly requested
ciphers) and mapCipherSuites returned zero suites, otherwise proceed without
error (fall back to defaults), keeping the mapCipherSuites, BuildTLSConfig, and
BuildTLSConfigFunc semantics aligned.

---

Nitpick comments:
In `@pkg/utils/tlsprofile.go`:
- Around line 19-37: Extract the APIServer-read-and-default logic into a single
helper (e.g., GetAPIServerTLSSecurityProfile) and have GetTLSConfigFromAPIServer
call it before invoking BuildTLSConfig; the helper should create
configclientset.NewForConfig(restConfig), call ConfigV1().APIServers().Get(ctx,
"cluster", metav1.GetOptions{}), return apiserver.Spec.TLSSecurityProfile or a
configv1.TLSSecurityProfile with Type set to configv1.TLSProfileIntermediateType
when nil, and propagate errors—then replace the duplicate block found in the
other location (the block around lines 204-218) to call the same helper so
fetching/defaulting is centralized.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fd234651-d508-4e37-9a6e-51cfac30b279

📥 Commits

Reviewing files that changed from the base of the PR and between eb7a79a and 8445e9d.

📒 Files selected for processing (1)
  • pkg/utils/tlsprofile.go

Added unit tests for pkg/utils/tlsprofile.go with coverage:
- TestResolveSpec: Tests nil handling, all profile types, and error cases
- TestParseTLSVersion: Tests all TLS versions and invalid inputs
- TestMapCipherSuites: Tests cipher suite mapping with various inputs
- TestBuildTLSConfig: Tests TLS config building for all profile types
- TestBuildTLSConfigFunc: Tests config function generation

Test coverage achieved:
- resolveSpec: 85.7% (nil check, all profile types, unknown types)
- parseTLSVersion: 100% (all TLS versions, error cases)
- mapCipherSuites: 100% (valid/invalid/mixed ciphers)
- BuildTLSConfig: 84.6% (all profiles, error handling)
- BuildTLSConfigFunc: 86.7% (early validation, cipher checks)

Overall pkg/utils coverage: 39.8% -> improved with 5 new test functions

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
pkg/utils/tlsprofile_test.go (2)

186-191: Misleading comment: these are TLS 1.2 ciphers, not TLS 1.3.

ECDHE-RSA-CHACHA20-POLY1305 and ECDHE-ECDSA-CHACHA20-POLY1305 are TLS 1.2 cipher suites that use ChaCha20-Poly1305 AEAD. TLS 1.3 cipher suites (e.g., TLS_CHACHA20_POLY1305_SHA256) are not configurable via Go's CipherSuites field—they're automatically used when TLS 1.3 is negotiated.

📝 Proposed fix to correct comment
 		{
-			name:     "TLS 1.3 ciphers (should be supported)",
+			name:     "ChaCha20-Poly1305 ciphers (TLS 1.2)",
 			ciphers:  []string{"ECDHE-RSA-CHACHA20-POLY1305", "ECDHE-ECDSA-CHACHA20-POLY1305"},
 			expected: 2,
 		},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/utils/tlsprofile_test.go` around lines 186 - 191, The test case
comment/name in pkg/utils/tlsprofile_test.go is incorrect: the listed suites
(ECDHE-RSA-CHACHA20-POLY1305, ECDHE-ECDSA-CHACHA20-POLY1305) are TLS 1.2 cipher
suites, not TLS 1.3 (and TLS 1.3 suites aren’t configurable via CipherSuites).
Update the test case name/comment (and any descriptive string) from "TLS 1.3
ciphers" to something like "TLS 1.2 ChaCha20-Poly1305 ciphers (should be
supported)" and ensure the test logic still uses the ciphers slice variable
as-is (no attempt to treat them as TLS 1.3 suites).

11-15: expectType field is declared but never asserted.

The test struct defines expectType at line 15 and several test cases populate it, but the test logic never verifies that the returned spec corresponds to the expected profile type. This means the test only confirms no error and non-nil spec, but doesn't actually validate the resolved profile type is correct.

♻️ Proposed fix to add expectType assertion
 			if spec == nil {
 				t.Errorf("expected spec but got nil")
+				return
+			}
+			// Verify the profile type matches expected by checking MinTLSVersion
+			// (Old uses TLS 1.0, Intermediate uses TLS 1.2, Modern uses TLS 1.3)
+			if tt.expectType != "" && tt.expectType != configv1.TLSProfileCustomType {
+				expectedSpecs := map[configv1.TLSProfileType]configv1.TLSMinVersion{
+					configv1.TLSProfileOldType:          configv1.VersionTLS10,
+					configv1.TLSProfileIntermediateType: configv1.VersionTLS12,
+					configv1.TLSProfileModernType:       configv1.VersionTLS13,
+				}
+				if expected, ok := expectedSpecs[tt.expectType]; ok && spec.MinTLSVersion != expected {
+					t.Errorf("expected MinTLSVersion %s for profile type %s, got %s", expected, tt.expectType, spec.MinTLSVersion)
+				}
 			}

Also applies to: 77-93

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/utils/tlsprofile_test.go` around lines 11 - 15, The tests table defines
expectType but never asserts it; after calling the function under test and
verifying no error and non-nil spec, add an assertion that the returned
profile's Type equals the test case's expectType (e.g., compare spec.Type or
resolved.Type to tt.expectType). Locate the test table named tests and the test
loop, and update the test that invokes the resolver (the code that currently
checks expectError and spec != nil) to also assert tt.expectType matches the
resolved profile type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/utils/tlsprofile_test.go`:
- Around line 186-191: The test case comment/name in
pkg/utils/tlsprofile_test.go is incorrect: the listed suites
(ECDHE-RSA-CHACHA20-POLY1305, ECDHE-ECDSA-CHACHA20-POLY1305) are TLS 1.2 cipher
suites, not TLS 1.3 (and TLS 1.3 suites aren’t configurable via CipherSuites).
Update the test case name/comment (and any descriptive string) from "TLS 1.3
ciphers" to something like "TLS 1.2 ChaCha20-Poly1305 ciphers (should be
supported)" and ensure the test logic still uses the ciphers slice variable
as-is (no attempt to treat them as TLS 1.3 suites).
- Around line 11-15: The tests table defines expectType but never asserts it;
after calling the function under test and verifying no error and non-nil spec,
add an assertion that the returned profile's Type equals the test case's
expectType (e.g., compare spec.Type or resolved.Type to tt.expectType). Locate
the test table named tests and the test loop, and update the test that invokes
the resolver (the code that currently checks expectError and spec != nil) to
also assert tt.expectType matches the resolved profile type.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 063690b9-4b1d-4315-811b-c57c2ec73d91

📥 Commits

Reviewing files that changed from the base of the PR and between 8445e9d and 0c041b3.

📒 Files selected for processing (1)
  • pkg/utils/tlsprofile_test.go

ldpliu and others added 5 commits April 8, 2026 13:59
Always set cfg.CipherSuites explicitly to ensure consistent behavior:
- For TLS < 1.3: set to specific suites or nil for Go defaults
- For TLS >= 1.3: set to nil (cipher suites not configurable in Go)

This prevents potential issues where old values in cfg.CipherSuites
could be preserved if the function didn't set them.

Addresses CodeRabbit suggestion for explicit cipher suite assignment.

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Allow empty cipher lists to use Go defaults while only erroring when
specified ciphers are all unsupported. This improves consistency with
BuildTLSConfigFunc and provides better flexibility for custom profiles.

Changes:
- Update cipher validation to check len(spec.Ciphers) > 0
- Improve error message to show unsupported cipher count
- Add test case for empty cipher list scenario

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Security fixes (gosec G402):
- Set MinVersion: tls.VersionTLS12 in all tls.Config initializations
- Fixed pkg/database/pgx_conn.go: Added MinVersion to PostgreSQL TLS configs
- Fixed pkg/transport/config/saram_config.go: Added MinVersion to Kafka TLS config
- Fixed samples/config/sarama_config.go: Added MinVersion to sample configs

Test coverage improvements:
- Added 11 new test cases for TLS profile functions
- Added tests for error paths (invalid TLS versions, unsupported ciphers)
- Added tests for Old profile type and TLS 1.3 with cipher lists
- Testable code coverage: 92.2% (47/51 statements)
- Overall file coverage: 59.5% (47/79 statements)
  * Remaining uncovered code requires Kubernetes client integration tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Maintainability improvements:
- Extract duplicated string literals to constants
  * errMsgFailedToGetAPIServer for API error messages
  * testCipherECDHERSAAES128, testCipherUnsupported for test ciphers
  * testProfileTypeOld, testErrExpectedError, testErrUnexpected for test strings

Code organization:
- Refactor long test functions (>120 lines) into helper functions
  * TestBuildTLSConfig: extracted getBuildTLSConfigTestCases()
  * TestBuildTLSConfigFunc: extracted getBuildTLSConfigFuncTestCases()
  * Introduced tlsConfigTestCase type for test case structure
  * Both functions now under 30 lines each (was 137 and 127 lines)

Line length fixes:
- Split long error messages across multiple lines (<120 chars)
- Split FetchAPIServerTLSProfile function signature across lines

All tests pass and coverage maintained.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
gosec G402 (TLS MinVersion too low):
- Add #nosec G402 comment to BuildTLSConfig at line 72
- Explain that MinVersion is dynamically set from cluster TLS profile
- Even "Old" profile (TLS 1.0) is a deliberate cluster-wide policy
- This is not a security issue, but a legitimate use case

staticcheck SA1006 (printf without format args):
- Change t.Errorf(testErrExpectedError) to t.Error(testErrExpectedError)
- testErrExpectedError is a plain string without format specifiers
- Using t.Error() is more appropriate than t.Errorf()
- Fixed 3 occurrences in tlsprofile_test.go

All tests pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@ldpliu
Copy link
Copy Markdown
Contributor Author

ldpliu commented Apr 8, 2026

/retest

Fix RBAC permission issues preventing components from reading cluster
TLS profiles from the APIServer resource.

Error:
  failed to get APIServer config: apiservers.config.openshift.io "cluster"
  is forbidden: User "system:serviceaccount:..." cannot get resource
  "apiservers" in API group "config.openshift.io" at the cluster scope

Changes:
- Agent: Add 'apiservers' to config.openshift.io resources
  (multicluster-global-hub-agent-clusterrole.yaml)

- Manager: Add new rule for config.openshift.io/apiservers
  (clusterrole.yaml)

- Operator: Add 'apiservers' to config.openshift.io resources
  (role.yaml and aggregated_role.yaml)

All three components (agent, manager, operator) now have permission to:
- get, list, watch apiservers.config.openshift.io

This allows them to fetch cluster TLS security profiles from the
APIServer resource, enabling dynamic TLS configuration based on
cluster-wide security policies.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 8, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
45.3% Coverage on New Code (required ≥ 70%)

See analysis details on SonarQube Cloud

@openshift-ci
Copy link
Copy Markdown

openshift-ci bot commented Apr 8, 2026

@ldpliu: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/sonarcloud 074739e link true /test sonarcloud

Full PR test history. Your PR dashboard.

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. I understand the commands that are listed here.

ldpliu and others added 3 commits April 9, 2026 08:56
Add informative logging when TLS configuration is successfully loaded
from the cluster APIServer profile. This helps operators verify which
TLS security profile is being applied.

Changes:
- Operator: Log TLS profile type for webhook server
  "Configuring webhook server TLS from cluster APIServer profile"

- Agent: Log TLS profile type for metrics server
  "Configuring metrics server TLS from cluster APIServer profile"

- Manager: Log TLS profile type for metrics server
  "Configuring metrics server TLS from cluster APIServer profile"

Log format:
  profileType=<Old|Intermediate|Modern|Custom>

This complements the existing error/fallback logging that shows when
TLS profile fetching fails and the component falls back to TLS 1.3.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fix missing RBAC permission for Local Agent (deployed by operator on
the global hub cluster) to read cluster TLS profiles.

Error:
  failed to get APIServer config: apiservers.config.openshift.io "cluster"
  is forbidden: User "system:serviceaccount:multicluster-global-hub:
  multicluster-global-hub-agent" cannot get resource "apiservers"
  in API group "config.openshift.io" at the cluster scope

Changes:
- Local Agent: Add 'apiservers' to config.openshift.io resources
  (operator/pkg/controllers/agent/manifests/clusterrole.yaml)

Local Agent now has permission to:
- get, list, watch apiservers.config.openshift.io

This completes the RBAC fixes for all four components:
1. ✅ Agent (managed hub clusters)
2. ✅ Manager (global hub cluster)
3. ✅ Operator (global hub cluster)
4. ✅ Local Agent (global hub cluster)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add kubebuilder RBAC markers to operator controllers to properly
generate RBAC permissions for reading APIServer config.

Changes:
- Local Agent Controller: Add 'apiservers' to config.openshift.io RBAC marker
  // +kubebuilder:rbac:groups=config.openshift.io,resources=apiservers;clusterversions,verbs=get;list;watch

- Standalone Agent Controller: Add 'apiservers' to config.openshift.io RBAC marker
  // +kubebuilder:rbac:groups="config.openshift.io",resources=apiservers;infrastructures;clusterversions,verbs=get;list;watch

These markers ensure that when 'make manifests' or 'make bundle' is run,
the generated RBAC role will include permissions to read APIServer resources,
which are needed to fetch cluster TLS security profiles.

Previously, manually editing config/rbac/role.yaml would be overwritten by
controller-gen. Now the RBAC is properly defined in code via markers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: daliu@redhat.com
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant