Simplifying Workload Identity and Service Accounts in GKE

Simplifying Workload Identity and Service Accounts in GKE

Introduction

In the world of Kubernetes, managing identity and access controls efficiently is crucial for secure and scalable deployments. Google Kubernetes Engine (GKE) provides robust mechanisms to handle these aspects through workload identity and service accounts. In this blog, we will explore these concepts, draw insights

Understanding Workload Identity

Workload identity in GKE allows your applications running in Kubernetes to securely access Google Cloud resources without manually managing credentials. By leveraging Google Cloud's IAM (Identity and Access Management), workloads can assume identities that grant them the necessary permissions to interact with Google Cloud services.

Key Benefits:

  1. Enhanced Security: Workload identity eliminates the need to manage and rotate service account keys manually, reducing the risk of credentials being exposed.

Simplified Access Management: Integrating IAM roles with Kubernetes service accounts simplifies the process of granting permissions to workloads.

1. Configure applications to use Workload Identity

Consider we have a GKE application and we want to enable workload identity,
so without workload identity, the workloads will use default service accounts like

to achieve this, go to Kubernetes Engine > Clusters > Security

and enable the workload identity option,

we need to update the node pool to use the GKE metadata server

To modify an existing node pool to use Workload Identity Federation for GKE, run the following command: you can either change it from the Google Console or CLI

gcloud container node-pools update NODEPOOL_NAME \
    --cluster=CLUSTER_NAME \
    --region=COMPUTE_REGION \
    --workload-metadata=GKE_METADATA

after that, log into your cluster and run this, to verify the service account

gcloud auth list

Look for the field autopilot in the output. If it says, your cluster is in Autopilot mode. If not, it's a Standard cluster

2. Create GCS Bucket & Service Accounts

  1. Create a bucket
gcloud storage buckets create gs://testing-nonprod --project=testing-nonprod --default-storage-class=STANDARD --location=us-west1 --uniform-bucket-level-access
  1. Create a Kubernetes Service Account:
kubectl create serviceaccount <k8s-service-account> --namespace <namespace>
  1. Create an IAM service account for your application or use an existing IAM service account instead. You can use any IAM service account in any project in your organization
gcloud iam service-accounts create <gcs-service-account> \
    --project=testing-nonprod
  1. Ensure that your IAM service account has the roles you need. You can grant additional roles using the following command(give permission from Cloud Console):
gcloud projects add-iam-policy-binding testing-nonprod \
    --member "serviceAccount:<gcs-service-account>@testing-nonprod.iam.gserviceaccount.com" \
    --role "roles/storage.admin" \
    --condition='title=testing-nonprod,expression=resource.service == "storage.googleapis.com" && resource.name.startsWith("projects/_/buckets/testing-nonprod")'
  1. Bind the service account to the Kubernetes service account: Use IAM policy bindings to link your Kubernetes service account with the Google Cloud service account (bind it from Google Console).
gcloud iam service-accounts add-iam-policy-binding <gcs-service-account>@milvus-testing-nonprod.iam.gserviceaccount.com \
    --role "roles/iam.workloadIdentityUser" \
    --member "serviceAccount:testing-nonprod.svc.id.goog[default/<k8s-service-account>]"
  1. Annotate the Kubernetes Service Account: Annotate the Kubernetes service account to link it with the Google Cloud service account.
kubectl annotate serviceaccount <k8s-service-account> \
  --namespace <namespace> \
  iam.gke.io/gcp-service-account=<gcs-service-account>@<project>.iam.gserviceaccount.com

Why annotate the service account? It allows GKE to recognize which service account it is associated with. When you describe your Kubernetes service account, you’ll see the newly applied annotation that wasn’t there before

  1. Deploy Your Workload: When deploying your workload, ensure it uses the Kubernetes service account you configured.

    💡
    In Standard clusters only, add the following to the template.spec
     spec:
       nodeSelector:
         iam.gke.io/gke-metadata-server-enabled: "true"
    

    Save the following manifest as test-app.yaml

     apiVersion: v1
     kind: Pod
     metadata:
       name: test-pod
       namespace: NAMESPACE
     spec:
       serviceAccountName: <k8s-service-account>
       nodeSelector:
         iam.gke.io/gke-metadata-server-enabled: "true"
       containers:
       - name: test-pod
         image: google/cloud-sdk:slim
         command: ["sleep","infinity"]
         resources:
           requests:
             cpu: 500m
             memory: 512Mi
             ephemeral-storage: 10M
    

    to check its mode from Google Cloud Console, go to Kubernetes Engine > Clusters, go to Nodes

    Alternative: Check via Console or gcloud Command:

    If the GCP UI still doesn’t show the mode, you can quickly check using the gcloud command-line tool:

     gcloud container clusters describe [CLUSTER_NAME] --zone [ZONE]
    

    Look for the field autopilot in the output. If it says, your cluster is in Autopilot mode. If not, it's a Standard cluster.

8. Apply the configuration to your cluster:

kubectl apply -f test-app.yaml
  1. Wait for the Pod to become ready. To check the status of the Pod, run the following command:

     kubectl get pods --namespace=NAMESPACE
    

    When the Pod is ready, the output is similar to the following:

     NAME       READY   STATUS    RESTARTS   AGE
     test-pod   1/1     Running   0          5m27s
    
  2. Open a shell session in the Pod:

     kubectl exec -it pods/test-pod --namespace=NAMESPACE -- /bin/bash
    
  3. Get a list of objects in the bucket:

     curl -X GET -H "Authorization: Bearer $(gcloud auth print-access-token)" \
         "https://storage.googleapis.com/storage/v1/b/BUCKET/o"
    

    The output is the following:

     {
       "kind": "storage#objects"
     }
    

    This output shows that your pod can access objects in the bucket.

alernatively test this command inside the Pod,

curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email

if it gives this: <gcs-service-account>@testing-nonprod.iam.gserviceaccount.com
then we can call the Gcloud API and it returns the applied service account

I hope you found valuable insights in this article. See you in the next read!