Skip to content

GCP-430: Wire GCP WIF credentials for CNCC in HyperShift HCP mode#2915

Draft
apahim wants to merge 1 commit intoopenshift:masterfrom
apahim:cncc
Draft

GCP-430: Wire GCP WIF credentials for CNCC in HyperShift HCP mode#2915
apahim wants to merge 1 commit intoopenshift:masterfrom
apahim:cncc

Conversation

@apahim
Copy link

@apahim apahim commented Feb 27, 2026

  • Read GCP_CNCC_CREDENTIALS_FILE env var (set by CPO) and set GOOGLE_APPLICATION_CREDENTIALS on the CNCC container so that CNCC's auto-detection (cloud-network-config-controller PR #206) picks up the WIF external_account credential.
  • Add --token-audience=openshift to the cloud-token minter so GCP STS can validate the projected service-account token against the OIDC provider's allowed audience.
  • Add unit tests for template rendering.

Summary by CodeRabbit

  • New Features

    • Added support for Google Cloud Platform Workload Identity Federation credentials in cloud network configuration
    • Implemented token audience configuration for OpenShift deployments
  • Tests

    • Added test coverage to verify GCP credentials are properly configured in the cloud network controller
    • Added test coverage to verify token audience is correctly set in the cloud token minter

- Read GCP_CNCC_CREDENTIALS_FILE env var (set by CPO) and set
  GOOGLE_APPLICATION_CREDENTIALS on the CNCC container so that
  CNCC's auto-detection (cloud-network-config-controller PR openshift#206)
  picks up the WIF external_account credential.
- Add --token-audience=openshift to the cloud-token minter so GCP
  STS can validate the projected service-account token against the
  OIDC provider's allowed audience.
- Add unit tests for template rendering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Feb 27, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 27, 2026

@apahim: This pull request references GCP-430 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

  • Read GCP_CNCC_CREDENTIALS_FILE env var (set by CPO) and set GOOGLE_APPLICATION_CREDENTIALS on the CNCC container so that CNCC's auto-detection (cloud-network-config-controller PR remove all sriov bits from CNO #206) picks up the WIF external_account credential.
  • Add --token-audience=openshift to the cloud-token minter so GCP STS can validate the projected service-account token against the OIDC provider's allowed audience.
  • Add unit tests for template rendering.

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.

@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

Walkthrough

This change adds GCP Workload Identity Federation support to the Cloud Network Config Controller. The manifest is updated to include token audience configuration and conditional Google credentials environment setup, while the implementation handles reading GCP credentials from environment variables and rendering them into deployments.

Changes

Cohort / File(s) Summary
Cloud Controller Manifest
bindata/cloud-network-config-controller/managed/controller.yaml
Added token-audience=openshift argument to cloud-token container and introduced conditional environment block for GCP credentials via GOOGLE_APPLICATION_CREDENTIALS when GCPCredentialsPath is provided.
GCP Credentials Handling
pkg/network/cloud_network.go
Implemented reading of GCP_CNCC_CREDENTIALS_FILE environment variable to conditionally set GCPCredentialsPath in HyperShift-enabled CNCC rendering path, alongside existing Azure/AWS credential handling.
Credential and Token Tests
pkg/network/cloud_network_test.go
Added unit tests to verify GOOGLE_APPLICATION_CREDENTIALS environment variable rendering when GCPCredentialsPath is set and to validate --token-audience=openshift argument presence in cloud-token minter container.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: wiring GCP WIF credentials for CNCC in HyperShift HCP mode, which aligns with all three objectives (GCP credentials setup, token audience configuration, and tests).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Stable And Deterministic Test Names ✅ Passed Test names TestGCPCredentialsPathTemplateRendering and TestCloudTokenMinterHasTokenAudience are stable, deterministic, and contain no dynamic values.
Test Structure And Quality ✅ Passed The added tests follow Go testing best practices with clear single responsibilities using table-driven subtests and meaningful assertions.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 27, 2026

@apahim: This pull request references GCP-430 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

  • Read GCP_CNCC_CREDENTIALS_FILE env var (set by CPO) and set GOOGLE_APPLICATION_CREDENTIALS on the CNCC container so that CNCC's auto-detection (cloud-network-config-controller PR remove all sriov bits from CNO #206) picks up the WIF external_account credential.
  • Add --token-audience=openshift to the cloud-token minter so GCP STS can validate the projected service-account token against the OIDC provider's allowed audience.
  • Add unit tests for template rendering.

