Skip to content

Commit

Permalink
Fix universe_domain for ADC and access token auth cases (GoogleCloudP…
Browse files Browse the repository at this point in the history
…latform#10517)

Co-authored-by: Riley Karson <rileykarson@google.com>
  • Loading branch information
2 people authored and jessdejong committed Jun 10, 2024
1 parent 03a6ed6 commit bd227d5
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 44 deletions.
44 changes: 15 additions & 29 deletions mmv1/third_party/terraform/provider/provider.go.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package provider

import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
Expand Down Expand Up @@ -279,35 +278,11 @@ func ProviderConfigure(ctx context.Context, d *schema.ResourceData, p *schema.Pr
})
}

// set universe_domain based on the service account key file.
if config.Credentials != "" {
contents, _, err := verify.PathOrContents(config.Credentials)
if err != nil {
return nil, diag.FromErr(fmt.Errorf("error loading service account credentials: %s", err))
}
var content map[string]any

if err := json.Unmarshal([]byte(contents), &content); err != nil {
return nil, diag.FromErr(err)
}

if content["universe_domain"] != nil {
config.UniverseDomain = content["universe_domain"].(string)
}
}

// Check if the user provided a value from the universe_domain field other than the default
if v, ok := d.GetOk("universe_domain"); ok && v.(string) != "googleapis.com" {
if config.UniverseDomain == "" {
return nil, diag.FromErr(fmt.Errorf("Universe domain mismatch: '%s' supplied directly to Terraform with no matching universe domain in credentials. Credentials with no 'universe_domain' set are assumed to be in the default universe.", v))
} else if v.(string) != config.UniverseDomain {
if _, err := os.Stat(config.Credentials); err == nil {
return nil, diag.FromErr(fmt.Errorf("Universe domain mismatch: '%s' does not match the universe domain '%s' already set in the credential file '%s'. The 'universe_domain' provider configuration can not be used to override the universe domain that is defined in the active credential. Set the 'universe_domain' provider configuration when universe domain information is not already available in the credential, e.g. when authenticating with a JWT token.", v, config.UniverseDomain, config.Credentials))
} else {
return nil, diag.FromErr(fmt.Errorf("Universe domain mismatch: '%s' does not match the universe domain '%s' supplied directly to Terraform. The 'universe_domain' provider configuration can not be used to override the universe domain that is defined in the active credential. Set the 'universe_domain' provider configuration when universe domain information is not already available in the credential, e.g. when authenticating with a JWT token.", v, config.UniverseDomain))
}
}
// Set the universe domain to the configured value, if any
if v, ok := d.GetOk("universe_domain"); ok {
config.UniverseDomain = v.(string)
}

// Configure DCL basePath
transport_tpg.ProviderDCLConfigure(d, &config)

Expand Down Expand Up @@ -406,6 +381,17 @@ func ProviderConfigure(ctx context.Context, d *schema.ResourceData, p *schema.Pr
return nil, diag.FromErr(err)
}

// Verify that universe domains match between credentials and configuration
if v, ok := d.GetOk("universe_domain"); ok {
if config.UniverseDomain == "" && v.(string) != "googleapis.com" { // v can't be "", as it wouldn't pass `ok` above
return nil, diag.FromErr(fmt.Errorf("Universe domain mismatch: '%s' supplied directly to Terraform with no matching universe domain in credentials. Credentials with no 'universe_domain' set are assumed to be in the default universe.", v))
} else if v.(string) != config.UniverseDomain && !(config.UniverseDomain == "" && v.(string) == "googleapis.com") {
return nil, diag.FromErr(fmt.Errorf("Universe domain mismatch: '%s' does not match the universe domain '%s' supplied directly to Terraform. The 'universe_domain' provider configuration must match the universe domain supplied by credentials.", config.UniverseDomain, v))
}
} else if config.UniverseDomain != "" && config.UniverseDomain != "googleapis.com" {
return nil, diag.FromErr(fmt.Errorf("Universe domain mismatch: Universe domain '%s' was found in credentials without a corresponding 'universe_domain' provider configuration set. Please set 'universe_domain' to '%s' or use different credentials.", config.UniverseDomain, config.UniverseDomain))
}

return &config, nil
}

