Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cloud/gcp: add assume role for GCS and GCP KMS #80417

Merged
merged 1 commit into from
May 30, 2022

Conversation

rhu713
Copy link
Contributor

@rhu713 rhu713 commented Apr 22, 2022

Add service account impersonation for GCS and GCP KMS. This is the assume role
equivalent for GCP that allows the service account from implicit or specified
credentials to obtain temporary credentials for another service account.

Release note (enterprise change): Storage and KMS URIs for GCP in Backup and
restore now accept an "ASSUME_ROLE" parameter, which informs the current
service account authenticated by either implicit or specified credentials to
obtain temporary credentials for the service account specified by the
ASSUME_ROLE parameter in order to access the resource specified by the URI.
Below are examples of URIs with "ASSUME_ROLE" parameter:

GCS: gs://<bucket>/<key>?AUTH=specified&ASSUME_ROLE=<service_account>@<project>.iam.gserviceaccount.com&CREDENTIALS=<credentials>
KMS GCP: gs:///<key_resource_name>?AUTH=implicit&ASSUME_ROLE=<service_account>@<project>.iam.gserviceaccount.com

@cockroach-teamcity
Copy link
Member

This change is Reviewable

@rhu713 rhu713 force-pushed the assume-role-gcp branch 4 times, most recently from 46ae65c to 8a99af4 Compare May 2, 2022 12:16
@rhu713 rhu713 marked this pull request as ready for review May 2, 2022 13:55
@rhu713 rhu713 requested a review from a team as a code owner May 2, 2022 13:55
@rhu713 rhu713 requested a review from a team May 2, 2022 13:55
@rhu713 rhu713 requested a review from a team as a code owner May 2, 2022 13:55
@rhu713 rhu713 requested review from msbutler and removed request for a team May 2, 2022 13:55
@rhu713 rhu713 force-pushed the assume-role-gcp branch 2 times, most recently from e274c6e to 23f89f6 Compare May 3, 2022 15:29
@rhu713 rhu713 requested review from adityamaru and removed request for msbutler May 3, 2022 18:03
@rhu713
Copy link
Contributor Author

rhu713 commented May 18, 2022

@adityamaru do you mind reviewing this PR?

@adityamaru
Copy link
Contributor

on it, sorry for the delay

Copy link
Contributor

@adityamaru adityamaru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly nits and questions. Can we edit the Release Note to maybe add an example of what form a URL with ASSUME and TARGET would look like?

