Production GKE Platform — From Zero to Production›06 · Identity & access: IAM, RBAC and Workload Identity Federation

Lesson 06 of 18 · Part 2 — Build the platform

Identity & access: IAM, RBAC and Workload Identity Federation

Control who can do what: IAM for project-wide access to GKE, Kubernetes RBAC for namespace-level access, Google Groups so permissions follow team membership, Workload Identity Federation so each workload gets its own Google Cloud permissions, and the guard-rails around all of it.

Practitioner → Advanced
Key wordsGKE IAMKubernetes RBACGoogle Groups for RBACWorkload Identity Federation for GKEKubernetes ServiceAccountprincipal identifierGKE metadata servernode service accountorganization policiesCI accessleast privilege
People and pipelines -> Kubernetes API Google identity user, group or CI via WIF IAM role project-wide, e.g. container.viewer + RBAC per namespace Pods -> Google Cloud APIs ServiceAccount shop/orders in the cluster IAM principal Workload Identity Federation least-privilege IAM roles Guard-rails around both Org policies what may exist Quotas & limits per namespace Admission control Pod Security, policies Audit Cloud Audit Logs node service account stays minimal; the GKE metadata server keeps pods off it
Two separate questions: who may use the Kubernetes API (IAM plus RBAC), and what pods may do in Google Cloud (Workload Identity Federation). Guard-rails wrap both.

Two layers for people: IAM and RBAC

Every Kubernetes API request is authenticated with a Google identity and authorised if either IAM or Kubernetes RBAC allows it.

Layer Scope Good for
IAM roles on the project (or cluster) All clusters, all namespaces in the project Platform admins, read-only access, the right to get credentials
RBAC Roles and RoleBindings One namespace (or cluster-wide with ClusterRoles) Team access to their own namespaces

Useful IAM roles:

Role Gives
roles/container.admin Everything, including cluster settings and IAM-related operations
roles/container.clusterAdmin Manage clusters (create, upgrade, delete), not their workloads' objects
roles/container.developer Read and write most Kubernetes objects, in every namespace
roles/container.viewer Read-only access to clusters and objects
roles/container.clusterViewer Just enough to get credentials and list clusters; pair it with RBAC

IAM is the building pass: it gets you through the front door and, for some roles, into every room. RBAC is the room key: it opens only your team's rooms. Give most people a simple building pass and the keys to their own rooms.

The usual pattern for team access:

  1. IAM container.clusterViewer (or a custom role with container.clusters.get) for each team group.
  2. RBAC RoleBinding per team namespace, binding the group to edit or a custom Role.
  3. Platform team: IAM container.admin through a group, with break-glass access documented.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: shop-devs-edit
  namespace: shop
subjects:
  - kind: Group
    name: shop-devs@example.com
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: edit
  apiGroup: rbac.authorization.k8s.io

Google Groups for RBAC must be enabled on the cluster, and team groups must be members (directly or nested) of the designated group gke-security-groups@<your-domain>; otherwise RBAC never sees the group.

Pods to Google Cloud: Workload Identity Federation for GKE

Pods must never use the node's service account. With Workload Identity Federation for GKE (enabled with the cluster's workload pool PROJECT_ID.svc.id.goog and the GKE metadata server on node pools), each Kubernetes ServiceAccount is an IAM principal:

# Grant directly to the Kubernetes ServiceAccount shop/orders
gcloud projects add-iam-policy-binding my-prod-project \
  --role roles/pubsub.publisher \
  --member "principal://iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/my-prod-project.svc.id.goog/subject/ns/shop/sa/orders"

The pod only needs serviceAccountName: orders. Client libraries ask the GKE metadata server, which returns a short-lived token for that ServiceAccount.

Not every Google Cloud API accepts these principals directly yet. The alternative (and the older, still common pattern) is to let the Kubernetes ServiceAccount impersonate a Google service account:

gcloud iam service-accounts add-iam-policy-binding orders@my-prod-project.iam.gserviceaccount.com \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:my-prod-project.svc.id.goog[shop/orders]"
kubectl -n shop annotate serviceaccount orders \
  iam.gke.io/gcp-service-account=orders@my-prod-project.iam.gserviceaccount.com

Either way: one identity per workload, least-privilege roles, no key files. Search for and delete any service-account keys stored in Secrets.

CI and automation

  • CI systems outside Google Cloud (GitHub Actions, GitLab) use Workload Identity Federation (a workload identity pool and provider trusting the CI's OIDC tokens) to get short-lived credentials. No JSON keys in CI secrets.
  • Give CI a dedicated identity with the narrowest role that works: often RBAC on specific namespaces only, or none at all if a GitOps agent in the cluster applies the changes (lesson 14).

Guard-rails

  • Organization policies: restrict service-account key creation (iam.disableServiceAccountKeyCreation), external IPs, allowed regions, required cluster settings (custom constraints on GKE resources).
  • IAM deny policies for permissions nobody should have in a folder.
  • Admission control in the cluster: Pod Security Admission, Policy Controller or Kyverno (lesson 12).
  • Cloud Audit Logs: admin activity is always logged; turn on data-access logs for the Kubernetes API where you need to see reads.
  • Review IAM with the IAM recommender (unused permissions) and Policy Analyzer.

Try it: namespace access and a pod identity

  1. Enable Google Groups for RBAC on a lab cluster and create a group nested in gke-security-groups@<domain> (or use a user if you have no Workspace domain).
  2. Give the group container.clusterViewer on the project and an edit RoleBinding in namespace shop. Check with kubectl auth can-i that it can't touch kube-system.
  3. Create bucket gs://<project>-orders and grant roles/storage.objectViewer to the principal of ServiceAccount shop/orders.
  4. Run a pod with that ServiceAccount and list the bucket; then run one with the default ServiceAccount and read the 403.
  5. From a pod, query the metadata server for the node's service account and confirm it's hidden.

Going deeper: identity at scale

  • Manage IAM bindings and RBAC as code (Terraform for IAM, GitOps for RBAC), reviewed in pull requests.
  • With many projects, grant roles at folder level to groups, and keep project-level grants for exceptions.
  • Watch out for identity sameness: the same namespace and ServiceAccount name in different clusters of the same fleet (or project) map to the same principal. Use that deliberately or avoid it with distinct names.

Recap

  • Requests are allowed if IAM or RBAC allows them: IAM is project-wide, RBAC is per namespace.
  • Give teams cluster viewer in IAM and RBAC in their namespaces; bind groups, not people.
  • Workload Identity Federation for GKE gives each Kubernetes ServiceAccount its own Google Cloud permissions, without keys.
  • CI uses Workload Identity Federation too; no JSON keys anywhere.
  • Wrap it with org policies, admission control and audit logs.

This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.