This guide provides detailed instructions for configuring GitHub Actions to securely push Docker images to Google Cloud Platform's Artifact Registry using Workload Identity Federation.
- Introduction
- Prerequisites
- Google Cloud Configuration Steps
- GitHub Workflow Configuration
- Troubleshooting
- Security Best Practices
This integration uses Workload Identity Federation, which is Google Cloud's recommended approach for authenticating workloads outside GCP without using service account keys. This method is more secure because:
- No long-lived credentials are stored in GitHub
- Authentication is temporary and tied to specific workflows
- Access can be limited to specific repositories and actions
- Permissions are granular and follow the principle of least privilege
- A Google Cloud Platform (GCP) account with billing enabled
- A GitHub repository with the code for your containerized application
- A Dockerfile in your repository's root directory
- Admin permissions on your GitHub repository
- Editor or Admin permissions on your GCP project
-
Open the Google Cloud Console: Go to https://console.cloud.google.com/
-
Navigate to Workload Identity Pools:
- Go to "IAM & Admin" > "Workload Identity Pools" in the left navigation menu
-
Create a New Pool:
- Click the "CREATE POOL" button
- Enter a Pool ID (e.g.,
github-pool-new) - Add a display name (e.g., "GitHub Actions Pool")
- Optionally add a description
- Click "CONTINUE"
- For the pool state, select "Enabled"
- Click "CREATE"
-
Access the Pool:
- Click on the newly created pool name (e.g., "github-pool-new")
-
Add a Provider:
- Click "ADD PROVIDER"
- Provider ID:
github-provider - Display name: "GitHub Actions Provider"
- Issuer URL:
https://token.actions.githubusercontent.com - Click "CONTINUE"
-
Configure Attribute Mappings:
- Add the following mappings by clicking "ADD MAPPING" for each:
google.subject = assertion.subattribute.actor = assertion.actorattribute.repository = assertion.repository
- Click "CONTINUE"
- Add the following mappings by clicking "ADD MAPPING" for each:
-
Set Attribute Condition:
- Add the condition that restricts authentication to your specific repository:
assertion.repository == "YOUR_GITHUB_USERNAME/YOUR_REPO_NAME"- Replace
YOUR_GITHUB_USERNAME/YOUR_REPO_NAMEwith your actual GitHub repository name (e.g.,pradeepkl/order-microservice) - Click "SAVE"
-
Navigate to Service Accounts:
- Go to "IAM & Admin" > "Service Accounts" in the left navigation menu
-
Create a Service Account:
- Click "CREATE SERVICE ACCOUNT"
- Name:
github-actions - Description: "Service account for GitHub Actions"
- Click "CREATE AND CONTINUE"
- Skip the "Grant this service account access to project" section for now
- Click "DONE"
-
Grant Artifact Registry Access:
- Go to "IAM & Admin" > "IAM"
- Click "GRANT ACCESS"
- In the "New principals" field, enter your service account email:
github-actions@YOUR_PROJECT_ID.iam.gserviceaccount.com - Select the role "Artifact Registry Writer"
- Click "SAVE"
-
Allow Workload Identity Federation to Impersonate the Service Account:
- Go to "IAM & Admin" > "Service Accounts"
- Click on your service account
- Go to the "PERMISSIONS" tab
- Click "GRANT ACCESS"
- In the "New principals" field, enter:
principalSet://iam.googleapis.com/projects/YOUR_PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool-new/attribute.repository/YOUR_GITHUB_USERNAME/YOUR_REPO_NAME - Replace
YOUR_PROJECT_NUMBERwith your actual GCP project number - Replace
YOUR_GITHUB_USERNAME/YOUR_REPO_NAMEwith your GitHub repository name - Select the role "Workload Identity User"
- Click "SAVE"
-
Navigate to Artifact Registry:
- Go to "Artifact Registry" > "Repositories" in the left navigation menu
-
Create a Repository:
- Click "CREATE REPOSITORY"
- Name: Enter a name for your repository (e.g.,
order-microservice-repo) - Format: Select "Docker"
- Location type: Select "Region"
- Region: Choose a region (e.g.,
asia-south1) - Click "CREATE"
-
Enable Required APIs:
- Go to "APIs & Services" > "Library"
- Search for and enable these APIs:
- Artifact Registry API
- IAM Credentials API
- Cloud Resource Manager API
- Security Token Service API
Create a new file in your repository at .github/workflows/build-push-gcp.yml with the following content:
name: Build and Push to GCP
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
env:
PROJECT_ID: YOUR_PROJECT_ID
GAR_LOCATION: YOUR_REGION
REPOSITORY: YOUR_ARTIFACT_REGISTRY_REPO_NAME
IMAGE_NAME: YOUR_IMAGE_NAME
permissions:
contents: read
id-token: write
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Google Auth
id: auth
uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/YOUR_PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool-new/providers/github-provider'
service_account: 'github-actions@YOUR_PROJECT_ID.iam.gserviceaccount.com'
token_format: 'access_token'
- name: Docker Auth
uses: 'docker/login-action@v3'
with:
registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev'
username: 'oauth2accesstoken'
password: '${{ steps.auth.outputs.access_token }}'
- name: Build and Push Container
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}:latestReplace the following placeholders with your actual values:
YOUR_PROJECT_ID: Your GCP project IDYOUR_PROJECT_NUMBER: Your GCP project numberYOUR_REGION: Your Artifact Registry region (e.g.,asia-south1)YOUR_ARTIFACT_REGISTRY_REPO_NAME: The name of your Artifact Registry repositoryYOUR_IMAGE_NAME: The name for your Docker imageYOUR_GITHUB_USERNAME/YOUR_REPO_NAME: Your GitHub repository name
Let's break down the GitHub workflow file:
-
Trigger Configuration:
on: push: branches: [ "master" ] pull_request: branches: [ "master" ]
This section defines when the workflow will run - on pushes and pull requests to the master branch.
-
Environment Variables:
env: PROJECT_ID: YOUR_PROJECT_ID GAR_LOCATION: YOUR_REGION REPOSITORY: YOUR_ARTIFACT_REGISTRY_REPO_NAME IMAGE_NAME: YOUR_IMAGE_NAME
These variables are used throughout the workflow to avoid repetition and make the workflow more maintainable.
-
Permissions:
permissions: contents: read id-token: write
This is crucial for Workload Identity Federation. The
id-token: writepermission allows the workflow to request an OIDC token from GitHub, which is exchanged for GCP credentials. -
Checkout Step:
- name: Checkout repository uses: actions/checkout@v4
This step checks out your repository code so it's available to the workflow.
-
Google Authentication:
- name: Google Auth id: auth uses: google-github-actions/auth@v2 with: workload_identity_provider: '...' service_account: '...' token_format: 'access_token'
This step authenticates with Google Cloud using Workload Identity Federation. It exchanges the GitHub OIDC token for a Google Cloud access token.
-
Docker Authentication:
- name: Docker Auth uses: 'docker/login-action@v3' with: registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev' username: 'oauth2accesstoken' password: '${{ steps.auth.outputs.access_token }}'
This step uses the access token from the previous step to authenticate with the Artifact Registry Docker repository.
-
Build and Push Container:
- name: Build and Push Container uses: docker/build-push-action@v5 with: context: . push: true tags: | ...
This step builds your Docker image from the Dockerfile in your repository and pushes it to Artifact Registry with two tags: the commit SHA and 'latest'.
-
"Identity Pool does not exist" Error:
- Verify the correct project number is used in the workflow file
- Check if the pool was created in the correct project
- Ensure the pool name matches exactly (case sensitive)
-
Authentication Failure:
- Wait 5-10 minutes after setting up Workload Identity Federation (permissions can take time to propagate)
- Verify the attribute condition correctly references your repository
- Check that the service account has the necessary permissions
-
Docker Push Failure:
- Ensure the Artifact Registry repository exists
- Verify the region is correct
- Check that the service account has Artifact Registry Writer permissions
-
API Not Enabled Error:
- Enable all required APIs listed in the prerequisites
-
Use Restrictive Attribute Conditions:
- Limit authentication to specific repositories
- Consider restricting to specific branches for production deployments
-
Follow Principle of Least Privilege:
- Grant only the necessary permissions to your service account
- Use different service accounts for different purposes
-
Use a Dedicated Project for Identity Pools:
- Maintain pools in a separate project for better security isolation
- Apply organizational policies to restrict pool creation
-
Regularly Audit Permissions:
- Periodically review who can authenticate and what they can access
- Remove permissions that are no longer needed
-
Enable Audit Logging:
- Track who is authenticating and what resources they're accessing
- Set up alerts for suspicious activities
By following this guide, you have established a secure, modern CI/CD pipeline that follows Google Cloud's recommended best practices for authentication and authorization.