GCP Developer Authentication Guide: Local, CI & Kubernetes¶
This guide provides developers with the standard, secure methods for authenticating applications to Google Cloud Platform (GCP) resources from local development machines, CI pipelines and Kubernetes clusters.
The primary goal is to avoid using static, long-lived service account keys (JSON files), which are a security risk.
Instead, we will use a combination of two modern GCP features:
-
Application Default Credentials (ADC): A standard feature in GCP client libraries. It allows your code to automatically find the correct credentials based on the environment it's running in, with no code changes.
-
Workload Identity Federation (WIF): The technology that allows identities outside of GCP (like your local machine or a Kubernetes pod) to securely impersonate a GCP service account and get short-lived, temporary credentials.
Part 1: Local Development with Application Default Credentials (ADC)¶
When you're building and testing code on your local machine, you need a simple way to grant your code the same permissions you have.
The Concept¶
Application Default Credentials (ADC) is a strategy used by Google client libraries to automatically find credentials based on the environment. On your local machine, you can "log in" via the gcloud CLI, and ADC will use those credentials.
How to Use It¶
1. Install the gcloud CLI:¶
If you haven't already, install the Google Cloud CLI.
2. Log in and Set Up ADC¶
Run the following command in your terminal:
This command will open a browser window, asking you to log in with your Google account (the one you use for GCP).
It will ask for permission to access your Google Cloud resources.
Once authorized, it stores your user credentials in a secure, well-known location on your file system.
3. Run Your Code (That's It!)¶
Google client libraries (e.g., for Python, Node.js, Go, Java) are built to automatically check for these credentials. You don't need to specify a key file path or write any special auth logic.
Example (Python):
# No key file or auth logic needed!
from google.cloud import storage
# The client library automatically finds the credentials
# created by `gcloud auth application-default login`.
client = storage.Client()
# Your code now runs with the permissions of your user account.
for bucket in client.list_buckets():
print(bucket.name)
Your code will now run with the same IAM permissions that your user account has in the GCP project.
Local development in Docker/Podman¶
For your local docker/podman containers to use ADC, you need to share the ADC credentials file with the container.
-
Locate the ADC credentials file on your host machine. By default, it is located at:
-
Linux/macOS:
~/.config/gcloud/application_default_credentials.json -
Windows:
%APPDATA%\gcloud\application_default_credentials.json -
When running your container, mount the directory containing the credentials file to the same path inside the container.
- Set the
GOOGLE_APPLICATION_CREDENTIALSenvironment variable inside the container to point to the credentials file. - Example Docker run command:
docker run -it --rm \
-v ~/.config/gcloud:/root/.config/gcloud \
-e GOOGLE_APPLICATION_CREDENTIALS=/root/.config/gcloud/application_default_credentials.json \
your-image-name
Pro Tip: Impersonating a Service Account Locally¶
If you need to test your code as a specific service account (e.g., to test permissions), you can!
Ensure your user account has the "Service Account Token Creator" (roles/iam.serviceAccountTokenCreator) role on the target service account.
Run this command:
gcloud auth application-default login --impersonate-service-account=YOUR_SA_EMAIL@PROJECT.iam.gserviceaccount.com
Run your code. It now acts as the service account.
To stop, simply run: gcloud auth application-default login and you will login back under your standard identity.
Part 2: CI/CD with Workload Identity Federation (WIF)¶
When running code in CI/CD pipelines, you want to avoid using static service account keys. Instead, you can use Workload Identity Federation to obtain short-lived credentials.
The Concept¶
Workload Identity Federation (WIF) allows your CI/CD system to securely impersonate a GCP service account without using long-lived keys. You can use a GitLab JWT ID token in your pipelines and exchange it for GCP credentials of a specific service account. Alternatively, you can configure WIF to allow your GitLab identity to access GCP resources directly.
Basic setup¶
1. Create a GCP Service Account¶
Create a service account in GCP with the necessary IAM roles for your application. Alternatively, you can configure WIF to allow your GitLab identity to access GCP resources directly without a service account (see section ), but using a service account is generally recommended for better security and auditability.
2. Grant the Workload Identity Role to your GitLab identity¶
Locate your GitLab User ID (found in your GitLab profile settings). Grant the roles/iam.workloadIdentityUser role to the following principal on the service account you've just created:
principalSet://iam.googleapis.com/projects/550463210744/locations/global/workloadIdentityPools/cr-gitlab-fftrader/attribute.user_id/<USER_ID>
where <USER_ID> is your GitLab User ID.
3. Configure your GitLab CI/CD Pipeline¶
In your .gitlab-ci.yml, include the WIF template provided by the Cloud Platform team and set the necessary variables:
include:
- project: 'devops/cicd-components/gitlab-templates'
file: 'gitlab-fftrader-wif.yml'
example_job:
stage: deploy
variables:
WORKLOAD_IDENTITY_PROJECT_NUMBER: 550463210744
WORKLOAD_IDENTITY_POOL: "cr-gitlab-fftrader"
WORKLOAD_IDENTITY_PROVIDER: "cr-gitlab-fftrader-jwt"
SERVICE_ACCOUNT: "YOUR_SA_NAME@PROJECT_ID.iam.gserviceaccount.com"
extends: .workload-identity # Injects before_script to pipeline with WIF configuration
script:
# Anything you need with access to GCP via Application Default Credentials...
The before_script will handle the authentication using WIF, and your script can use Application Default Credentials as usual.
Setup without service account impersonation¶
If you prefer to allow your GitLab identity to access GCP resources directly without using a service account, you can grant the necessary IAM roles directly to your GitLab identity principal. Locate your GitLab User ID (found in your GitLab profile settings) and grant the required roles (e.g., roles/storage.objectViewer) to the following principal:
principalSet://iam.googleapis.com/projects/550463210744/locations/global/workloadIdentityPools/cr-gitlab-fftrader/attribute.user_id/<USER_ID>
Then, in your .gitlab-ci.yml, you can use the alternative WIF template (.workload-identity-direct) as follows:
include:
- project: "devops/cicd-components/gitlab-templates"
file: "gitlab-fftrader-wif.yml"
example_job:
stage: deploy
variables:
WORKLOAD_IDENTITY_PROJECT_NUMBER: 550463210744
WORKLOAD_IDENTITY_POOL: "cr-gitlab-fftrader"
WORKLOAD_IDENTITY_PROVIDER: "cr-gitlab-fftrader-jwt"
extends: .workload-identity-direct # Injects before_script to pipeline with WIF configuration
script:
# Anything you need with access to GCP via Application Default Credentials...
Pro tip: Using gsutil with WIF¶
The gsutil command-line tool does not respect the prepared environment variable with the Application Default Credentials. You need to run gcloud auth login command first to authenticate gsutil:
script:
- gcloud auth login --cred-file $GOOGLE_APPLICATION_CREDENTIALS --project $GCP_PROJECT_ID
- gsutil ls gs://your-bucket-name
Part 3: Kubernetes with Workload Identity Federation (WIF)¶
In Kubernetes, we don't have a "user" to log in as. Instead, we want our application (running in a Pod) to act as a specific Google Service Account (GSA).
Workload Identity Federation (WIF) allows a Kubernetes Service Account in your cluster to impersonate a GCP Service Account or grant access to GCP resources directly.
This process involves NO service account keys.
Method A: Grant access to the Kubernetes resources directly¶
To provide access with Workload Identity Federation for GKE, you create an IAM allow policy that grants access on a specific Google Cloud resource to a principal that corresponds to your application's identity. For example, you could give read permissions on a Cloud Storage bucket to all Pods that use the database-reader Kubernetes ServiceAccount.
Reference Kubernetes resources in IAM policies¶
In your IAM policy, you refer to a Kubernetes resource by using an IAM principal identifier to select the resource. This identifier has the following syntax:
Grant access based on a service account:
principal://iam.googleapis.com/projects/412451610001/locations/global/workloadIdentityPools/devops-309909.svc.id.goog/subject/ns/<NAMESPACE>/sa/<SERVICEACCOUNT>
Where <NAMESPACE> is the Kubernetes namespace and <SERVICEACCOUNT> is the Kubernetes Service Account name.
or
Grant access to all pods in a namespace:
principalSet://iam.googleapis.com/projects/412451610001/locations/global/workloadIdentityPools/devops-309909.svc.id.goog/namespace/<NAMESPACE>
Where <NAMESPACE> is the Kubernetes namespace.
Method B: Linking Kubernetes Service Account to Google Service Account¶
You can link a specific Kuibernetes Service Account to a Google Service Account to gain the same privileges the Google Service Account has.
How it Works¶
-
Your Pod runs with a specific Kubernetes Service Account.
-
You configure a "trust" relationship between your Kubernetes Service Account and a GCP Service Account.
-
The GCP client libraries in your Pod (using ADC) automatically detect they are in GKE.
-
ADC automatically exchanges the Kubernetes Service Account's token for a short-lived GCP Service Account token from the GKE metadata server.
-
Your application transparently runs with the permissions of the GCP Service Account.
Setup¶
1. Create a GCP Service Account and grant it the necessary IAM roles for your application.¶
2. Create a Kubernetes Service Account (KSA) in your namespace.¶
3. Annotate your Kubernetes Service Account with the name of the GCP Service Account.¶
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-ksa-name
namespace: my-k8s-namespace
annotations:
# This annotation links the Kubernetes Service Account to the GCP Service Account
iam.gke.io/gcp-service-account: YOUR_SA_NAME@PROJECT_ID.iam.gserviceaccount.com
4. Use the Kubernetes Service Account in your Pod¶
apiVersion: apps/v1
kind: Deployment;
metadata:
name: my-app
namespace: my-k8s-namespace
spec:
# ...
template:
metadata:
# ...
spec:
# Use the annotated KSA
serviceAccountName: my-ksa-name
containers:
- name: my-app-container
image: gcr.io/my-project/my-app
# ...
If you are using the Standard Service Chart provided by Cloud Platform team, just set this in your values.yaml:
5. That's it!¶
On GKE, the client libraries (ADC) inside your container will automatically detect this setup and perform the token exchange for you. Your code remains identical to the local development example.
The Payoff: Identical Application Code¶
The biggest benefit of ADC and WIF is that your application code does not change. The exact same code you wrote for local development will work perfectly inside your Kubernetes container or CI pipeline!
# This code works LOCALLY and in KUBERNETES (GKE or non-GKE)
# without any changes.
from google.cloud import storage
# ADC handles the "how" of getting credentials.
# - Locally: Uses gcloud login.
# - In K8s: Uses the WIF token exchange.
client = storage.Client()
for bucket in client.list_buckets():
print(bucket.name)
By configuring the environment correctly, you free your application from managing authentication logic or handling sensitive key files.