Jump to Content
Security & Identity

Configuring Workload Identity Federation for GitHub actions and Terraform Cloud

July 7, 2023
Mikesh Khanal

Cloud Security Engineer

Sanmay Mishra

Cloud Infrastructure Consultant

Join us as we build on the concept and use cases of Workload Identity Federation, showcasing the security benefits of "keyless authentication.” We will dive into how Workload Identity Federation can be used in the context of CI/CD pipelines and tools that are commonly found in enterprise environments. 

Workload Identity Federation can be integrated with external providers, such as Gitlab, GitHub actions, and Terraform Cloud. We will show how the tokens issued by the external providers can be mapped to various attributes and how it can be used to evaluate conditions to restrict which identities can authenticate. 

Prerequisites

  • A Google Cloud project

  • A GitHub repository

  • A GitHub Actions workflow

  • Terraform Cloud Account/ Workspace

Scenario 1:

Objective: Demonstrate how two separate GitHub repositories and their corresponding CI/CD pipelines use separate service accounts to access respective Google Cloud resources in the least-privileged access manner.

In this scenario, we will configure the workload identity pool to check for a custom attribute "repository" and validate that the corresponding service account is used for a given GitHub repository.

https://storage.googleapis.com/gweb-cloudblog-publish/images/1_-_gcp_wif_with_github_action_architectu.max-2200x2200.jpeg

Instructions

1. Create a workload identity pool

It is recommended to create and manage workload identity pools from a single dedicated project as per best practices.

Loading...

2. Create a workload identity pool provider 

Note the purpose of some arguments as explained below. 

a. The issuer-uri identifies GitHub as the identity provider and lets workload identity discover the OIDC metadata and JSON Web Key Set(JWKs) endpoints. Google Cloud uses these endpoints to validate tokens. 

b. GitHub issues a unique token for each workflow job. This token contains claims that describe the identity of the workflow. By using an Attribute mapping, we translate the claims in the token so that we can use them in principal/principalSet identifiers to grant access to service accounts, or to create an attribute condition. 

c. Attribute-condition: After mapping the attributes, we are asserting conditions for authentication. In this case, we only allow authentication requests from GitHub Actions workflows on any repository from the “sec-mik” GitHub organization. Either assertions or previously mapped attributes can be used to build the condition.

Loading...

In this case, we are mapping the subject, repository, repository owner, and branch attributes from the claims in the token to GCP assertions. CEL (Common Expression Language) expression is used to extract the branch from the subject claim in the token and map it to the custom attribute “branch.” 

3. Create a service account for each repository and assign them appropriate IAM permissions

In this scenario, one repository/service account will be for an application team that will deploy a Hello World app to Cloud Run and one repository will be owned by the networking team.

Loading...

As a result, the networking-sa service account will have "networking" related IAM permissions and the application team will have cloud run and associated IAM permissions.

https://storage.googleapis.com/gweb-cloudblog-publish/images/2_-_service_account_and_relevant_roles.max-700x700.png

4. Add IAM bindings for the workload pool 

We will utilize previously mapped attributes to create principals/principalSets, and then use them in IAM bindings to grant permissions to create short-lived credentials for the appropriate service account. In this case, we want to map the networking service account ("networking-sa") with the networking GitHub repository that is encapsulated in the "repository" attribute.

Loading...

In accordance with Google Cloud best practices, we recommend that you use the system-generated IDs such as repository_id, rather than repository name, because it is immutable and does not change between renames of entities such as renaming a repository.

Next, we will repeat the same process to associate the application team's service account with the "application-repo" repository. In this case, we will map against a "subject" assertion that represents a specific repository name and branch, instead of a custom attribute. The "sub" claim in the JWT is comprehensive and predictable, as it is assembled from concatenated metadata, and is therefore the preferred mapping option in most cases.

Loading...

Once the IAM bindings are applied, we will observe it like this from Google Cloud Console workload identity provider page.