Expand Down
63 changes: 48 additions & 15 deletions mmv1/third_party/terraform/transport/config.go.erb
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,7 @@ type StaticTokenSource struct {
// If initialCredentialsOnly is true, don't follow the impersonation settings and return the initial set of creds
// instead.
func (c *Config) GetCredentials(clientScopes []string, initialCredentialsOnly bool) (googleoauth.Credentials, error) {
// UniverseDomain is assumed to be the previously set provider-configured value for access tokens
if c.AccessToken != "" {
contents, _, err := verify.PathOrContents(c.AccessToken)
if err != nil {
Expand All @@ -1159,12 +1160,25 @@ func (c *Config) GetCredentials(clientScopes []string, initialCredentialsOnly bo
}, nil
}

// UniverseDomain is set by the credential file's "universe_domain" field
if c.Credentials != "" {
contents, _, err := verify.PathOrContents(c.Credentials)
if err != nil {
return googleoauth.Credentials{}, fmt.Errorf("error loading credentials: %s", err)
}

var content map[string]any
if err := json.Unmarshal([]byte(contents), &content); err != nil {
return googleoauth.Credentials{}, fmt.Errorf("error unmarshaling credentials: %s", err)
}

if content["universe_domain"] != nil {
c.UniverseDomain = content["universe_domain"].(string)
} else {
// Unset UniverseDomain if not found in credentials file
c.UniverseDomain = ""
}

if c.ImpersonateServiceAccount != "" && !initialCredentialsOnly {
opts := []option.ClientOption{option.WithCredentialsJSON([]byte(contents)), option.ImpersonateCredentials(c.ImpersonateServiceAccount, c.ImpersonateServiceAccountDelegates...), option.WithScopes(clientScopes...)}
creds, err := transport.Creds(context.TODO(), opts...)
Expand Down Expand Up @@ -1194,33 +1208,52 @@ func (c *Config) GetCredentials(clientScopes []string, initialCredentialsOnly bo
}
}

var creds *googleoauth.Credentials
var err error
if c.ImpersonateServiceAccount != "" && !initialCredentialsOnly {
opts := option.ImpersonateCredentials(c.ImpersonateServiceAccount, c.ImpersonateServiceAccountDelegates...)
creds, err := transport.Creds(context.TODO(), opts, option.WithScopes(clientScopes...))
creds, err = transport.Creds(context.TODO(), opts, option.WithScopes(clientScopes...))
if err != nil {
return googleoauth.Credentials{}, err
}
} else {
log.Printf("[INFO] Authenticating using DefaultClient...")
log.Printf("[INFO] -- Scopes: %s", clientScopes)

return *creds, nil
if c.UniverseDomain != "" && c.UniverseDomain != "googleapis.com" {
log.Printf("[INFO] -- Sending JwtWithScope option")
creds, err = transport.Creds(context.Background(), option.WithScopes(clientScopes...), internaloption.EnableJwtWithScope())
if err != nil {
return googleoauth.Credentials{}, fmt.Errorf("Attempted to load application default credentials since neither `credentials` nor `access_token` was set in the provider block. No credentials loaded. To use your gcloud credentials, run 'gcloud auth application-default login'. Original error: %w", err)
}
} else {
creds, err = transport.Creds(context.Background(), option.WithScopes(clientScopes...))
if err != nil {
return googleoauth.Credentials{}, fmt.Errorf("Attempted to load application default credentials since neither `credentials` nor `access_token` was set in the provider block. No credentials loaded. To use your gcloud credentials, run 'gcloud auth application-default login'. Original error: %w", err)
}
}
}

log.Printf("[INFO] Authenticating using DefaultClient...")
log.Printf("[INFO] -- Scopes: %s", clientScopes)

if c.UniverseDomain != "" && c.UniverseDomain != "googleapis.com" {
log.Printf("[INFO] -- Sending JwtWithScope option")
creds, err := transport.Creds(context.Background(), option.WithScopes(clientScopes...), internaloption.EnableJwtWithScope())
if creds.JSON != nil {
var content map[string]any
if err := json.Unmarshal([]byte(creds.JSON), &content); err != nil {
log.Printf("[WARN] error unmarshaling credentials, skipping Universe Domain detection")
c.UniverseDomain = ""
} else if content["universe_domain"] != nil {
c.UniverseDomain = content["universe_domain"].(string)
} else {
// Unset UniverseDomain if not found in ADC credentials file
c.UniverseDomain = ""
}
} else {
// creds.GetUniverseDomain may retrieve a domain from the metadata server
ud, err := creds.GetUniverseDomain()
if err != nil {
return googleoauth.Credentials{}, fmt.Errorf("Attempted to load application default credentials since neither `credentials` nor `access_token` was set in the provider block. No credentials loaded. To use your gcloud credentials, run 'gcloud auth application-default login'. Original error: %w", err)
log.Printf("[WARN] Error retrieving universe domain: %s", err)
}
return *creds, nil
c.UniverseDomain = ud
}

creds, err := transport.Creds(context.Background(), option.WithScopes(clientScopes...))
if err != nil {
return googleoauth.Credentials{}, fmt.Errorf("Attempted to load application default credentials since neither `credentials` nor `access_token` was set in the provider block. No credentials loaded. To use your gcloud credentials, run 'gcloud auth application-default login'. Original error: %w", err)
}

return *creds, nil
}

Expand Down

0 comments on commit bd227d5

Please sign in to comment.