Skip to content

OCPBUGS-81675: pkg/cvo/egress: trust user-ca-bundle in HyperShift mode#1370

Open
dpateriya wants to merge 4 commits intoopenshift:mainfrom
dpateriya:hypershift-trust-user-ca-bundle
Open

OCPBUGS-81675: pkg/cvo/egress: trust user-ca-bundle in HyperShift mode#1370
dpateriya wants to merge 4 commits intoopenshift:mainfrom
dpateriya:hypershift-trust-user-ca-bundle

Conversation

@dpateriya
Copy link
Copy Markdown

@dpateriya dpateriya commented Apr 10, 2026

Problem

In a HyperShift hosted cluster, HostedCluster.spec.additionalTrustBundle is synced by the Hosted Cluster Config Operator (HCCO) into openshift-config/user-ca-bundle. However, that ConfigMap is not automatically merged into openshift-config-managed/trusted-ca-bundle — which is the only source getTLSConfig() currently reads.

As a result, when spec.updateService points to an update service whose TLS certificate is signed by an internal CA provided exclusively via additionalTrustBundle, the CVO Cincinnati client fails with:

x509: certificate signed by unknown authority

This affects customers running an internal Cincinnati-compatible update service (e.g. OpenShift Update Service / OSUS) behind a wildcard certificate signed by an internal CA.

Fix

When running with --hypershift, also read openshift-config/user-ca-bundle (via the already-wired cmConfigLister) and merge its contents into the cert pool alongside trusted-ca-bundle.

The new code path is strictly gated on optr.hypershift, so standard OCP clusters are completely unaffected.

Both listers (cmConfigLister for openshift-config and cmConfigManagedLister for openshift-config-managed) are already wired in cvo.New() — no new informers or flags are required.

Changes

  • pkg/cvo/egress.go: extend getTLSConfig() to merge openshift-config/user-ca-bundle into the cert pool when --hypershift is set.
  • pkg/cvo/egress_test.go: new TestGetTLSConfig with 8 table-driven cases covering all combinations of bundle presence and the hypershift flag.

Testing

go test ./pkg/cvo/ -run TestGetTLSConfig -v

All 8 cases pass:

  • non-hypershift, no bundles → nil config
  • non-hypershift, trusted-ca-bundle only → CA trusted
  • non-hypershift, user-ca-bundle present but hypershift=false → ignored
  • non-hypershift, both present but hypershift=false → only trusted-ca-bundle used
  • hypershift, no bundles → nil config
  • hypershift, trusted-ca-bundle only → CA trusted
  • hypershift, user-ca-bundle only → CA trusted
  • hypershift, both bundles → both CAs merged and trusted

Summary by CodeRabbit

  • New Features

    • HyperShift-aware certificate bundle handling to include user CA bundles when applicable.
  • Bug Fixes

    • More robust certificate loading that augments system trust with custom CAs and clearer error reporting for managed/user bundle failures.
    • Return nil when no CA material is available to avoid using an empty cert pool.
  • Tests

    • Added table-driven tests covering HyperShift mode, presence/absence of managed and user bundles, and error scenarios.

In a HyperShift hosted cluster, HostedCluster.spec.additionalTrustBundle
is synced by HCCO into openshift-config/user-ca-bundle, but that
ConfigMap is never merged into openshift-config-managed/trusted-ca-bundle
(which is the only source getTLSConfig() currently reads).

As a result, when spec.updateService points to an update service whose
TLS certificate is signed by an internal CA provided exclusively via
additionalTrustBundle, CVO's Cincinnati client fails with:

  x509: certificate signed by unknown authority

Fix: when running with --hypershift, also read
openshift-config/user-ca-bundle via the already-wired cmConfigLister and
merge it into the cert pool alongside trusted-ca-bundle.  The new code
path is strictly gated on optr.hypershift, so non-HyperShift clusters
are completely unaffected.

Add TestGetTLSConfig covering all combinations of bundle presence and
the hypershift flag.

Made-with: Cursor
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 9a0b5bc2-64b4-467e-b4e6-46b3a1af94d7

📥 Commits

Reviewing files that changed from the base of the PR and between 7d0b4a5 and 56c1973.

📒 Files selected for processing (1)
  • pkg/cvo/egress.go

Walkthrough

Rewrites getTLSConfig() to build a single x509.CertPool initialized from system roots (fallback to empty), append ca-bundle.crt from the managed trusted-ca-bundle, and—when optr.hypershift—also attempt user-ca-bundle; returns nil if no CA material appended. Adds table-driven tests covering presence/absence and error cases.

Changes

