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.
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:
- IAM
container.clusterViewer(or a custom role withcontainer.clusters.get) for each team group. - RBAC RoleBinding per team namespace, binding the group to
editor a custom Role. - Platform team: IAM
container.adminthrough 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
- 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). - Give the group
container.clusterVieweron the project and aneditRoleBinding in namespaceshop. Check withkubectl auth can-ithat it can't touchkube-system. - Create bucket
gs://<project>-ordersand grantroles/storage.objectViewerto the principal of ServiceAccountshop/orders. - Run a pod with that ServiceAccount and list the bucket; then run one with the
defaultServiceAccount and read the 403. - 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.