Introduction
In the past, when you wanted to allow an application like GitHub to access your Google Cloud resources, you had to follow a few steps. First, you would create a separate IAM service account for each application. Then, you would download a long-lasting JSON service account key and store it in your GitHub repository's secrets. These service account keys could then be used to make GCP API calls from your application.
Managing these service account keys involved secure storage, regular rotation, and continuous monitoring to protect your Google resources from potential malicious actors. If a malicious actor gained access to your keys, they could maintain prolonged access to your Google Cloud resources. This is why this approach is not recommended.
So, what's the solution? The answer is Workload Identity Federation (WIF).
Through the Workload Identity Federation (WIF), you can grant external identities IAM roles, by allowing them to impersonate service accounts that have these roles.
This approach is superior for the following reasons:
You no longer need to manage key rotation or store service account keys.
The credentials have a short default lifespan and are configurable. This approach simplifies the management and security aspects associated with service account keys.
Steps to Set Up Workload Identity Federation
Step 1: Create a Workload Identity Pool
According to the documentation, a Workload Identity Pool (WIP) is an entity that lets you manage external identities. In general, GCP recommends creating a new pool for each non-Google Cloud environment that needs to access Google Cloud resources, such as development, staging, or production environments.
In this step, we are defining the locals to be used, and we are using a unique name for the Workload Identity Pool (WIP).
locals {
project_id = "PROJECT_ID"
service_account = "SERVICE_ACCOUNT"
organization = "YOUR_ORGANISATION_HERE"
repository = "YOUR_REPOSITORY_HERE"
}
resource "google_iam_workload_identity_pool" "github_pool" {
project = local.project_id
workload_identity_pool_id = "github-pool-oidc"
display_name = "GitHub pool"
description = "Identity pool for GitHub deployments"
}
Step 2: Configure the Workload Identity Pool Provider
In this step, we configure the Workload Identity Pool provider. Your provider should have a unique name. Then, we can set the "Attribute Mapping" to map the GitHub Actions attributes to GCP identity attributes.
Here, we can also define an "Attribute Condition" which is recommended to be more secure. This condition specifies when the identity provider should be used based on GitHub repository attributes, for example, repository name, repository owner, actor, and others.
We also set up the OIDC settings by providing the correct issuer URI.
resource "google_iam_workload_identity_pool_provider" "github" {
project = local.project_id
workload_identity_pool_id = google_iam_workload_identity_pool.github_pool.workload_identity_pool_id
workload_identity_pool_provider_id = "github-provider"
attribute_mapping = {
"attribute.aud" = "assertion.aud"
"google.subject" = "assertion.sub"
"attribute.sub" = "assertion.sub"
"attribute.actor" = "assertion.actor"
"attribute.repository" = "assertion.repository"
"attribute.repository_owner" = "assertion.repository_owner"
"attribute.ref" = "assertion.ref"
}
# If you want to restrict to organisation, use:
# "assertion.repository_owner==\"${local.organization}\""
# For more than one repository, use:
# "assertion.repository==\"ORG/repository-1\" || assertion.repository==\"ORG/repository-2\""
attribute_condition = "assertion.repository==\"${local.organization}/${local.repository}\""
oidc {
allowed_audiences = []
issuer_uri = "https://token.actions.githubusercontent.com"
}
}
Step 3: Configure IAM Roles
In this step, we configure the GCP IAM role for our service account. This can be done by setting the roles/iam.workloadIdentityUser to the service account we want to impersonate.
What this role does is to allow the associated service account to assume identities from a trusted identity provider to access Google Cloud resources which this service account has permission for.
Step 5: Full Terraform code
To sum up, this is the full terraform code we needed for the setup.
# wif.tf
locals {
project_id = "PROJECT_ID"
service_account = "SERVICE_ACCOUNT"
project_name = "YOUR_PROJECT_NAME"
organization = "YOUR_ORGANISATION_HERE"
repository = "YOUR_REPOSITORY_HERE"
}
resource "google_iam_workload_identity_pool" "github_pool" {
project = local.project_id
workload_identity_pool_id = "github-pool-oidc"
display_name = "GitHub pool"
description = "Identity pool for GitHub deployments"
}
resource "google_iam_workload_identity_pool_provider" "github" {
project = local.project_id
workload_identity_pool_id = google_iam_workload_identity_pool.github_pool.workload_identity_pool_id
workload_identity_pool_provider_id = "github-provider"
attribute_mapping = {
"attribute.aud" = "assertion.aud"
"google.subject" = "assertion.sub"
"attribute.sub" = "assertion.sub"
"attribute.actor" = "assertion.actor"
"attribute.repository" = "assertion.repository"
"attribute.repository_owner" = "assertion.repository_owner"
"attribute.ref" = "assertion.ref"
}
# If you want to restrict the organization, use the:
# "assertion.repository_owner==\"${local.organization}\""
# For more than one repository, use:
# "assertion.repository==\"ORG/repository-1\" || assertion.repository==\"ORG/repository-2\""
# For one repository:
attribute_condition = "assertion.repository==\"${local.organization}/${local.repository}\""
oidc {
allowed_audiences = []
issuer_uri = "https://token.actions.githubusercontent.com"
}
}
resource "google_service_account_iam_member" "workload_identity_user" {
service_account_id = 'projects/${local.project_name}/serviceAccounts/${local.service_account}' #Replace with your service account
role = "roles/iam.workloadIdentityUser"
member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_pool.name}/attribute.repository_owner/${local.organization}"
}
Step 6: Setup GitHub Actions Workflow
You need to save as secrets the provider_name
as this is sensitive information. It has the following format:
WORKLOAD_IDENTITY_PROVIDER = projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool-oidc/providers/github-provider
Below is an example of a workflow using GCP WIF OpenID Connect (OIDC) in GitHub Actions.
name: Example GCP WIF with GitHub Actions
on:
push:
branches:
- setup-wif-oidc
jobs:
job_id:
runs-on: ubuntu-latest
permissions:
contents: 'read'
id-token: 'write'
steps:
# actions/checkout MUST come before auth
- uses: 'actions/checkout@v3'
- id: "auth"
name: "Authenticate to Google Cloud"
uses: "google-github-actions/auth@v2"
with:
token_format: "access_token"
workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
service_account: YOUR_SERVICE_ACCOUNT
export_environment_variables: true
audience: ${{ secrets.GCP_POOL_AUDIENCE }}
create_credentials_file: true
access_token_lifetime: 500
# ... further steps are automatically authenticated
- name: "Set up Cloud SDK"
uses: "google-github-actions/setup-gcloud@v1"
with:
version: ">= 390.0.0"
- name: Check currently authenticated user
run: gcloud auth list
# interact with google cloud
- name: Run gcloud
run: gcloud storage buckets list
The workflow authenticates with Google Cloud, sets up necessary tools, and performs a listing of storage buckets within the Google Cloud environment.
Step 7: GitHub Action in Action
Find here how the output of the run looks like:
More Ressources
For more information on best practices for using WIF on Google Cloud: https://cloud.google.com/iam/docs/best-practices-for-using-workload-identity-federation
-
👋 Hello, I'm Laysa, a developer and cloud engineer, a public speaker, and a knowledge sharer as I go along.
♻️ Share this article, if you like the content.
💜 Find me everywhere | X | LinkedIn
Top comments (0)