diff --git a/blob/config.go b/blob/config.go index 7fd87be97..f28611063 100644 --- a/blob/config.go +++ b/blob/config.go @@ -22,7 +22,9 @@ const ( ) type Config struct { - Provider Provider - Bucket string - KeyPath string + Provider Provider + Bucket string + KeyPath string + TargetPrincipal string + ImpersonationLifetime int } diff --git a/blob/gcs.go b/blob/gcs.go index ade73421e..65324a005 100644 --- a/blob/gcs.go +++ b/blob/gcs.go @@ -23,9 +23,13 @@ import ( "cloud.google.com/go/storage" "github.com/rs/zerolog/log" + "google.golang.org/api/impersonate" "google.golang.org/api/option" ) +// scopes best practice: https://cloud.google.com/compute/docs/access/service-accounts#scopes_best_practice +const defaultScope = "https://www.googleapis.com/auth/cloud-platform" + type GCSStore struct { // Bucket is the name of the GCS bucket to use. bucket string @@ -45,11 +49,20 @@ func NewGCSStore(cfg Config) (Store, error) { }, nil } - // Use workload identity default credentials (GKE environment) - client, err := storage.NewClient(context.Background()) + // Use workload identity impersonation default credentials (GKE environment) + ts, err := impersonate.CredentialsTokenSource(context.Background(), impersonate.CredentialsConfig{ + TargetPrincipal: cfg.TargetPrincipal, + Scopes: []string{defaultScope}, // Required field + Lifetime: time.Duration(cfg.ImpersonationLifetime) * time.Hour, + }) if err != nil { - return nil, fmt.Errorf("failed to create GCS client with workload identity or default credentials: %w", err) + return nil, fmt.Errorf("failed to impersonate the client service account %s : %w", cfg.TargetPrincipal, err) } + client, err := storage.NewClient(context.Background(), option.WithTokenSource(ts)) + if err != nil { + return nil, fmt.Errorf("failed to create GCS client with workload identity impersonation: %w", err) + } + return &GCSStore{ bucket: cfg.Bucket, client: client, diff --git a/cli/server/config.go b/cli/server/config.go index 6fca996b5..ef8d47da8 100644 --- a/cli/server/config.go +++ b/cli/server/config.go @@ -247,9 +247,11 @@ func ProvideBlobStoreConfig(config *types.Config) (blob.Config, error) { config.BlobStore.Bucket = filepath.Join(homedir, gitnessHomeDir, blobDir) } return blob.Config{ - Provider: config.BlobStore.Provider, - Bucket: config.BlobStore.Bucket, - KeyPath: config.BlobStore.KeyPath, + Provider: config.BlobStore.Provider, + Bucket: config.BlobStore.Bucket, + KeyPath: config.BlobStore.KeyPath, + TargetPrincipal: config.BlobStore.TargetPrincipal, + ImpersonationLifetime: config.BlobStore.ImpersonationLifetime, }, nil } diff --git a/types/config.go b/types/config.go index 53171bd70..310b8c0af 100644 --- a/types/config.go +++ b/types/config.go @@ -155,6 +155,11 @@ type Config struct { // In case of GCS provider, this is expected to be the path to the service account key file. KeyPath string `envconfig:"GITNESS_BLOBSTORE_KEY_PATH" default:""` + + // Email ID of the google service account that needs to be impersonated + TargetPrincipal string `envconfig:"GITNESS_BLOBSTORE_TARGET_PRINCIPAL" default:""` + + ImpersonationLifetime int `envconfig:"GITNESS_BLOBSTORE_IMPERSONATION_LIFETIME" default:"12"` } // Token defines token configuration parameters.