Skip to content

Commit

Permalink
feat: Downscope OAuth2 token included in ephemeral certificate (#1450)
Browse files Browse the repository at this point in the history
In the past, when using Auto IAM AuthN, the token embedded into the
ephemeral certificate would have a broad scope. This commit ensures that
in most cases, the embedded token has only a sqlservice.login scope.

When users provide a token to launch the proxy, the proxy will use that
token with whatever scopes are attached.

Fixes #1446.
  • Loading branch information
enocom authored Oct 6, 2022
1 parent 2de4d0a commit 7557a35
Showing 1 changed file with 31 additions and 8 deletions.
39 changes: 31 additions & 8 deletions cmd/cloud_sql_proxy/cloud_sql_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,11 @@ func checkFlags(onGCE bool) error {
return nil
}

// iamLoginScope is the OAuth2 scope attached to tokens which are used for
// database login only. This scope is only applicable when auto IAM authn is
// being used.
const iamLoginScope = "https://www.googleapis.com/auth/sqlservice.login"

func authenticatedClientFromPath(ctx context.Context, f string) (*http.Client, oauth2.TokenSource, error) {
all, err := ioutil.ReadFile(f)
if err != nil {
Expand All @@ -355,38 +360,56 @@ func authenticatedClientFromPath(ctx context.Context, f string) (*http.Client, o
// First try and load this as a service account config, which allows us to see the service account email:
if cfg, err := goauth.JWTConfigFromJSON(all, proxy.SQLScope); err == nil {
logging.Infof("using credential file for authentication; email=%s", cfg.Email)
return cfg.Client(ctx), cfg.TokenSource(ctx), nil
// Created a downscoped token source using the same credentials.
scoped, err := goauth.JWTConfigFromJSON(all, iamLoginScope)
if err != nil {
return nil, nil, err
}
return cfg.Client(ctx), scoped.TokenSource(ctx), nil
}

cred, err := goauth.CredentialsFromJSON(ctx, all, proxy.SQLScope)
if err != nil {
return nil, nil, fmt.Errorf("invalid json file %q: %v", f, err)
}
// Created a downscoped token source using the same credentials.
scoped, err := goauth.CredentialsFromJSON(ctx, all, iamLoginScope)
if err != nil {
return nil, nil, fmt.Errorf("invalid json file %q: %v", f, err)
}
logging.Infof("using credential file for authentication; path=%q", f)
return oauth2.NewClient(ctx, cred.TokenSource), cred.TokenSource, nil
return oauth2.NewClient(ctx, cred.TokenSource), scoped.TokenSource, nil
}

func authenticatedClient(ctx context.Context) (*http.Client, oauth2.TokenSource, error) {
if *tokenFile != "" {
return authenticatedClientFromPath(ctx, *tokenFile)
} else if tok := *token; tok != "" {
}
if tok := *token; tok != "" {
src := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: tok})
return oauth2.NewClient(ctx, src), src, nil
} else if f := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"); f != "" {
}
if f := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"); f != "" {
return authenticatedClientFromPath(ctx, f)
}

// If flags or env don't specify an auth source, try either gcloud or application default
// credentials.
src, err := util.GcloudTokenSource(ctx)
scoped := src
if err != nil {
src, err = goauth.DefaultTokenSource(ctx, proxy.SQLScope)
}
if err != nil {
return nil, nil, err
if err != nil {
return nil, nil, err
}
// Created a downscoped token source using the same credentials.
scoped, err = goauth.DefaultTokenSource(ctx, iamLoginScope)
if err != nil {
return nil, nil, err
}
}

return oauth2.NewClient(ctx, src), src, nil
return oauth2.NewClient(ctx, src), scoped, nil
}

// quotaProjectTransport is an http.RoundTripper that adds an X-Goog-User-Project
Expand Down

0 comments on commit 7557a35

Please sign in to comment.