Overview
As part of the first part of the series we understood what is Workload Identity. In this DIY blog we will apply Workload Identity to our GKE workloads by deploying a demo application called lingua-greeter to GKE, the lingua-greeter will call Translate API to translate the greeting text passed to it.
Glossary
| Abbreviation | Expansion |
|---|---|
| API | Application Programming Interface |
| ACL | Access Control List |
| GCS | Google Cloud Storage |
| GKE | Google Kubernetes Engine |
| GSA | Google Service Account |
| IAM | Identity and Access Management |
| KSA | Kubernetes Service Account |
| RBAC | Role Based Access Control |
| SA | Service Account |
| VPC | Virtual Private Cloud |
Pre-requisites
- Google Cloud Account
- With a Service Account with roles:
-
Kubernetes Engine Admin- to create GKE cluster -
Service Accountroles used to create/update/delete Service Account - iam.serviceAccounts.actAs
- iam.serviceAccounts.get
- iam.serviceAccounts.create
- iam.serviceAccounts.delete
- iam.serviceAccounts.update
- iam.serviceAccounts.get
- iam.serviceAccounts.getIamPolicy
-
iam.serviceAccounts.setIamPolicy
Or simply you can add
Service Account AdminandService Account Userroles -
Compute Network Admin- to create the VPC networks
-
- Enable Translation API on your Google Cloud Account
- Google Cloud SDK
- terraform
- kubectl
- Taskfile
Optional
Download Sources
git clone https://github.com/kameshsampath/workload-identiy-gke-demo && cd "$(basename "$_" .git)"
export DEMO_HOME="$PWD"
Environment Setup
Variables
When working with Google Cloud the following environment variables helps in setting the right Google Cloud context like Service Account Key file, project etc., You can use direnv or set the following variables on your shell,
export GOOGLE_APPLICATION_CREDENTIALS="the google cloud service account key json file to use"
export CLOUDSDK_ACTIVE_CONFIG_NAME="the google cloud cli profile to use"
export GOOGLE_CLOUD_PROJECT="the google cloud project to use"
export KUBECONFIG="$DEMO_HOME/.kube"
(e.g.)
export CLOUDSDK_ACTIVE_CONFIG_NAME=personal
export GOOGLE_APPLICATION_CREDENTIALS=~/.ssh/my-sa-key.json
export GOOGLE_CLOUD_PROJECT=my-awesome-project
export KUBECONFIG="$DEMO_HOME/.kube"
TIP If you are using direnv you can then create file
.envrc.localand add the environment variables. They can then be loaded usingdirenv allow .
You can find more information about gcloud cli configurations at https://cloud.google.com/sdk/docs/configurations.
We will be using terraform to create the Google Cloud resources e.g. GKE Cluster with Workload Identity enabled, Google Service Accounts(GSA), IAM policies and bindings.
As you may need to override few terraform variables that you don't want to check in to VCS, add them to a file called .local.tfvars. Set the following environment variable to make terraform use the variable values form the file .local.tfvars
export TFVARS_FILE=.local.tfvars
Check the Inputs section for all possible terraform variables that are configurable.
Example
An example .local.tfvars that will use a Google Cloud project my-awesome-project, create a two node GKE cluster named wi-demo in region asia-south1 with Kubernetes version 1.24. from stable release channel. The machine type of each cluster node will be e2-standard-4. The demo will be deployed in Kubernetes namespace demo-apps, will use lingua-greeter as the Kubernetes Service Account.
app_ksa = "lingua-greeter"
app_namespace = "demo-apps"
cluster_name = "wi-demo"
configure_app_workload_identity = false
gke_num_nodes = 2
kubernetes_version = "1.24."
machine_type = "e2-standard-4"
project_id = "my-awesome-project"
region = "asia-south1"
release_channel = "stable"
NOTE: For rest of the section we assume that your tfvars file is called
.local.tfvars
Application Overview
As part of the demo, let us deploy a Kubernetes application called lingua-greeter. The application exposes a REST API /:lang , that allows you to translate a text Hello World! into the language :lang using Google Translate client.
NOTE: The
:langis the BCP 47 language code.
Create Environment
We will use terraform to create a GKE cluster with WorkloadIdentity enabled for its nodes,
Initialize terraform and download its modules,
task init
Create GKE cluster
The terraform apply will creates a Kubernetes(GKE) Cluster,
task create_cluster
The terraform apply will create the following Google Cloud resources,
- A Kubernetes cluster on GKE
- A Google Cloud VPC that will be used with GKE
- GKE is configured to use Workload Identity Pool. As refresher check How Workload Identity Works.
Deploy Application
To see Workload Identity in action we will deploy the application(workload) on to GKE in two parts,
- Application is not enabled for Workload Identity
- Application enabled for Workload Identity
Without Workload Identity Enabled
Create the namespace demo-apps to deploy the lingua-greeter application,
kubectl create ns demo-apps
Run the following command to deploy the application,
kubectl apply -n demo-apps -k $DEMO_HOME/app/config
Wait for application to be ready,
kubectl rollout status -n demo-apps deployment/lingua-greeter --timeout=60s
Get the application service LoadBalancer IP,
kubectl get svc -n demo-apps lingua-greeter
NOTE: If the
EXTERNAL-IPis<pending>then wait for the IP to be assigned. It will take few minutes for theEXTERNAL-IPto be assigned.
You can use the following command to wait untilExternal-IPis assigned,while [ -z $(kubectl get svc -n demo-apps lingua-greeter -ojsonpath="{.status.loadBalancer.ingress[*].ip}") ]; do sleep .3; done;
Call Service
export SERVICE_IP=$(kubectl get svc -n demo-apps lingua-greeter -ojsonpath="{.status.loadBalancer.ingress[*].ip}")
Call the service to return the translation of Hello World! in Tamil(ta),
curl "http://$SERVICE_IP/ta"
The service should fail with a message,
{"message":"Internal Server Error"}
When you check the logs of the lingua-greeter pod,
kubectl logs -n demo-apps -lapp=lingua-greeter
You should see a message like,
____ __
/ __/___/ / ___
/ _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.10.0
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
O\
⇨ http server started on [::]:8080
time="2023-03-10T07:36:35Z" level=error msg="googleapi: Error 401: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.\nMore details:\nReason: authError, Message: Invalid Credentials\n"
As it describes you don't have authentication credentials to call the API. All Google Cloud API requires GOOGLE_APPLICATION_CREDENTIALS to allow client to authenticate itself before calling the API. If you check the deployment manifest we dont have one configured.
Configure Application To Use Workload Identity
Run the following command to configure the application to use Workload Identity,
task use_workload_identity
The terraform script rbac.tf does the following,
- Create a GSA called
translator - Add role
roles/iam.workloadIdentityUsertotranslatorwith a single memberserviceAccount:$GOOGLE_CLOUD_PROJECT.svc.id.goog[demo-apps/lingua-greeter]. This basically allows KSA to impersonate itself as GSAtranslatorthereby allowing it to call Google Cloud services that are allowed for GSAtranslator, in this case to use Google Translate API. - Add IAM policy binding to
translatorfor roleroles/cloudtranslate.userwhich allows it to call the Google Translate API. - Finally an updated
lingua-greeterKSA manifest$DEMO_HOME/k8s/sa.yaml, that is annotated with the GSAclient_emailthat it is allowed to use, in this case it will be something liketranslator@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com.
apiVersion: v1
kind: ServiceAccount
metadata:
name: lingua-greeter
namespace: demo-apps
annotations:
iam.gke.io/gcp-service-account: "translator@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com"
Run the following command to update the Kubernetes SA lingua-greeter to use the Google IAM Service Account(GSA) translator using Workload Identity mechanics and call the Google Translate API,
kubectl apply -n demo-apps -f "$DEMO_HOME/k8s/sa.yaml"
Call the service again, the service should succeed with a response,
{"text":"Hello World!","translation":"வணக்கம் உலகம்!","translationLanguage":"ta"}
NOTE: Sometimes it may take few seconds for the pods to refresh the metadata, in such cases try to call the service after few seconds.
Cleanup
To clean up all the Google Cloud resources that were created as part of this demo,
task destroy
Summary
- Deploy GKE cluster with Workload Identity enabled
- Deploy
lingua-greeterapplication to GKE - Create Google Service Account
translatorwith permissions to call Google Translate API - Annotating Kubernetes Service Account
lingua-greeterwith Google Service Accounttranslatorallowing it to impersonate Google Service Account.

Top comments (0)