Skip to content

Commit

Permalink
feat: add support for service account impersonation
Browse files Browse the repository at this point in the history
  • Loading branch information
enocom committed Dec 7, 2022
1 parent de15073 commit 666c7ee
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 1 deletion.
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ the maximum time has passed. Defaults to 0s.`)
cmd.PersistentFlags().StringVar(&c.conf.FUSEDir, "fuse", "",
"Mount a directory at the path using FUSE to access Cloud SQL instances.")
cmd.PersistentFlags().StringVar(&c.conf.FUSETempDir, "fuse-tmp-dir",
filepath.Join(os.TempDir(), "csql-tmp"),
filepath.Join(os.TempDir(), "alloydb-tmp"),
"Temp dir for Unix sockets created with FUSE")
cmd.PersistentFlags().StringVar(&c.impersonationChain, "impersonate-service-account", "",
`Comma separated list of service accounts to impersonate. Last value
Expand Down
78 changes: 78 additions & 0 deletions internal/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"cloud.google.com/go/alloydbconn"
"github.com/GoogleCloudPlatform/alloydb-auth-proxy/alloydb"
"github.com/GoogleCloudPlatform/alloydb-auth-proxy/internal/gcloud"
"github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cloudsql"
"golang.org/x/oauth2"
"google.golang.org/api/impersonate"
"google.golang.org/api/option"
Expand Down Expand Up @@ -119,6 +120,83 @@ type Config struct {
// StructuredLogs sets all output to use JSON in the LogEntry format.
// See https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
StructuredLogs bool

// ImpersonateTarget is the service account to impersonate. The IAM
// principal doing the impersonation must have the
// roles/iam.serviceAccountTokenCreator role.
ImpersonateTarget string
// ImpersonateDelegates are the intermediate service accounts through which
// the impersonation is achieved. Each delegate must have the
// roles/iam.serviceAccountTokenCreator role.
ImpersonateDelegates []string
}

func (c *Config) credentialsOpt(l cloudsql.Logger) (alloydbconn.Option, error) {
// If service account impersonation is configured, set up an impersonated
// credentials token source.
if c.ImpersonateTarget != "" {
var iopts []option.ClientOption
switch {
case c.Token != "":
l.Infof("Impersonating service account with OAuth2 token")
iopts = append(iopts, option.WithTokenSource(
oauth2.StaticTokenSource(&oauth2.Token{AccessToken: c.Token}),
))
case c.CredentialsFile != "":
l.Infof("Impersonating service account with the credentials file at %q", c.CredentialsFile)
iopts = append(iopts, option.WithCredentialsFile(c.CredentialsFile))
case c.CredentialsJSON != "":
l.Infof("Impersonating service account with JSON credentials environment variable")
iopts = append(iopts, option.WithCredentialsJSON([]byte(c.CredentialsJSON)))
case c.GcloudAuth:
l.Infof("Impersonating service account with gcloud user credentials")
ts, err := gcloud.TokenSource()
if err != nil {
return nil, err
}
iopts = append(iopts, option.WithTokenSource(ts))
default:
l.Infof("Impersonating service account with Application Default Credentials")
}
ts, err := impersonate.CredentialsTokenSource(
context.Background(),
impersonate.CredentialsConfig{
TargetPrincipal: c.ImpersonateTarget,
Delegates: c.ImpersonateDelegates,
Scopes: []string{sqladmin.SqlserviceAdminScope},
},
iopts...,
)
if err != nil {
return nil, err
}
return alloydbconn.WithTokenSource(ts), nil
}
// Otherwise, configure credentials as usual.
switch {
case c.Token != "":
l.Infof("Authorizing with OAuth2 token")
return alloydbconn.WithTokenSource(
oauth2.StaticTokenSource(&oauth2.Token{AccessToken: c.Token}),
), nil
case c.CredentialsFile != "":
l.Infof("Authorizing with the credentials file at %q", c.CredentialsFile)
return alloydbconn.WithCredentialsFile(c.CredentialsFile), nil
case c.CredentialsJSON != "":
l.Infof("Authorizing with JSON credentials environment variable")
return alloydbconn.WithCredentialsJSON([]byte(c.CredentialsJSON)), nil
case c.GcloudAuth:
l.Infof("Authorizing with gcloud user credentials")
ts, err := gcloud.TokenSource()
if err != nil {
return nil, err
}
return alloydbconn.WithTokenSource(ts), nil
default:
l.Infof("Authorizing with Application Default Credentials")
// Return no-op options to avoid having to handle nil in caller code
return alloydbconn.WithOptions(), nil
}
}

func (c *Config) credentialsOpt(l alloydb.Logger) (alloydbconn.Option, error) {
Expand Down

0 comments on commit 666c7ee

Please sign in to comment.