Cohort / File(s) Summary
TLS configuration logic
pkg/cvo/egress.go
Reworked getTLSConfig() to initialize cert pool from x509.SystemCertPool() (fallback to x509.NewCertPool()), append managed trusted-ca-bundle ca-bundle.crt, optionally append user-ca-bundle when optr.hypershift is true, track whether any certs were added and only return tls.Config when so, and surface non-NotFound lister errors with distinct messages for managed vs user bundles.
Unit tests
pkg/cvo/egress_test.go
Added tests with embedded PEM certs and helpers to parse/inspect cert pools. TestGetTLSConfig exercises combinations of hypershift and presence/absence/non-NotFound errors for managed (trusted-ca-bundle) and user (user-ca-bundle) configmaps, asserting expected tls.Config and RootCAs contents.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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.

🧹 Nitpick comments (2)
pkg/cvo/egress.go (1)

109-113: Consider documenting the intentional omission of MinVersion.

Static analysis flags the missing MinVersion field. For a client connecting to Cincinnati/OSUS update services, Go's default (TLS 1.2) is typically acceptable and avoids breaking compatibility with services that don't support TLS 1.3. However, a brief comment explaining this choice would preempt future questions.

📝 Optional: Add clarifying comment
 	if !found {
 		return nil, nil
 	}

+	// MinVersion intentionally omitted; Go defaults to TLS 1.2 for clients,
+	// which is acceptable for Cincinnati/OSUS connectivity and avoids
+	// breaking compatibility with update services that only support TLS 1.2.
 	return &tls.Config{RootCAs: certPool}, nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/cvo/egress.go` around lines 109 - 113, Add a brief explanatory comment
next to the creation of the tls.Config (the &tls.Config{RootCAs: certPool}
return in pkg/cvo/egress.go) stating that MinVersion is intentionally omitted so
the client uses Go's default TLS version (currently 1.2) to maintain
compatibility with Cincinnati/OSUS update services; include a note that this
choice is deliberate to avoid forcing TLS 1.3 and to preempt future questions
from static analysis.
pkg/cvo/egress_test.go (1)

166-219: Consider adding error-path test cases.

The current tests cover happy paths and "not found" scenarios. Adding cases where the listers return non-NotFound errors would validate the error propagation paths in getTLSConfig() (lines 81-82 and 98-99 in egress.go).

🧪 Example error test case to add
{
    name:       "managed lister error propagates",
    hypershift: false,
    // Set up managedLister with Err field to simulate a non-NotFound error
    wantError:  true,
},

This would require adding a wantError bool field to the test struct and adjusting the test logic to expect and verify the error.

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

In `@pkg/cvo/egress_test.go` around lines 166 - 219, Add tests for error paths by
extending the test table to include a wantError bool and cases that simulate
non-NotFound errors from the listers (set the Err field on the cmConfigLister
used for managedLister and/or configLister). In the t.Run loop, when
tt.wantError is true assert that optr.getTLSConfig() returns a non-nil error
(and skip further nil checks), otherwise keep the existing assertions; reference
the cmConfigLister used to populate Operator.cmConfigManagedLister and
Operator.cmConfigLister and the getTLSConfig() call to locate where to make
changes.
🤖 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/cvo/egress_test.go`:
- Around line 166-219: Add tests for error paths by extending the test table to
include a wantError bool and cases that simulate non-NotFound errors from the
listers (set the Err field on the cmConfigLister used for managedLister and/or
configLister). In the t.Run loop, when tt.wantError is true assert that
optr.getTLSConfig() returns a non-nil error (and skip further nil checks),
otherwise keep the existing assertions; reference the cmConfigLister used to
populate Operator.cmConfigManagedLister and Operator.cmConfigLister and the
getTLSConfig() call to locate where to make changes.

In `@pkg/cvo/egress.go`:
- Around line 109-113: Add a brief explanatory comment next to the creation of
the tls.Config (the &tls.Config{RootCAs: certPool} return in pkg/cvo/egress.go)
stating that MinVersion is intentionally omitted so the client uses Go's default
TLS version (currently 1.2) to maintain compatibility with Cincinnati/OSUS
update services; include a note that this choice is deliberate to avoid forcing
TLS 1.3 and to preempt future questions from static analysis.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 8cd764c4-898f-4073-9843-37ae10219b5a

📥 Commits

Reviewing files that changed from the base of the PR and between e9c1c39 and ec14b44.

📒 Files selected for processing (2)
  • pkg/cvo/egress.go
  • pkg/cvo/egress_test.go