@@ -1398,6 +1398,8 @@ message ExternalStorage {
string billing_project = 4;

string credentials = 5;

string target = 6;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a comment describing what target is? I think it's a generic enough word that it might help to provide some gcs specific context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to service account, which should be more self descriptive.

@@ -68,11 +70,21 @@ func MakeGCSKMS(uri string, env cloud.KMSEnv) (cloud.KMS, error) {

// Client options to authenticate and start a GCS KMS session.
// Currently only accepting json of service account.
var credentialsOpt []option.ClientOption
var clientOpts []option.ClientOption
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there any reason to pull this out? It seems like its logic that is specific to only cloud.AuthParamSpecified so should we move it back inside the case stmt for readability or am I missing something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. Reverted this section back to what it was. I originally had this code reused for specified credentials for the assume AUTH but that code turned out to be slightly different in the end.

pkg/cloud/gcp/gcs_kms.go Outdated Show resolved Hide resolved
@@ -43,6 +45,9 @@ const (
// CredentialsParam is the query parameter for the base64-encoded contents of
// the Google Application Credentials JSON file.
CredentialsParam = "CREDENTIALS"
// TargetPrincipalParam is the target principal to impersonate. The target
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just for my edification what is a principal? Do you think it is worth spelling that out here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change this to SERVICE_ACCOUNT actually (and also in other places target is used). I originally wanted to use the same params as the API in the impersonate package but it's probably more clear to just call everything "service account"

pkg/cloud/gcp/gcs_storage.go Outdated Show resolved Hide resolved
@@ -116,26 +122,38 @@ func makeGCSStorage(

switch conf.Auth {
case cloud.AuthParamImplicit:
// Do nothing; use implicit params:
// Use implicit params to access storage or impersonate target:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does AuthParamImplicit interact with impersonating a target? I'm probably missing something here but this case doesn't have fallthrough so implicit is doing what it used to do i.e. not setup any options?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, removed this comment change. There's no interaction with implicit auth and impersonate.

pkg/cloud/gcp/gcs_kms.go Outdated Show resolved Hide resolved

assumeOpt, err := createImpersonateCredentials(ctx, conf.Target, []string{scope}, conf.Credentials)
if err != nil {
return nil, errors.Wrapf(err, "failed to assume %s", conf.Target)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will Target have sensitive information?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm you're right. I suppose that there could be information revealed about what the service account does from its email address. I've removed it from the logging.

Copy link
Contributor Author

@rhu713 rhu713 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed comments. Also modified the release note to add some example URIs with this change.

@@ -68,11 +70,21 @@ func MakeGCSKMS(uri string, env cloud.KMSEnv) (cloud.KMS, error) {

// Client options to authenticate and start a GCS KMS session.
// Currently only accepting json of service account.
var credentialsOpt []option.ClientOption
var clientOpts []option.ClientOption
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. Reverted this section back to what it was. I originally had this code reused for specified credentials for the assume AUTH but that code turned out to be slightly different in the end.

pkg/cloud/gcp/gcs_kms.go Outdated Show resolved Hide resolved
@@ -43,6 +45,9 @@ const (
// CredentialsParam is the query parameter for the base64-encoded contents of
// the Google Application Credentials JSON file.
CredentialsParam = "CREDENTIALS"
// TargetPrincipalParam is the target principal to impersonate. The target
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change this to SERVICE_ACCOUNT actually (and also in other places target is used). I originally wanted to use the same params as the API in the impersonate package but it's probably more clear to just call everything "service account"

@@ -116,26 +122,38 @@ func makeGCSStorage(

switch conf.Auth {
case cloud.AuthParamImplicit:
// Do nothing; use implicit params:
// Use implicit params to access storage or impersonate target:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, removed this comment change. There's no interaction with implicit auth and impersonate.


assumeOpt, err := createImpersonateCredentials(ctx, conf.Target, []string{scope}, conf.Credentials)
if err != nil {
return nil, errors.Wrapf(err, "failed to assume %s", conf.Target)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm you're right. I suppose that there could be information revealed about what the service account does from its email address. I've removed it from the logging.

pkg/cloud/gcp/gcs_storage.go Outdated Show resolved Hide resolved
@@ -1398,6 +1398,8 @@ message ExternalStorage {
string billing_project = 4;

string credentials = 5;

string target = 6;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to service account, which should be more self descriptive.

// ServiceAccountParam is the email of the service account to impersonate.
// This service account should have the necessary access to the relevant cloud
// services.
ServiceAccountParam = "SERVICE_ACCOUNT"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I definitely think ServiceAccount is clearer than target but does this now give the impression that we are passing the service account that will be used to access cloud services? I guess in a way we are but should we be more explicit and call it ASSUMED_SERVICE_ACCOUNT or something? Might also be worth asking a few more folks for a bikeshed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, now we are using the param ASSUME_ROLE and not using an additional auth method

@@ -1424,6 +1424,8 @@ message ExternalStorage {
string billing_project = 4;

string credentials = 5;

string service_account = 6;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

depending on how we address the comment on the URI param name we should change this accordingly. A comment might still be nice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added comment

pkg/cloud/gcp/gcs_storage_test.go Show resolved Hide resolved
go.mod Outdated
@@ -5,7 +5,7 @@ go 1.17
require (
cloud.google.com/go/kms v1.1.0
cloud.google.com/go/pubsub v1.16.0
cloud.google.com/go/storage v1.21.0
cloud.google.com/go/storage v1.22.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just checking what motivated this bump?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no longer bumped with the separate vendor PR

@adityamaru adityamaru self-requested a review May 20, 2022 13:40
@rhu713 rhu713 force-pushed the assume-role-gcp branch 5 times, most recently from 3f4b9fd to 61c1cc4 Compare May 25, 2022 15:23
@rhu713
Copy link
Contributor Author

rhu713 commented May 25, 2022

@adityamaru this should be ready FAL

credentialsOpt = append(credentialsOpt, authOption)
}

opts := []option.ClientOption{option.WithScopes(scope)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we add a quick comment mentioning that once we have resolved AUTH as implicit or specified we check if we need to use these creds to assume a role.

m.Go(func(ctx context.Context) error {
t.Status(`running backup`)
c.Run(ctx, c.Node(1), fmt.Sprintf(
"./cockroach sql --insecure -e \"BACKUP bank.bank TO '%s' WITH KMS='%s'\"",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we use the BACKUP INTO syntax? We want to get rid of TO soonish so we'll have to cleanup all roachtests eventually.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it useful to RESTORE from the backup and check if the table has expected rows as well? It'll be a more complete test imo, wdyt?

@adityamaru
Copy link
Contributor

nit: We should update PR description with updated commit message

Add service account impersonation for GCS and GCP KMS. This is the assume role
equivalent for GCP that allows the service account from implicit or specified
credentials to obtain temporary credentials for another service account.

Release note (enterprise change): Storage and KMS URIs for GCP in Backup and
restore now accept an "ASSUME_ROLE" parameter, which informs the current
service account authenticated by either implicit or specified credentials to
obtain temporary credentials for the service account specified by the
ASSUME_ROLE parameter in order to access the resource specified by the URI.
Below are examples of URIs with "ASSUME_ROLE" parameter:

```
GCS: gs://<bucket>/<key>?AUTH=specified&ASSUME_ROLE=<service_account>@<project>.iam.gserviceaccount.com&CREDENTIALS=<credentials>
KMS GCP: gs:///<key_resource_name>?AUTH=implicit&ASSUME_ROLE=<service_account>@<project>.iam.gserviceaccount.com
```
@rhu713
Copy link
Contributor Author

rhu713 commented May 30, 2022

bors r+

@craig
Copy link
Contributor

craig bot commented May 30, 2022

Build succeeded:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants