diff --git a/plugin/backend.go b/plugin/backend.go index 1830edd..ce42673 100644 --- a/plugin/backend.go +++ b/plugin/backend.go @@ -11,6 +11,8 @@ import ( "sync" "time" + gauth "cloud.google.com/go/auth/credentials" + "cloud.google.com/go/auth/oauth2adapt" "github.com/hashicorp/errwrap" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-gcp-common/gcputil" @@ -142,8 +144,18 @@ func (b *backend) IAMAdminClient(s logical.Storage) (*iam.Service, error) { return nil, errwrap.Wrapf("failed to create IAM HTTP client: {{err}}", err) } + ctx := context.Background() + + cfg, err := getConfig(ctx, s) + if err != nil { + return nil, err + } + if cfg == nil { + cfg = &config{} + } + client, err := b.cache.Fetch("iam", cacheTime, func() (interface{}, error) { - client, err := iam.NewService(context.Background(), option.WithHTTPClient(httpClient)) + client, err := iam.NewService(context.Background(), option.WithHTTPClient(httpClient), option.WithUniverseDomain(cfg.UniverseDomain)) if err != nil { return nil, errwrap.Wrapf("failed to create IAM client: {{err}}", err) } @@ -203,10 +215,16 @@ func (b *backend) credentials(s logical.Storage) (*google.Credentials, error) { // default application credentials. var creds *google.Credentials if len(credBytes) > 0 { - creds, err = google.CredentialsFromJSON(ctx, credBytes, iam.CloudPlatformScope) + scopes := []string{"openid", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/appengine.admin", "https://www.googleapis.com/auth/sqlservice.login", "https://www.googleapis.com/auth/compute"} + gcred, err := gauth.DetectDefault(&gauth.DetectOptions{ + Scopes: scopes, + CredentialsJSON: credBytes, + UseSelfSignedJWT: true, + UniverseDomain: cfg.UniverseDomain}) if err != nil { return nil, errwrap.Wrapf("failed to parse credentials: {{err}}", err) } + creds = oauth2adapt.Oauth2CredentialsFromAuthCredentials(gcred) } else if cfg.IdentityTokenAudience != "" { ts := &PluginIdentityTokenSupplier{ sys: b.System(), diff --git a/plugin/path_config.go b/plugin/path_config.go index 3855ab4..d1eabe1 100644 --- a/plugin/path_config.go +++ b/plugin/path_config.go @@ -18,6 +18,8 @@ import ( "github.com/hashicorp/vault/sdk/rotation" ) +const DefaultUniverseDomain = "googleapis.com" + func pathConfig(b *backend) *framework.Path { p := &framework.Path{ Pattern: "config", @@ -43,6 +45,12 @@ func pathConfig(b *backend) *framework.Path { Type: framework.TypeString, Description: `Email ID for the Service Account to impersonate for Workload Identity Federation.`, }, + "universe_domain": { + Type: framework.TypeString, + Required: false, + Default: "googleapis.com", + Description: `universe_domain specifies the Google Cloud environment a client connects to, enabling specialized offerings and sovereign controls beyond the default googleapis.com`, + }, }, Operations: map[logical.Operation]framework.OperationHandler{ @@ -86,6 +94,7 @@ func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, data "ttl": int64(cfg.TTL / time.Second), "max_ttl": int64(cfg.MaxTTL / time.Second), "service_account_email": cfg.ServiceAccountEmail, + "universe_domain": cfg.UniverseDomain, } cfg.PopulatePluginIdentityTokenData(configData) @@ -200,6 +209,13 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, dat return logical.ErrorResponse("error registering rotation job: %s", err), nil } } + // Update Universe-Domain. + UniverseDomainRaw, ok := data.GetOk("universe_domain") + if ok { + cfg.UniverseDomain = UniverseDomainRaw.(string) + } else { + cfg.UniverseDomain = DefaultUniverseDomain + } entry, err := logical.StorageEntryJSON("config", cfg) if err != nil { @@ -234,6 +250,7 @@ type config struct { ServiceAccountEmail string pluginidentityutil.PluginIdentityTokenParams automatedrotationutil.AutomatedRotationParams + UniverseDomain string } func getConfig(ctx context.Context, s logical.Storage) (*config, error) { diff --git a/plugin/path_config_test.go b/plugin/path_config_test.go index 0e75c95..befad74 100644 --- a/plugin/path_config_test.go +++ b/plugin/path_config_test.go @@ -44,6 +44,7 @@ func TestConfig(t *testing.T) { "rotation_period": float64(0), "rotation_schedule": "", "disable_automated_rotation": false, + "universe_domain": "googleapis.com", } testConfigRead(t, b, reqStorage, expected) diff --git a/plugin/secrets_access_token.go b/plugin/secrets_access_token.go index cbbf3db..080aa64 100644 --- a/plugin/secrets_access_token.go +++ b/plugin/secrets_access_token.go @@ -8,11 +8,12 @@ import ( "encoding/base64" "time" + gauth "cloud.google.com/go/auth/credentials" + "cloud.google.com/go/auth/oauth2adapt" "github.com/hashicorp/errwrap" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" "golang.org/x/oauth2" - "golang.org/x/oauth2/google" ) func (b *backend) secretAccessTokenResponse(ctx context.Context, s logical.Storage, tokenGen *TokenGenerator) (*logical.Response, error) { @@ -40,12 +41,17 @@ func (tg *TokenGenerator) getAccessToken(ctx context.Context) (*oauth2.Token, er return nil, errwrap.Wrapf("could not b64-decode key data: {{err}}", err) } - cfg, err := google.JWTConfigFromJSON(jsonBytes, tg.Scopes...) + gcred, err := gauth.DetectDefault(&gauth.DetectOptions{ + Scopes: tg.Scopes, + CredentialsJSON: jsonBytes, + UseSelfSignedJWT: true, + }) if err != nil { return nil, errwrap.Wrapf("could not generate token JWT config: {{err}}", err) } + cfg := oauth2adapt.Oauth2CredentialsFromAuthCredentials(gcred) - tkn, err := cfg.TokenSource(ctx).Token() + tkn, err := cfg.TokenSource.Token() if err != nil { return nil, errwrap.Wrapf("got error while creating OAuth2 token: {{err}}", err) }