https://storage.googleapis.com/gweb-cloudblog-publish/images/3_-_workload_identity_provider_github_acti.max-2200x2200.png

5. Update the GitHub Actions workflow to use the workload identity pool to authenticate to Google Cloud.

a. The workload identity pool (WORKLOAD_IDENTITY_PROVIDER) is configured as “projects/987654321/locations/global/workloadIdentityPools/GitHub-actions-pool/providers/GitHub-actions-oidc” and service account email (SERVICE_ACCOUNT_EMAIL) as "application-sa@production-secmik.iam.gserviceaccount.com". 

b. Once authenticated, our pipeline will run two jobs, which are oriented towards a networking and application deployment.

Loading...

We run this GitHub action from the application-repo where the cloud run deployment succeeds, but the networking command (listing firewalls) fails.

https://storage.googleapis.com/gweb-cloudblog-publish/images/4_-_cloud_run_successful_deployment_using_.max-2200x2200.png

6. Analyzing the Cloud Audit Logs

We need to enable IAM and STS Data Access Logs on the Audit Logs of all the projects that contain workload identity pools and service accounts used for workload identity federation; to detect any IAM violations and observe how token exchange works between GitHub and STS API as shown below. 

First, the `auth` action uses the STS API to retrieve a short lived federated access token. This token exchange may fail due to attribute condition mismatch or configuration error.

The following is a log of the token exchange.

https://storage.googleapis.com/gweb-cloudblog-publish/images/5_-_log_for_successful_token_exchange_requ.max-2200x2200.png

Please observe the following parameters in the image:

protoPayload.authenticationInfo.principalSubject: The subject from the GitHub JWT token i.e. repo:sec-mik/networking-repo:ref:refs/heads/main

protoPayload.metadata.mapped_principal: The subject of the token, using IAM syntax to identify the principal i.e. principal://iam.googleapis.com/projects/987654321/locations/global/workloadIdentityPools/GitHub-actions-pool/subject/repo:sec-mik/networking-repo:ref:refs/heads/main

Next, the `auth` action uses the IAM Credentials API to obtain a short-lived credential for a service account. Failure may occur due to either the absence of the workloadIdentityUser IAM role or the incorrect principal attempting to create the token.

The following is a log of the creation of short-lived credentials for service accounts.

https://storage.googleapis.com/gweb-cloudblog-publish/images/6_-_log_for_successful_access_token_creati.max-2200x2200.png

Please observe the following parameters in the image:

protoPayload.authenticationInfo.principalSubject: The subject of the federated token. i.e. principal://iam.googleapis.com/projects/987654321/locations/global/workloadIdentityPools/GitHub-action-pool/subject/repo:sec-mik/application-repo:ref:refs/heads/main

metadata.identityDelegateChain: The service account for which short-lived credentials are generated, such as example-app-sa@production-secmik.iam.gserviceaccount.com

Refer to log examples for more details.

Summary

To summarize, we saw how a GitHub repository was mapped as a principal to authenticate with Google Cloud using a GitHub OIDC token, which was subsequently exchanged for Google Cloud credentials. Once authenticated, the IAM bindings for the corresponding service account performed authorization checks and was granted access accordingly.

Scenario 2

Objective: Demonstrate how two Terraform Cloud workspaces can use two separate but corresponding service accounts for provisioning. 

In this scenario, we will explore another popular tool in the IaC space that is commonly used as an orchestrator, Hashicorp Terraform Cloud and see how workload identity federation can be used in a similar fashion. 

Below is an overview of what we will demonstrate.

Mappings (GitHub repo → Terraform Cloud Workspace → Google Cloud Service Account → Google Cloud project )

  • app-repo-dev → app-ws-dev → app-sa-dev → dev-secmik 

  • app-repo-prod → app-ws-prod → app-sa-prod → production-secmik

https://storage.googleapis.com/gweb-cloudblog-publish/images/7_-_gcp_wif_with_terraform_cloud_architec.max-2200x2200.jpeg