@dpateriya dpateriya changed the title pkg/cvo/egress: trust user-ca-bundle in HyperShift mode OCPBUGS-81675: pkg/cvo/egress: trust user-ca-bundle in HyperShift mode Apr 10, 2026
@openshift-ci-robot openshift-ci-robot added jira/severity-moderate Referenced Jira bug's severity is moderate for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Apr 10, 2026
@openshift-ci-robot
Copy link
Copy Markdown
Contributor

@dpateriya: This pull request references Jira Issue OCPBUGS-81675, which is invalid:

  • expected the bug to target the "4.22.0" version, but no target version was set

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

Problem

In a HyperShift hosted cluster, HostedCluster.spec.additionalTrustBundle is synced by the Hosted Cluster Config Operator (HCCO) into openshift-config/user-ca-bundle. However, that ConfigMap is not automatically merged into openshift-config-managed/trusted-ca-bundle — which is the only source getTLSConfig() currently reads.

As a result, when spec.updateService points to an update service whose TLS certificate is signed by an internal CA provided exclusively via additionalTrustBundle, the CVO Cincinnati client fails with:

x509: certificate signed by unknown authority

This affects customers running an internal Cincinnati-compatible update service (e.g. OpenShift Update Service / OSUS) behind a wildcard certificate signed by an internal CA.

Fix

When running with --hypershift, also read openshift-config/user-ca-bundle (via the already-wired cmConfigLister) and merge its contents into the cert pool alongside trusted-ca-bundle.

The new code path is strictly gated on optr.hypershift, so standard OCP clusters are completely unaffected.

Both listers (cmConfigLister for openshift-config and cmConfigManagedLister for openshift-config-managed) are already wired in cvo.New() — no new informers or flags are required.

Changes

  • pkg/cvo/egress.go: extend getTLSConfig() to merge openshift-config/user-ca-bundle into the cert pool when --hypershift is set.
  • pkg/cvo/egress_test.go: new TestGetTLSConfig with 8 table-driven cases covering all combinations of bundle presence and the hypershift flag.

Testing

go test ./pkg/cvo/ -run TestGetTLSConfig -v

All 8 cases pass:

  • non-hypershift, no bundles → nil config
  • non-hypershift, trusted-ca-bundle only → CA trusted
  • non-hypershift, user-ca-bundle present but hypershift=false → ignored
  • non-hypershift, both present but hypershift=false → only trusted-ca-bundle used
  • hypershift, no bundles → nil config
  • hypershift, trusted-ca-bundle only → CA trusted
  • hypershift, user-ca-bundle only → CA trusted
  • hypershift, both bundles → both CAs merged and trusted

Summary by CodeRabbit

  • New Features

  • Added HyperShift-specific certificate bundle handling for enhanced configuration management.

  • Bug Fixes

  • Improved certificate loading and error handling to be more robust when custom certificates are present or absent.

  • Enhanced certificate pool merging to properly validate all certificate sources.

  • Tests

  • Added comprehensive test coverage for certificate configuration scenarios, including HyperShift and non-HyperShift deployments.

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 openshift-eng/jira-lifecycle-plugin repository.

@dpateriya
Copy link
Copy Markdown
Author

/jira refresh

@openshift-ci-robot openshift-ci-robot added jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. and removed jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Apr 10, 2026
@openshift-ci-robot
Copy link
Copy Markdown
Contributor

@dpateriya: This pull request references Jira Issue OCPBUGS-81675, which is valid. The bug has been moved to the POST state.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (4.22.0) matches configured target version for branch (4.22.0)
  • bug is in the state New, which is one of the valid states (NEW, ASSIGNED, POST)
Details

In response to this:

/jira refresh

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 openshift-eng/jira-lifecycle-plugin repository.

@dpateriya
Copy link
Copy Markdown
Author

/lgtm

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Apr 10, 2026

@dpateriya: you cannot LGTM your own PR.

Details

In response to this:

/lgtm

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.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Apr 10, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: dpateriya
Once this PR has been reviewed and has the lgtm label, please assign pratikmahajan 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

- Add comment explaining why tls.Config.MinVersion is intentionally
  omitted (Go defaults to TLS 1.2 for clients, acceptable for
  Cincinnati/OSUS and avoids breaking services not yet on TLS 1.3)
- Add error-path test cases for non-NotFound lister errors from both
  cmConfigManagedLister and cmConfigLister to verify error propagation

Made-with: Cursor
@dpateriya
Copy link
Copy Markdown
Author

@PratikMahajan can you please review this PR?

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

