hashicorp / terraform-provider-google

Terraform Provider for Google Cloud Platform
https://registry.terraform.io/providers/hashicorp/google/latest/docs
Mozilla Public License 2.0
2.31k stars 1.73k forks source link

Cannont generate Signed URL though google_service_account_access_token #3558

Open salrashid123 opened 5 years ago

salrashid123 commented 5 years ago

google_service_account_access_token allows one service account to impersonate another within a template.

However, when used with google_storage_object_signed_url, the impersonated credential lacks a local service account signing object (json cert, .p12, pem) thats used sign.

That is, the following config

provider "google" {}

data "google_client_config" "default" {
  provider = "google"
}

data "google_service_account_access_token" "default" {
 provider = "google"
 target_service_account = "impersonated-account@fabled-ray-104117.iam.gserviceaccount.com"
 scopes = ["userinfo-email", "cloud-platform"]
 lifetime = "300s"
}

provider "google" {
   alias  = "impersonated"
   access_token = "${data.google_service_account_access_token.default.access_token}"
}

data "google_client_openid_userinfo" "me" {
  provider = "google.impersonated"
}

data "google_storage_object_signed_url" "artifact" {
  provider = "google.impersonated"
  bucket = "fabled-ray-104117"
  path   = "signed_url_file.txt"
}

output "target-email" {
  value = "${data.google_client_openid_userinfo.me.email}"
}

output "signedurl" {
  value = "${data.google_storage_object_signed_url.artifact.signed_url}"
}

when used with

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/source_svc_account.json
(which is svc account for source_service_account@PROJECTA.iam.gserviceaccount.com)

gives a signedurl based off of the source account (not impersonated account

Outputs:

signedurl = https://storage.googleapis.com/fabled-ray-104117/signed_url_file.txt?GoogleAccessId=source_service_account@PROJECTA.iam.gserviceaccount.com&Expires=1557003947&Signature<redactd>

target-email = impersonated-account@fabled-ray-104117.iam.gserviceaccount.com

when used with

export GOOGLE_CLOUD_KEYFILE_JSON=/path/to/source_svc_account.json

gives

Error: Error refreshing state: 1 error(s) occurred:

* data.google_storage_object_signed_url.artifact: 1 error(s) occurred:

* data.google_storage_object_signed_url.artifact: data.google_storage_object_signed_url.artifact: Credentials not found in datasource, provider configuration or GOOGLE_APPLICATION_CREDENTIALS environment variable.

Baiscally, signedurl expects a local signing key. Potential solution wouldbe to 'remotely sign' via iamcredentials.signBlob() as described here:

Affected Resource(s)

edwardmedia commented 4 years ago

@salrashid123 I followed your steps and can't repro the Error. Both return the expected outputs. I am using terraform-provider-google_v3.4.0. What version were you using? If your versions were old, could you please upgrade and then try it again? I am closing the issue. Please reopen it if you still experience the issue or you need further discussion. Thanks

salrashid123 commented 4 years ago

yeah, i can still repro this even with

$ terraform version
Terraform v0.12.19
+ provider.google v3.4.0

By expected output do you mean an actual signedURL issed by the impersonated service account as intended above? i.,e with


The code in https://github.com/terraform-providers/terraform-provider-google/blob/master/google/data_source_storage_object_signed_url.go#L144 looks for a service accoun'ts json file that will perform the actual signing

https://github.com/terraform-providers/terraform-provider-google/blob/master/google/data_source_storage_object_signed_url.go#L344

...but what the impersonated credential that is supposed to do the signing doens't have a cert ...ther's no way it can sign..the only way maybe if the impersonated credential uses the IAM API to 'sign for itself'

salrashid123 commented 4 years ago

@edwardmedia could you reopen this one? its a pretty uncommon usage but its still an issue

hiloboy0119 commented 4 years ago

@edwardmedia I am running into this same issue, trying to deploy forseti on gke using service account impersonation

salrashid123 commented 4 years ago

here is an example of generating signedurl with iamcredentials signblob:

https://gist.github.com/salrashid123/b8cb77bd0119f3b48610a4d9f16cb167

in this case, the terraform signedurl function would accept the new parameters below and elect to use iamcredentials instead of the certificate file

data "google_storage_object_signed_url" "artifact" {
  provider = "google.impersonated"
  bucket = "fabled-ray-104117"
  path   = "signed_url_file.txt"

      targetPrincipal = "impersonated-account@your_project.iam.gserviceaccount.com"
     delegates := []string{}
    expires  = 1580735835
}

there are probably cleaner ways but if the last three prameters are set, youcan use iamcredentials.

edwardmedia commented 4 years ago

I do see the impersonated-account is not the one used for the GoogleAccessId in the signedurl

shanemcd commented 4 years ago

I also just ran into this when attempting to run terraform on a GCE instance w/ an associated service account. I unfortunately had to upload a key file and use GOOGLE_APPLICATION_CREDENTIALS.

emilymye commented 4 years ago

As is, you can use a service account key instead of an google_service_account_access_token and provide it as the credentials field.

I do also see the benefit of not requiring a local key/exposing that potentially in state. We could treat this as a datasource that just calls iam.SignBlob on a URL for the provider credentials, instead of trying to use a local private key.

If we added a service_account field, that should be enough to indicate we want to use signBlob against that service account. Otherwise, if credentials are given, use those to sign. If neither are given, use the local environment key to sign.

Work would just entail adding this if-else logic and a function that calls signBlob

salrashid123 commented 4 years ago

i don't think you an just use the service account name field alone to do this since the actual API could potentially several additional parameter for it to work (eg if the impersonation requires chained delegation, you wouln't know which ones to supply in request

benhxy commented 11 months ago

You seem to want to create a SignedURL without service account key, which can be done by IAMCredential.SignBlob API. Instead of Terraform (which is more a infra tool), can you use gcloud/Python SDK instead? Those tools are more suitable for this type of workflows.

melinath commented 3 months ago

Note from triage: We're not entirely sure what the best way to handle this is, so will leave it up to the product team to decide how to proceed. However, it does seem like a thing that would reasonably make sense to support in Terraform.

@benhxy many Terraform-using companies require Terraform to be used for all API interactions, so a gcloud-based workaround may not be sufficient.