Skip to content

Commit 5364455

Browse files
Log identities executing changes in terraform (#4620) (#3266)
Co-authored-by: upodroid <[email protected]> Signed-off-by: Modular Magician <[email protected]> Co-authored-by: upodroid <[email protected]>
1 parent 7d8a1ec commit 5364455

File tree

5 files changed

+78
-23
lines changed

5 files changed

+78
-23
lines changed

.changelog/4620.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
```release-note:none
2+
```

google-beta/config.go

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ func (c *Config) LoadAndValidate(ctx context.Context) error {
271271

272272
c.context = ctx
273273

274-
tokenSource, err := c.getTokenSource(c.Scopes)
274+
tokenSource, err := c.getTokenSource(c.Scopes, false)
275275
if err != nil {
276276
return err
277277
}
@@ -283,6 +283,12 @@ func (c *Config) LoadAndValidate(ctx context.Context) error {
283283
// 1. OAUTH2 TRANSPORT/CLIENT - sets up proper auth headers
284284
client := oauth2.NewClient(cleanCtx, tokenSource)
285285

286+
// Userinfo is fetched before request logging is enabled to reduce additional noise.
287+
err = c.logGoogleIdentities()
288+
if err != nil {
289+
return err
290+
}
291+
286292
// 2. Logging Transport - ensure we log HTTP requests to GCP APIs.
287293
loggingTransport := logging.NewTransport("Google", client.Transport)
288294

@@ -359,8 +365,57 @@ func (c *Config) synchronousTimeout() time.Duration {
359365
return c.RequestTimeout
360366
}
361367

362-
func (c *Config) getTokenSource(clientScopes []string) (oauth2.TokenSource, error) {
363-
creds, err := c.GetCredentials(clientScopes)
368+
// Print Identities executing terraform API Calls.
369+
func (c *Config) logGoogleIdentities() error {
370+
if c.ImpersonateServiceAccount == "" {
371+
372+
tokenSource, err := c.getTokenSource(c.Scopes, true)
373+
if err != nil {
374+
return err
375+
}
376+
c.client = oauth2.NewClient(c.context, tokenSource) // c.client isn't initialised fully when this code is called.
377+
378+
email, err := GetCurrentUserEmail(c, c.userAgent)
379+
if err != nil {
380+
log.Printf("[INFO] error retrieving userinfo for your provider credentials. have you enabled the 'https://www.googleapis.com/auth/userinfo.email' scope? error: %s", err)
381+
}
382+
383+
log.Printf("[INFO] Terraform is using this identity: %s", email)
384+
385+
return nil
386+
387+
}
388+
389+
// Drop Impersonated ClientOption from OAuth2 TokenSource to infer original identity
390+
391+
tokenSource, err := c.getTokenSource(c.Scopes, true)
392+
if err != nil {
393+
return err
394+
}
395+
c.client = oauth2.NewClient(c.context, tokenSource) // c.client isn't initialised fully when this code is called.
396+
397+
email, err := GetCurrentUserEmail(c, c.userAgent)
398+
if err != nil {
399+
log.Printf("[INFO] error retrieving userinfo for your provider credentials. have you enabled the 'https://www.googleapis.com/auth/userinfo.email' scope? error: %s", err)
400+
}
401+
402+
log.Printf("[INFO] Terraform is configured with service account impersonation, original identity: %s, impersonated identity: %s", email, c.ImpersonateServiceAccount)
403+
404+
// Add the Impersonated ClientOption back in to the OAuth2 TokenSource
405+
406+
tokenSource, err = c.getTokenSource(c.Scopes, false)
407+
if err != nil {
408+
return err
409+
}
410+
c.client = oauth2.NewClient(c.context, tokenSource) // c.client isn't initialised fully when this code is called.
411+
412+
return nil
413+
}
414+
415+
// Get a TokenSource based on the Google Credentials configured.
416+
// If initialCredentialsOnly is true, don't follow the impersonation settings and return the initial set of creds.
417+
func (c *Config) getTokenSource(clientScopes []string, initialCredentialsOnly bool) (oauth2.TokenSource, error) {
418+
creds, err := c.GetCredentials(clientScopes, initialCredentialsOnly)
364419
if err != nil {
365420
return nil, fmt.Errorf("%s", err)
366421
}
@@ -877,7 +932,10 @@ type staticTokenSource struct {
877932
oauth2.TokenSource
878933
}
879934

880-
func (c *Config) GetCredentials(clientScopes []string) (googleoauth.Credentials, error) {
935+
// Get a set of credentials with a given scope (clientScopes) based on the Config object.
936+
// If initialCredentialsOnly is true, don't follow the impersonation settings and return the initial set of creds
937+
// instead.
938+
func (c *Config) GetCredentials(clientScopes []string, initialCredentialsOnly bool) (googleoauth.Credentials, error) {
881939

882940
if c.AccessToken != "" {
883941
contents, _, err := pathOrContents(c.AccessToken)
@@ -886,7 +944,7 @@ func (c *Config) GetCredentials(clientScopes []string) (googleoauth.Credentials,
886944
}
887945
token := &oauth2.Token{AccessToken: contents}
888946

889-
if c.ImpersonateServiceAccount != "" {
947+
if c.ImpersonateServiceAccount != "" && !initialCredentialsOnly {
890948
opts := []option.ClientOption{option.WithTokenSource(oauth2.StaticTokenSource(token)), option.ImpersonateCredentials(c.ImpersonateServiceAccount, c.ImpersonateServiceAccountDelegates...), option.WithScopes(clientScopes...)}
891949
creds, err := transport.Creds(context.TODO(), opts...)
892950
if err != nil {
@@ -908,7 +966,7 @@ func (c *Config) GetCredentials(clientScopes []string) (googleoauth.Credentials,
908966
if err != nil {
909967
return googleoauth.Credentials{}, fmt.Errorf("error loading credentials: %s", err)
910968
}
911-
if c.ImpersonateServiceAccount != "" {
969+
if c.ImpersonateServiceAccount != "" && !initialCredentialsOnly {
912970
opts := []option.ClientOption{option.WithCredentialsJSON([]byte(contents)), option.ImpersonateCredentials(c.ImpersonateServiceAccount, c.ImpersonateServiceAccountDelegates...), option.WithScopes(clientScopes...)}
913971
creds, err := transport.Creds(context.TODO(), opts...)
914972
if err != nil {
@@ -926,7 +984,7 @@ func (c *Config) GetCredentials(clientScopes []string) (googleoauth.Credentials,
926984
return *creds, nil
927985
}
928986

929-
if c.ImpersonateServiceAccount != "" {
987+
if c.ImpersonateServiceAccount != "" && !initialCredentialsOnly {
930988
opts := option.ImpersonateCredentials(c.ImpersonateServiceAccount, c.ImpersonateServiceAccountDelegates...)
931989
creds, err := transport.Creds(context.TODO(), opts, option.WithScopes(clientScopes...))
932990
if err != nil {

google-beta/data_source_google_service_account_id_token.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func dataSourceGoogleServiceAccountIdTokenRead(d *schema.ResourceData, meta inte
6969
}
7070

7171
targetAudience := d.Get("target_audience").(string)
72-
creds, err := config.GetCredentials([]string{userInfoScope})
72+
creds, err := config.GetCredentials([]string{userInfoScope}, false)
7373
if err != nil {
7474
return fmt.Errorf("error calling getCredentials(): %v", err)
7575
}

google-beta/userinfo.go

Lines changed: 0 additions & 15 deletions
This file was deleted.

google-beta/utils.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,16 @@ func multiEnvSearch(ks []string) string {
491491
return ""
492492
}
493493

494+
func GetCurrentUserEmail(config *Config, userAgent string) (string, error) {
495+
// See https://github.com/golang/oauth2/issues/306 for a recommendation to do this from a Go maintainer
496+
// URL retrieved from https://accounts.google.com/.well-known/openid-configuration
497+
res, err := sendRequest(config, "GET", "", "https://openidconnect.googleapis.com/v1/userinfo", userAgent, nil)
498+
if err != nil {
499+
return "", fmt.Errorf("error retrieving userinfo for your provider credentials. have you enabled the 'https://www.googleapis.com/auth/userinfo.email' scope? error: %s", err)
500+
}
501+
return res["email"].(string), nil
502+
}
503+
494504
func checkStringMap(v interface{}) map[string]string {
495505
m, ok := v.(map[string]string)
496506
if ok {

0 commit comments

Comments
 (0)