Welcome to part two of this series where we’re looking at how to improve an organization’s security posture by reducing the risks associated with using long-lived credentials. Part one reviewed the security risks associated with using service account keys.
This post will focus on reducing risk by implementing an alternative authentication method to service account keys. Specifically, I’m going to configure Workload Identity Federation to issue short-lived credentials to GitHub Actions workflows that are used to manage my rules in Google Security Operations (SecOps).
Workload Identity Federation allows you to grant workloads access to your Google Cloud resources and eliminates the maintenance and security risks associated with managing and storing service account keys. It can be used with workloads that run on AWS or Azure, with deployment pipelines such as GitHub and GitLab, and with any identity provider (IdP) that supports OpenID Connect (OIDC) or Security Assertion Markup Language (SAML) V2.0.
A security benefit of using Workload Identity Federation is that it issues short-lived credentials to workloads. Even if an attacker manages to obtain a short-lived credential, their window of opportunity to exploit them is limited in comparison to a long-lived credential. Think of it like getting a room key card (that expires after your stay) instead of a physical key when you check into a hotel.
In today’s example, we’re going to configure Workload Identity Federation to issue short-lived credentials to my GitHub Actions workflows that are used to manage my rules in Google SecOps. Once I’ve validated that my CI/CD pipeline is working with Workload Identity Federation, I’ll delete the redundant service account key from my Google Cloud project.
As per the documentation, one of the first steps for configuring Workload Identity Federation is to define the attribute mapping between the IdP (GitHub Actions in this example) and the workload identity pool provider in my Google Cloud project.
When a GitHub Actions workflow runs, it requests an OpenID Connect (OIDC) token from GitHub’s OIDC provider, which responds with a unique, automatically generated JSON web token (JWT). Attributes within the OIDC token describe the workload such as:
GitHub’s documentation includes an example OIDC token and tool that you can use to explore the various fields and values available to use in your attribute mapping.
The workload identity pool provider in Google Cloud holds configurations for external identities. You define rules and conditions within this pool to determine which workloads are allowed to access your Google Cloud resources.
For my environment, I decided on the attribute mapping below. In production, I’d consider implementing an even stricter mapping to only grant access to the workload identity pool from certain branches of the GitHub repository or if the commit SHA for the workflow file is an expected value indicating that it has not been tampered with by a bad actor to carry out malicious actions in my pipeline.
Note that I opted to map the GitHub repository ID (an immutable value) as opposed to the GitHub repository name to decrease the chance of typosquatting attacks. I did the same for the attribute that identifies the owner ID for the GitHub repository.
OIDC (GitHub Actions) | |
google.subject | assertion.sub |
attribute.repository_id | assertion.repository_id |
attribute.repository_owner_id |
assertion.repository_owner_id |
The next step is to define an attribute condition to restrict which identities can authenticate using the workload identity pool. In this example, I’m going to use the following Common Expression Language (CEL) expression to restrict access based on the values for the GitHub repository ID and GitHub owner ID.
assertion.repository_id == "123456789" &&
assertion.repository_owner_id == "87654321"
Now that we’ve defined the attribute mapping and attribute condition to allow/deny access to Google Cloud resources, we can create the new workload identity pool and provider in the Google Cloud project. I configured the new workload identity pool as shown in the image below.
Creating a new workload identity pool
I added GitHub Actions as a provider to the workload identity pool as follows.
Adding GitHub Actions as a provider to the workload identity pool
The attribute mapping for the workload identity pool provider that I defined earlier is configured in the next step.
Configuring the attribute mapping between the workload identity pool and IdP (GitHub Actions)
The CEL expression we defined earlier is configured under “Attribute Conditions” to restrict authentication to the workload identity pool based on the GitHub repository ID and owner ID.
Restricting access to the workload identity pool using a CEL expression
Now it’s time to grant the external workload (GitHub Actions) access to Google Cloud resources, which in this case is my rules in Google SecOps.
On the IAM page in the Google Cloud console, I granted direct resource access to the principal (my GitHub Actions workflows in my GitHub repository) to Google SecOps by assigning it the “Chronicle API Editor” role. In production, I’d recommend creating a custom IAM role and assigning it only the minimum necessary permissions that your GitHub Actions workflow needs.
Granting the GitHub Actions workflows direct resource access to Google SecOps
The google-github-actions/auth action makes it easy to configure your GitHub Actions workflows to authenticate to a Google Cloud workload identity pool and carry out actions in your Google Cloud environment. I added the step below to my GitHub Actions workflows that manage my rules in Google SecOps.
The “workload_identity_provider” should be set to the full identifier of the workload identity pool provider including the Google Cloud project number, pool name, and provider name. For example, “projects/1234567891234/locations/global/workloadIdentityPools/github-actions-google-secops-1/providers/github”.
I set the “access_token_lifetime” to 180 seconds. Three minutes is long enough for my GitHub Actions workflows to run and shortens an attacker’s window of opportunity to abuse the credential if it’s compromised.
- uses: google-github-actions/auth@v2
with:
project_id: ${{ vars.GOOGLE_CLOUD_PROJECT_ID }}
# The full identifier of the workload identity pool provider
workload_identity_provider: ${{ secrets.GOOGLE_CLOUD_WORKLOAD_IDENTITY_PROVIDER}}
# Request an access token with a lifetime of 3 minutes (180 seconds)
access_token_lifetime: 180s
# Export common environment variables to be consumed by downstream libraries and tools (e.g. GOOGLE_APPLICATION_CREDENTIALS and GOOGLE_GHA_CREDS_PATH)
export_environment_variables: true
# Generate a credentials file which can be used for authentication via gcloud and Google Cloud SDKs in other steps in the workflow
create_credentials_file: true
# Remove any created credentials from the filesystem upon completion
cleanup_credentials: true
I also did the following before testing authentication from my GitHub Actions workflows using Workload Identity Federation:
To validate that my GitHub Actions workflows can authenticate via Workload Identity Federation and manage my rules in Google SecOps, I created a new YARA-L rule in my GitHub repository. This triggered my GitHub Actions workflow that pushes rule updates to Google SecOps.
The output below shows that my GitHub Actions workflow authenticated to my Google Cloud environment successfully.
Validating authentication via Workload Identity Federation from GitHub Actions
The following images show that the GitHub Actions workflow has direct resource access to Google SecOps and was able to create the new rule successfully.
Creating a new rule via the Google SecOps API
Viewing the new rule in Google SecOps
Finally, to validate that the attribute condition I configured earlier is restricting access to the workload identity pool, I changed the “assertion.repository_id” value in the CEL expression temporarily in the Google Cloud console and ran the GitHub Actions workflow again.
The output below shows that authentication failed. This is expected behavior. I changed the attribute condition back to its previous state.
Validating that authentication fails based on the attribute condition configured in the workload identity pool provider
The configuration of Workload Identity Federation with my GitHub Actions and Google SecOps environment is complete. It’s now safe to delete the service account key from my Google Cloud project and GitHub repository. I can also delete the service account itself if it’s not used elsewhere in my environment.
That’s it for part two where we learned how to improve an organization’s security posture by configuring Workload Identity Federation as an alternative authentication method to service account keys. Now it’s time to focus on monitoring and detection.
Join me in part three where I’ll explain how to use Google SecOps to understand service account key usage and detect service account key creation in a Google Cloud organization.