A Hitchhiker’s Guide to GCP Service Account Impersonation in Terraform

Garrett Wong
Google Cloud - Community
6 min readMar 17, 2020

One of the most common GCP questions I continue to hear around Secrets Management is minimizing risk and reducing overall attack surface when using service account keys. If you have used Google Cloud Platform, it is quite likely that you have generated at least one, if not many service account keys and stored the files locally, in buckets, or in Vault (+1 for storing them here). One of the topics I wanted to cover is around minimizing potential service account key exposure through discussing best practices around the introduction and operationalization of Service Account Impersonation.

One of the primary use cases for GCP Service Account Key usage happens to be the plethora of Terraform examples out there, suggesting that you initialize the provider with the credentials property as referenced below.

provider “google” {
credentials = file(“account.json”)
project = “my-project-id”
region = “us-central1”
}

This suggests the necessity for both the generation of a USER_MANAGED service account key file AND the storage of that key file locally on the user’s device.

Self-Managed Keys poses a few potential risks:

  • Possibility of the Service Account Key getting committed into Github or related VCS
  • Service Account Key Files floating around on user’s laptops
  • Potential overlook of proper governance standards around the management of Service Account Keys
  • Potential for generating multiple keys for the same set of service accounts without proper Service Account Key clean up

But hey. Infrastructure as Code is a recommended approach, and if I have to run Terraform, I need to leverage a locally-stored Service Account Key. Right? No, not quite.

As a direct alternative, we’ll bring Service Account Impersonation into the mix. It can be leveraged to remove the need for having service account key files. In this blog, we’ll visit scenarios specifically revolving around running Terraform. Service Account Impersonation can be conducted via a User or a Service Account, as long as the appropriate roles are granted.

Let’s look at some benefits of leveraging Service Account Impersonation:

  • Reduce attack surface by eliminating Service Account Keys (for Terraform)
  • Clearly identify who (group, user, service account) should have the ability to impersonate higher privileged accounts
  • Rely on the Security around User Authentication rather than a Key File (which generally involves Multi-Factor Authentication)
  • Rely on Google Managed Service Account Keys
  • Works in conjunction with Short Lived Credentials, allowing time-limited access to roles that Service Account has.

So we’re using Service Account Keys today to run Terraform… how do we transition?

There are three steps that I’ll highlight.

Step 1. Setup Considerations

Let’s assume that we have a Service Account for Infrastructure Deployment (via Terraform) in our GCP project today. That account generally will have a higher set of privileges.

  1. Remove existing USER_MANAGED keys specific to Terraform Service Accounts within your GCP project
  2. Next, remove the ability to generate service account keys within your GCP project
  3. We want to remove roles such as roles/owner, roles/editor, or roles/iam.serviceAccountKeyAdmin
  4. Identify the User, Group, or Service Account that should have access to impersonate and grant it the roles, roles/iam.serviceAccountTokenCreator on the Terraform Service Account’s IAM Policy.
    Note: This is not a Project IAM Binding; this is a Service Account IAM Binding.
  5. Assuming we already have a terraform service account defined with “enough permissions” to deploy infrastructure, we will designate that account as the account that we will impersonate.
  • Click `ADD MEMBER (on the info panel on the right-hand side of the page)
  • Add the associated Group, User, or Service Account, as a member and add the two roles: roles/iam.serviceAccountTokenCreator.

Step 2. Update and Run your Terraform Code

Now that we’ve walked through the above steps, let’s update our Terraform Code. A set of simple steps to our sample main.tf file will kickstart us into leveraging impersonation.

  1. Use Application Default Credentials (ADC)
  • You’ll need to authenticate as the user or service account that has permissions to impersonate the Terraform Service Account. Terraform will execute as your ADC after you sign in using gcloud auth application-default login.

2. Impersonate the Service Account for a Limited Time

  • A few cookie cutter provider definitions need to be updated to reference the google.tokengen provider. Additionally, on line 12, within the “google_service_account_access_token” block, there is a `lifetime` property which allows us to specify the length of time the access token requested during impersonation will last for. Depending on the size of the Infrastructure Deployment, we may want to modify the lifetime accordingly. For the majority of cases, impersonating the service account with an access token for 600s or 10 minutes, will be more than enough.
provider "google" {
version = "~> 2.0, >= 2.5.1"
alias = "tokengen"
}
data "google_client_config" "default" {
provider = "google.tokengen"
}
data "google_service_account_access_token" "sa" {
provider = "google.tokengen"
target_service_account = "terraform@my-project-id.iam.gserviceaccount.com"
lifetime = "600s"
scopes = [
"https://www.googleapis.com/auth/cloud-platform",
]
}
/******************************************
GA Provider configuration
*****************************************/
provider "google" {
version = "~> 2.0, >= 2.5.1"
access_token = data.google_service_account_access_token.sa.access_token
project = "my-project-id"
}
/******************************************
Beta Provider configuration
*****************************************/
provider "google-beta" {
version = "~> 2.0, >= 2.5.1"
access_token = data.google_service_account_access_token.sa.access_token
project = "my-project-id"
}
resource "google_storage_bucket" "test" {
name = "my-project-id-test-bucket"
location = "us-west1"
}
  • And as consolation, we’ll deploy a simple GCS test bucket.

3. Now you’re ready to run your Terraform Code.

  • `terraform init`
  • `terraform plan`
  • `terraform apply`

You should now have a GCS bucket!

Step 3. Audit

And just so we do not forget, let’s ensure that we are able to verify a proper audit trail when users begin impersonating service accounts (Generating Access Tokens).

  • We first, should confirm that Data Access Logs are enabled for our Project.
  • Second, simply navigate over to Stackdriver > Logging and run a query, similar to what is shown below:
terraform@[MY-PROJECT-ID].iam.gserviceaccount.com AND logName=”projects/[MY-PROJECT-ID]/logs/cloudaudit.googleapis.com%2Fdata_access” AND protoPayload.methodName = “GenerateAccessToken”
  • Next, we’ll get a response containing aa set of logs containing details on when the IAM Service Account Credentials API was triggered and when temporary access tokens have been generated.

In Conclusion

In wrapping up, I wanted to highlight the benefits and a high-level overview around the operationalization of Service Account Impersonation within your GCP environment. Service Account Impersonation enables us to rely on Google Managed Keys when it comes to leveraging Service Accounts used for Terraform Infrastructure Deployment purposes.

Another major benefit is it removes the onus on the users from implementing key management processes, around key rotation, creation and deletion. Configuration of Service Account Impersonation also forces us to consider which accounts should be able to leverage the more privileged service accounts within our projects, and better positions us to think about implementing least privilege within our projects. There are a number of other benefits and quite a low overhead in implementing Service Account Impersonation, so I recommend you give it a run.

--

--

Garrett Wong
Google Cloud - Community

Google Cloud Strategic Cloud Engineer, 11x GCP certified