Summary by CodeRabbit

  • New Features

  • Added support for Google Cloud Platform Workload Identity Federation credentials in cloud network configuration

  • Implemented token audience configuration for OpenShift deployments

  • Tests

  • Added test coverage to verify GCP credentials are properly configured in the cloud network controller

  • Added test coverage to verify token audience is correctly set in the cloud token minter

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.

@openshift-ci openshift-ci bot requested review from danwinship and tssurya February 27, 2026 14:18
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 27, 2026

@apahim: This pull request references GCP-430 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

  • Read GCP_CNCC_CREDENTIALS_FILE env var (set by CPO) and set GOOGLE_APPLICATION_CREDENTIALS on the CNCC container so that CNCC's auto-detection (cloud-network-config-controller PR #206) picks up the WIF external_account credential.
  • Add --token-audience=openshift to the cloud-token minter so GCP STS can validate the projected service-account token against the OIDC provider's allowed audience.
  • Add unit tests for template rendering.

Summary by CodeRabbit

  • New Features

  • Added support for Google Cloud Platform Workload Identity Federation credentials in cloud network configuration

  • Implemented token audience configuration for OpenShift deployments

  • Tests

  • Added test coverage to verify GCP credentials are properly configured in the cloud network controller

  • Added test coverage to verify token audience is correctly set in the cloud token minter

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.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 27, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

@apahim
Copy link
Author

apahim commented Feb 27, 2026

/hold

@openshift-ci openshift-ci bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Feb 27, 2026
Copy link

@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

🧹 Nitpick comments (1)
pkg/network/cloud_network_test.go (1)

34-66: Extract shared render-data fixture to reduce duplication.

Both tests build nearly identical render.MakeRenderData() payloads. A helper fixture will keep future template-key updates consistent.

Refactor sketch
+func makeManagedControllerRenderData() render.RenderData {
+	data := render.MakeRenderData()
+	data.Data["ReleaseVersion"] = "4.18.0"
+	data.Data["PlatformType"] = "GCP"
+	data.Data["PlatformRegion"] = "us-central1"
+	data.Data["PlatformTypeAWS"] = "AWS"
+	data.Data["PlatformTypeAzure"] = "Azure"
+	data.Data["PlatformTypeGCP"] = "GCP"
+	data.Data["CloudNetworkConfigControllerImage"] = "test-image"
+	data.Data["KubernetesServiceURL"] = "https://localhost:6443"
+	data.Data["ExternalControlPlane"] = true
+	data.Data["PlatformAzureEnvironment"] = ""
+	data.Data["PlatformAWSCAPath"] = ""
+	data.Data["PlatformAPIURL"] = ""
+	data.Data["CLIImage"] = "cli-image"
+	data.Data["TokenMinterImage"] = "token-minter-image"
+	data.Data["TokenAudience"] = "https://issuer.example.com"
+	data.Data["ManagementClusterName"] = "test-cluster"
+	data.Data["HostedClusterNamespace"] = "test-ns"
+	data.Data["ReleaseImage"] = "release-image"
+	data.Data["HCPNodeSelector"] = map[string]string{}
+	data.Data["HCPLabels"] = map[string]string{}
+	data.Data["HCPTolerations"] = []string{}
+	data.Data["RunAsUser"] = ""
+	data.Data["PriorityClass"] = ""
+	data.Data["HTTP_PROXY"] = ""
+	data.Data["HTTPS_PROXY"] = ""
+	data.Data["NO_PROXY"] = ""
+	data.Data["AzureManagedCertDirectory"] = ""
+	data.Data["AzureManagedCredsPath"] = ""
+	data.Data["AzureManagedSecretProviderClass"] = ""
+	return data
+}

Also applies to: 106-137

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

In `@pkg/network/cloud_network_test.go` around lines 34 - 66, The test duplicated
a large render.MakeRenderData() payload in cloud_network_test.go (the blocks
starting at the shown diff and again at lines ~106-137); refactor by extracting
a shared helper (e.g., newTestRenderData or makeRenderDataFixture) that returns
render.MakeRenderData() with all required keys set (ReleaseVersion,
PlatformType, PlatformRegion, PlatformTypeAWS/Azure/GCP,
CloudNetworkConfigControllerImage, KubernetesServiceURL, ExternalControlPlane,
PlatformAzureEnvironment, PlatformAWSCAPath, PlatformAPIURL, CLIImage,
TokenMinterImage, TokenAudience, ManagementClusterName, HostedClusterNamespace,
ReleaseImage, HCPNodeSelector, HCPLabels, HCPTolerations, RunAsUser,
PriorityClass, HTTP_PROXY, HTTPS_PROXY, NO_PROXY, AzureManagedCertDirectory,
AzureManagedCredsPath, AzureManagedSecretProviderClass, GCPCredentialsPath) and
have both tests call that helper (passing tc.gcpCredentialsPath when needed) to
remove duplication and keep future template-key updates in one place.
🤖 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/network/cloud_network.go`:
- Around line 113-117: Validate and constrain the GCP_CNCC_CREDENTIALS_FILE
value to a filename before joining: read it via os.Getenv into gcpCredsFile,
compute base := filepath.Base(gcpCredsFile) and only use
filepath.Join("/etc/secret/cloudprovider", base) when base equals the original
gcpCredsFile and base is non-empty (otherwise treat it as invalid and set
data.Data["GCPCredentialsPath"] = "" or handle error); update the assignment to
data.Data["GCPCredentialsPath"] accordingly so absolute paths or values with
directories cannot escape the intended directory.

---

Nitpick comments:
In `@pkg/network/cloud_network_test.go`:
- Around line 34-66: The test duplicated a large render.MakeRenderData() payload
in cloud_network_test.go (the blocks starting at the shown diff and again at
lines ~106-137); refactor by extracting a shared helper (e.g., newTestRenderData
or makeRenderDataFixture) that returns render.MakeRenderData() with all required
keys set (ReleaseVersion, PlatformType, PlatformRegion,
PlatformTypeAWS/Azure/GCP, CloudNetworkConfigControllerImage,
KubernetesServiceURL, ExternalControlPlane, PlatformAzureEnvironment,
PlatformAWSCAPath, PlatformAPIURL, CLIImage, TokenMinterImage, TokenAudience,
ManagementClusterName, HostedClusterNamespace, ReleaseImage, HCPNodeSelector,
HCPLabels, HCPTolerations, RunAsUser, PriorityClass, HTTP_PROXY, HTTPS_PROXY,
NO_PROXY, AzureManagedCertDirectory, AzureManagedCredsPath,
AzureManagedSecretProviderClass, GCPCredentialsPath) and have both tests call
that helper (passing tc.gcpCredentialsPath when needed) to remove duplication
and keep future template-key updates in one place.

ℹ️ Review info

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 1c1a097 and a430fb1.

📒 Files selected for processing (3)
  • bindata/cloud-network-config-controller/managed/controller.yaml
  • pkg/network/cloud_network.go
  • pkg/network/cloud_network_test.go

Comment on lines +113 to +117
gcpCredsFile := os.Getenv("GCP_CNCC_CREDENTIALS_FILE")
if gcpCredsFile != "" {
data.Data["GCPCredentialsPath"] = filepath.Join("/etc/secret/cloudprovider", gcpCredsFile)
} else {
data.Data["GCPCredentialsPath"] = ""
Copy link

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:

#!/bin/bash
# First, find the file and verify the code context
cat -n pkg/network/cloud_network.go | head -130 | tail -30

Repository: openshift/cluster-network-operator

Length of output: 2070


🏁 Script executed:

#!/bin/bash
# Verify all producers/consumers of GCP_CNCC_CREDENTIALS_FILE in-repo
rg -n -C3 'GCP_CNCC_CREDENTIALS_FILE|GCPCredentialsPath'

Repository: openshift/cluster-network-operator

Length of output: 4821


🏁 Script executed:

#!/bin/bash
# Check filepath.Join behavior to understand the vulnerability
# This tests if filepath.Join escapes directory with absolute/traversal paths
python3 << 'EOF'
import os
from pathlib import Path

# Test cases for filepath.Join behavior in Go
test_cases = [
    ("/etc/secret/cloudprovider", "normal.txt"),
    ("/etc/secret/cloudprovider", "/etc/passwd"),
    ("/etc/secret/cloudprovider", "../../etc/passwd"),
    ("/etc/secret/cloudprovider", "./normal.txt"),
]

print("Go filepath.Join() behavior simulation (path.Join in Go):")
for base, rel in test_cases:
    # Note: Go's filepath.Join uses / separator and works similarly to os.path.join
    result = os.path.join(base, rel)
    print(f"  filepath.Join('{base}', '{rel}') => '{result}'")
    if result.startswith(base):
        print(f"    ✓ Contained within {base}")
    else:
        print(f"    ✗ ESCAPED {base}")
EOF

Repository: openshift/cluster-network-operator

Length of output: 718


🏁 Script executed:

#!/bin/bash
# Search for documentation, comments, or configuration of GCP_CNCC_CREDENTIALS_FILE
rg -n -C2 'GCP_CNCC_CREDENTIALS_FILE' --type-list | head -50
find . -name '*.md' -o -name '*.yaml' -o -name '*.yml' | xargs grep -l 'GCP_CNCC_CREDENTIALS_FILE' 2>/dev/null

Repository: openshift/cluster-network-operator

Length of output: 469


🏁 Script executed:

#!/bin/bash
# Check if there are any validation wrappers around environment variable usage
rg -B5 -A5 'os.Getenv.*CREDENTIALS' pkg/network/ | head -100

Repository: openshift/cluster-network-operator

Length of output: 1229


Constrain GCP_CNCC_CREDENTIALS_FILE to a filename before joining.

Using filepath.Join("/etc/secret/cloudprovider", gcpCredsFile) without validation allows absolute paths to escape the intended directory. For example, if gcpCredsFile is /etc/passwd, the result becomes /etc/passwd instead of a path under /etc/secret/cloudprovider. Add a basename validation to reject non-filename values.

Suggested fix
 		// GCP WIF credential path for HCP deployments.
 		gcpCredsFile := os.Getenv("GCP_CNCC_CREDENTIALS_FILE")
-		if gcpCredsFile != "" {
+		if gcpCredsFile == "" {
+			data.Data["GCPCredentialsPath"] = ""
+		} else if filepath.Base(gcpCredsFile) != gcpCredsFile {
+			return nil, errors.Errorf("invalid GCP_CNCC_CREDENTIALS_FILE %q: must be a filename", gcpCredsFile)
+		} else {
 			data.Data["GCPCredentialsPath"] = filepath.Join("/etc/secret/cloudprovider", gcpCredsFile)
-		} else {
-			data.Data["GCPCredentialsPath"] = ""
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/network/cloud_network.go` around lines 113 - 117, Validate and constrain
the GCP_CNCC_CREDENTIALS_FILE value to a filename before joining: read it via
os.Getenv into gcpCredsFile, compute base := filepath.Base(gcpCredsFile) and
only use filepath.Join("/etc/secret/cloudprovider", base) when base equals the
original gcpCredsFile and base is non-empty (otherwise treat it as invalid and
set data.Data["GCPCredentialsPath"] = "" or handle error); update the assignment
to data.Data["GCPCredentialsPath"] accordingly so absolute paths or values with
directories cannot escape the intended directory.

@apahim apahim marked this pull request as draft February 27, 2026 14:44
@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 27, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 27, 2026

@apahim: 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/security a430fb1 link false /test security

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.

data.Data["AzureManagedCredsPath"] = filepath.Join(azureCertPath, os.Getenv("MANAGED_AZURE_HCP_CREDENTIALS_FILE_PATH"))
data.Data["AzureManagedSecretProviderClass"] = os.Getenv("ARO_HCP_SECRET_PROVIDER_CLASS")
// GCP WIF credential path for HCP deployments.
gcpCredsFile := os.Getenv("GCP_CNCC_CREDENTIALS_FILE")
Copy link
Contributor

Choose a reason for hiding this comment

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

In the commit message you say that GCP_CNCC_CREDENTIALS_FILE is set by CPO. What's CPO? You're setting this env var in this other PR, right? https://github.com/openshift/hypershift/pull/7824/changes

data.Data["AzureManagedCertDirectory"] = ""
data.Data["AzureManagedCredsPath"] = ""
data.Data["AzureManagedSecretProviderClass"] = ""
data.Data["GCPCredentialsPath"] = ""
Copy link
Contributor

Choose a reason for hiding this comment

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

You can probably use a helper function to set up data.Data for your tests

}
yamlStr := string(yaml)

hasGoogleAppCreds := strings.Contains(yamlStr, "GOOGLE_APPLICATION_CREDENTIALS")
Copy link
Contributor

Choose a reason for hiding this comment

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

Here and below, every time you call strings.Contains, you're just looking for a substring in the entire yaml. I think it's more appropriate to actually parse the object and look for exactly the parameters you're interested in and check their value. For example, existing unit tests use uns.NestedSlice and uns.NestedString to do that.

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

Labels

do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. 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.

3 participants