From 67ef8cb2bf32690bcbf70103dc73d395491d528f Mon Sep 17 00:00:00 2001 From: Patrick Dillon Date: Wed, 1 Jul 2026 15:25:47 -0400 Subject: [PATCH 1/2] GCP Clients: support alternate universe domains Updates GCP client initialization to support alternate universe domains. option.WithCredentialsJSON is used to handle most auth cases because it utilizes a different authentication flow using a self-signed JWT which is sent directly to GCP/GCD, rather than oauth2. The oauth2 flow of option.WithCredentials is problematic for GCD because service account keys (and other JSON creds), for some reason, set their token uri to https://oauth2.apis-berlin-build0.goog/token, an endpoint that doesn't exist; it is clearly documented in GCD docs that oauth2 should still use googleapis. Nevertheless, the option.WithCredentials will always use the token uri in the JSON even when it is correct. To get around this issue we use option.WithCredentialsJSON, which does not need the oauth2 token endpoint at all. We fallback to option.WithCredentials for authenticating via a service account attached to a VM, in which case there is no JSON to parse. Note that option.WithCredentialsJSON is deprecated in the GCP package because if credentials are accepted from an external source, there are potential vulnerabilities. See: https://docs.cloud.google.com/docs/authentication/client-libraries#external-credentials The installer is built to work under any circumstances as it serves a diverse set of needs. Clients built on top of the installer may accept external credentials, but it is up to those clients to validate the inputs. --- pkg/asset/installconfig/gcp/client.go | 6 +++++- pkg/asset/installconfig/gcp/services.go | 27 ++++++++++++++++++++----- pkg/quota/gcp/gcp.go | 6 +++++- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/pkg/asset/installconfig/gcp/client.go b/pkg/asset/installconfig/gcp/client.go index ee7bbe27a41..a7adb72e732 100644 --- a/pkg/asset/installconfig/gcp/client.go +++ b/pkg/asset/installconfig/gcp/client.go @@ -720,7 +720,11 @@ func (c *Client) GetNamespacedTagValue(ctx context.Context, tagNamespacedName st } func (c *Client) getKeyManagementClient(ctx context.Context) (*kms.KeyManagementClient, error) { - kmsClient, err := kms.NewKeyManagementClient(ctx, option.WithCredentials(c.ssn.Credentials)) + opts, err := CredentialOptions(c.ssn) + if err != nil { + return nil, fmt.Errorf("failed to get credential options: %w", err) + } + kmsClient, err := kms.NewKeyManagementClient(ctx, opts...) if err != nil { return nil, fmt.Errorf("failed to create kms key management client: %w", err) } diff --git a/pkg/asset/installconfig/gcp/services.go b/pkg/asset/installconfig/gcp/services.go index 38185f135e6..915b6951bdf 100644 --- a/pkg/asset/installconfig/gcp/services.go +++ b/pkg/asset/installconfig/gcp/services.go @@ -59,17 +59,34 @@ func CreateEndpointOption(endpointName string, service ServiceNameGCP) option.Cl return option.WithEndpoint(endpoint) } +// CredentialOptions returns the client options for authenticating with GCP, +// including universe domain support for Google Cloud Dedicated. +// When credential JSON is available, it is passed via WithCredentialsJSON so +// the client library can use self-signed JWTs for non-default universe +// domains (where the OAuth2 token endpoint is unavailable). Falls back to +// WithCredentials for metadata-based credentials that have no JSON. +func CredentialOptions(ssn *Session) ([]option.ClientOption, error) { + ud, err := ssn.Credentials.GetUniverseDomain() + if err != nil { + return nil, fmt.Errorf("failed to get universe domain: %w", err) + } + var opts []option.ClientOption + if len(ssn.Credentials.JSON) > 0 { + opts = append(opts, option.WithCredentialsJSON(ssn.Credentials.JSON)) + } else { + opts = append(opts, option.WithCredentials(ssn.Credentials)) + } + opts = append(opts, option.WithUniverseDomain(ud)) + return opts, nil +} + // getOptions creates the options for use during service creation. func getOptions(ctx context.Context) ([]option.ClientOption, error) { ssn, err := GetSession(ctx) if err != nil { return nil, fmt.Errorf("failed to get session: %w", err) } - - options := []option.ClientOption{ - option.WithCredentials(ssn.Credentials), - } - return options, nil + return CredentialOptions(ssn) } // GetComputeService creates the compute service. The service is created with credentials and any service diff --git a/pkg/quota/gcp/gcp.go b/pkg/quota/gcp/gcp.go index 552c2b266c8..1baf81ebaed 100644 --- a/pkg/quota/gcp/gcp.go +++ b/pkg/quota/gcp/gcp.go @@ -2,6 +2,7 @@ package gcp import ( "context" + "fmt" "net/http" "sort" "strings" @@ -35,7 +36,10 @@ func Load(ctx context.Context, project string, endpoint *gcptypes.PSCEndpoint, s if err != nil { return nil, errors.Wrap(err, "failed to create services svc") } - metricsOptions := []option.ClientOption{option.WithCredentials(ssn.Credentials)} + metricsOptions, err := gcpconfig.CredentialOptions(ssn) + if err != nil { + return nil, fmt.Errorf("failed to get credential options: %w", err) + } metricsSvc, err := monitoring.NewMetricClient(ctx, metricsOptions...) if err != nil { return nil, errors.Wrap(err, "failed to create metrics svc") From 40e300c0ebc25b72e53c960ae996f891bfb8bb32 Mon Sep 17 00:00:00 2001 From: Patrick Dillon Date: Thu, 2 Jul 2026 14:20:00 -0400 Subject: [PATCH 2/2] clusterapi: set GCP universe domain Until CAPG has built in universe domain support, we can explicitly set the universe domain as an environment variable. --- pkg/clusterapi/system.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/clusterapi/system.go b/pkg/clusterapi/system.go index f991d0f54ab..0e07d683750 100644 --- a/pkg/clusterapi/system.go +++ b/pkg/clusterapi/system.go @@ -311,6 +311,17 @@ func (c *system) Run(ctx context.Context) error { //nolint:gocyclo logrus.Infof("setting %q to %s for capg infrastructure controller", gAppCredEnvVar, v) } + // Google Cloud Dedicated support: detect universe domain from + // credentials and pass it to the CAPG controller via env var. + ud, err := session.Credentials.GetUniverseDomain() + if err != nil { + return fmt.Errorf("failed to get universe domain from gcp credentials: %w", err) + } + if ud != "googleapis.com" { + capgEnvVars["GOOGLE_CLOUD_UNIVERSE_DOMAIN"] = ud + logrus.Infof("setting GOOGLE_CLOUD_UNIVERSE_DOMAIN to %q for capg infrastructure controller", ud) + } + controllers = append(controllers, c.getInfrastructureController( &GCP,