Instructions

1. Create a workload identity pool for dev and prod environment

In this scenario, we will create a separate workload identity pool to follow Google Cloud best practices which recommends having one-to-one mapping between external identity provider and workload identity pool to prevent subject collisions. The format of the subject is "organization:my-org:project:Default Project:workspace:my-workspace:run_phase:apply" and the reference to workspace will be different for dev and prod environments in our scenario.

Loading...

2. Create a workload identity pool provider 

The next step is to configure the provider within the workload identity pool. Before this, it is important to review the various claims that are part of the Terraform JWT token. The few important claims from the JWT token are:

  • iss : The issuer of the token. 

  • terraform_full_workspace : The full workspace name.

  • terraform_project_id : The ID of the project that the workspace is associated with.

  • terraform_workspace_id : The ID of the workspace.

In the dev and prod workload identity pool configuration, the CEL condition will limit access to identity tokens from a specific workspace (corresponding to dev/ prod) within a Terraform Cloud organization and Project.

Loading...

Loading...

3. Create Google Cloud Service accounts

Create two service accounts that will be used by separate workspaces.

  • main workspace: "app-sa-prod" 

  • develop workspace: "app-sa-dev"

4. Add IAM bindings for the workload pool

Add IAM bindings for the workload pool providers that were created earlier. In this scenario, IAM verifies the workspace ID attribute before authorizing access to impersonate a service account. Workspace ID is immutable, meaning it cannot be changed, so if the workspace is deleted and recreated with the same name, no accidental access will be granted as per best practice from Google.

Loading...

Dev service account

Loading...

Prod service account

Once the IAM bindings are applied, we will observe it like this on Google Cloud Console’s workload identity provider page.

https://storage.googleapis.com/gweb-cloudblog-publish/images/8_-_workload_identity_provider_terraform_c.max-2200x2200.png

5. Terraform Cloud Configuration

We will specify the workload pool related configuration as a workspace variable as shown below. The variables can be classified as sensitive to prevent them from being viewed on the UI or in logs. For further information, please refer to HashiCorp's public documentation.

  • TFC_GCP_PROVIDER_AUTH : Terraform Cloud will use dynamic credentials to authenticate to GCP.

  • TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL: The service account email address that Terraform Cloud will use to authenticate to Google Cloud.

  • TFC_GCP_WORKLOAD_PROVIDER_NAME: The canonical name of the workload identity provider. Format is : projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_NAME/providers/PROVIDER_NAME

https://storage.googleapis.com/gweb-cloudblog-publish/images/9_-_terraform_workspace_variables_for_auth.max-2200x2200.png

If the credential exchange fails due to a condition check, one would see an error similar to one below:

https://storage.googleapis.com/gweb-cloudblog-publish/images/10_-_token_exchange_failure_attribute_condit.max-800x800.png

If the token generation fails due to permissions, one would see an error similar to one below

https://storage.googleapis.com/gweb-cloudblog-publish/images/11_-_token_generation_failure_permission_err.max-900x900.png

A successful deployment would result in something like this:

https://storage.googleapis.com/gweb-cloudblog-publish/images/12_-_cloud_run_successful_deployment_using.max-2200x2200.png

6. Auditing Workload Identity Federation service accounts

To obtain an organization-wide view of all service accounts that have been provisioned to use Workload Identity Federation, follow these steps:

  • In the Policy Analyzer, select your organization.

  • Select the Workload Identity User role as a parameter.

  • Do not select any checkboxes.

  • Click Analyze.

The results will be all service accounts that have been provisioned to be used with Workload Identity Federation, including their attribute conditions.

https://storage.googleapis.com/gweb-cloudblog-publish/images/13_-_auditing_workload_identity_federation.max-2200x2200.png

Get started today

To get started with Workload Identity Federation with CI/CD pipelines, see Configure workload identity federation with deployment pipelines.

Posted in