🤖 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/cvo/egress.go`:
- Around line 75-77: The current code creates an empty certPool via
x509.NewCertPool() (certPool) and later assigns it to TLSClientConfig.RootCAs,
which replaces system roots; instead, initialize certPool from system roots
first (use x509.SystemCertPool() and fall back to a new pool if nil), then
append the user/managed bundles to that pool before assigning it to
TLSClientConfig.RootCAs so system trust is preserved; update the code paths that
call x509.NewCertPool() and the assignment to TLSClientConfig.RootCAs to use
this 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 2cfe5e62-7ca5-4a0a-a36a-61d4ce5246ef

📥 Commits

Reviewing files that changed from the base of the PR and between ec14b44 and d335cc8.

📒 Files selected for processing (2)
  • pkg/cvo/egress.go
  • pkg/cvo/egress_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/cvo/egress_test.go

@openshift-ci-robot
Copy link
Copy Markdown
Contributor

@dpateriya: This pull request references Jira Issue OCPBUGS-81675, which is valid.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (4.22.0) matches configured target version for branch (4.22.0)
  • bug is in the state POST, which is one of the valid states (NEW, ASSIGNED, POST)
Details

In response to this:

Problem

In a HyperShift hosted cluster, HostedCluster.spec.additionalTrustBundle is synced by the Hosted Cluster Config Operator (HCCO) into openshift-config/user-ca-bundle. However, that ConfigMap is not automatically merged into openshift-config-managed/trusted-ca-bundle — which is the only source getTLSConfig() currently reads.

As a result, when spec.updateService points to an update service whose TLS certificate is signed by an internal CA provided exclusively via additionalTrustBundle, the CVO Cincinnati client fails with:

x509: certificate signed by unknown authority

This affects customers running an internal Cincinnati-compatible update service (e.g. OpenShift Update Service / OSUS) behind a wildcard certificate signed by an internal CA.

Fix

When running with --hypershift, also read openshift-config/user-ca-bundle (via the already-wired cmConfigLister) and merge its contents into the cert pool alongside trusted-ca-bundle.

The new code path is strictly gated on optr.hypershift, so standard OCP clusters are completely unaffected.

Both listers (cmConfigLister for openshift-config and cmConfigManagedLister for openshift-config-managed) are already wired in cvo.New() — no new informers or flags are required.

Changes

  • pkg/cvo/egress.go: extend getTLSConfig() to merge openshift-config/user-ca-bundle into the cert pool when --hypershift is set.
  • pkg/cvo/egress_test.go: new TestGetTLSConfig with 8 table-driven cases covering all combinations of bundle presence and the hypershift flag.

Testing

go test ./pkg/cvo/ -run TestGetTLSConfig -v

All 8 cases pass:

  • non-hypershift, no bundles → nil config
  • non-hypershift, trusted-ca-bundle only → CA trusted
  • non-hypershift, user-ca-bundle present but hypershift=false → ignored
  • non-hypershift, both present but hypershift=false → only trusted-ca-bundle used
  • hypershift, no bundles → nil config
  • hypershift, trusted-ca-bundle only → CA trusted
  • hypershift, user-ca-bundle only → CA trusted
  • hypershift, both bundles → both CAs merged and trusted

Summary by CodeRabbit

  • New Features

  • Added HyperShift-aware certificate bundle handling to include user CA bundles when applicable.

  • Bug Fixes

  • More robust certificate loading and clearer error reporting when managed or user bundles are missing or fail to load.

  • Return nil when no CA material is available to avoid using an empty cert pool.

  • Tests

  • Added table-driven tests covering combinations of HyperShift mode, presence/absence of managed and user bundles, and error cases.

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 openshift-eng/jira-lifecycle-plugin repository.

Starting from x509.NewCertPool() causes RootCAs to replace system trust
entirely when custom bundles are present.  Seed from x509.SystemCertPool()
instead so that custom CAs are additive — system CA trust is preserved and
public-CA endpoints (e.g. a Cincinnati service with a Let's Encrypt cert)
continue to work alongside internal-CA endpoints.

If SystemCertPool is unavailable on the current platform, fall back to an
empty pool to preserve the previous behaviour rather than hard-failing.

Made-with: Cursor
@dpateriya
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@dpateriya
Copy link
Copy Markdown
Author

/retest-required

@dpateriya
Copy link
Copy Markdown
Author

/test e2e-agnostic-ovn

@dpateriya
Copy link
Copy Markdown
Author

/retest-required

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Apr 10, 2026

@dpateriya: The following tests 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/e2e-hypershift-conformance 56c1973 link true /test e2e-hypershift-conformance
ci/prow/e2e-hypershift 56c1973 link true /test e2e-hypershift

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.

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

Labels

jira/severity-moderate Referenced Jira bug's severity is moderate for the branch this PR is targeting. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants