Workload Identity in Practice
In this tutorial, we’re going to go through the Workload Identity feature and see how it helps to improve the way we manage access to Google Services and APIs from applications running in Google Kubernetes Engine (GKE).
Workload Identity is the recommended way to access Google Cloud APIs from within GKE due to its improved security properties and manageability. With Workload Identity you can control access to APIs using Google service accounts and IAM roles without deploying static service account JSON keys to Pods and without relying on the node’s service account.
Create a GKE Cluster
Let’s get started.
Assuming you already have a GCP project, create a variable with the name of your project and two other variables for cluster name and region.
gcp_project=my-project
cluster_name=le-cluster
cluster_region=us-east1
gcloud config set project $gcp_project
Start creating a simple cluster with Workload Identity enabled, which is defined with the property --workload-pool
.
gcloud beta container clusters create ${cluster_name} \
--release-channel regular \
--enable-ip-alias \
--region $cluster_region \
--workload-pool=${gcp_project}.svc.id.goog
Get cluster credentials:
gcloud container clusters get-credentials $cluster_name --region $cluster_region
Configure Workload Identity
With Workload Identity, you configure a Kubernetes service account (KSA) to act as a Google service account (GSA). Any workload running as the KSA, automatically authenticates as the GSA when accessing Google Cloud APIs.
Let’s create a Google Service Account:
gcloud iam service-accounts create le-application-stg
Now we create a k8s namespace and k8s service account:
kubectl create namespace staging
kubectl create serviceaccount le-application -n staging
A note on Kubernetes Declarative Configuration
In this tutorial, we are using the kubectl
CLI to create and annotate the namespace and service account, and to run Pods. The purpose is to play around and quickly evaluate how Workload Identity works. In the real world you’ll likely want to use YAML configuration to declaratively manage your Kubernetes resources.
Associate both SAs using an IAM Policy binding:
gcloud iam service-accounts add-iam-policy-binding \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:${gcp_project}.svc.id.goog[staging/le-application]" \
le-application-stg@${gcp_project}.iam.gserviceaccount.com
And, finally, annotate your service account:
kubectl annotate serviceaccount \
le-application \
iam.gke.io/gcp-service-account=le-application-stg@${gcp_project}.iam.gserviceaccount.com \
-n staging
Validate the config
The workload identity configuration is in place and we are ready to use it. First, we’ll create a Google Cloud Storage (GCS) bucket and upload a file.
gsutil mb -p $gcp_project gs://le-application-stg/
echo foo > my_application_file
gsutil cp my_application_file gs://le-application-stg/
Now let’s run a Pod and try to download the file from the GCS bucket just created:
kubectl run -it --rm --restart=Never \
--serviceaccount le-application \
--image google/cloud-sdk:slim bb8 -n staging
root@bb8:/ gsutil cp gs://le-application-stg/my_application_file .
AccessDeniedException: 403 le-application-stg@my-project.iam.gserviceaccount.com does not have storage.objects.list access to the Google Cloud Storage bucket.
It fails. To fix it we grant the GSA access to the bucket:
gsutil iam ch serviceAccount:le-application-stg@${gcp_project}.iam.gserviceaccount.com:objectViewer gs://le-application-stg/
From now on, we manage access to services and APIs using the GSA and Google IAM and in Kubernetes we grant access to applications using the KSA.
Run again and verify:
kubectl run -it --rm --image google/cloud-sdk:slim bb8 --serviceaccount le-application -n staging --restart=Never
root@bb8:/ gsutil cp gs://le-application-stg/my_application_file .
root@bb8:/ cat my_application_file
Clean up
gsutil rm -r gs://le-application-stg
gcloud iam service-accounts delete le-application-stg@${gcp_project}.iam.gserviceaccount.com
gcloud container clusters delete $cluster_name --region $cluster_region
Next Steps
If you liked this content you might be interested to check my tutorial Restrict Google Kubernetes Engine Workload Identity with Kyverno on the Google Cloud Community tutorials.