IntegrAuth Academy
Identity, security and practical AI — free, from zero. Byte-sized lessons, real diagrams, hands-on labs, zero fluff. Two programs, each with its own exam and certificate.
English is hard? Your browser can translate this page into Tamil, Telugu, Kannada, Malayalam or Hindi. Tech words stay in English so you learn the real names.
How?
- Chrome on phone: ⋮ menu → Translate
- Chrome on computer: right-click → Translate to …
- Edge: the translate icon in the address bar
Kubernetes identity — service accounts & RBAC
Kai's app now runs in a Kubernetes cluster, next to many other teams' apps. The cluster does not give the app a password. It gives a ServiceAccount and a token file. Your goal: set these up so a hacked pod stays a small problem.
- Software in Kubernetes runs as a ServiceAccount. Give each app its own.
- A bound token dies with its pod and expires in an hour by default.
- Role bindings decide what the token may do. Bind in one namespace.
- Whoever can create pods can use any ServiceAccount there.
- Pin the cloud trust to one exact ServiceAccount.
Kubernetes in one paragraph
Kubernetes is open-source software that runs containers on a group of machines called nodes. The smallest unit is the pod: containers that start and stop together. Everything goes through one front door, the API server. Namespaces split a cluster into named sections, such as shop and pay.
ServiceAccounts: the caller for software
Software gets a ServiceAccount. It lives in one namespace. Its username looks like system:serviceaccount:shop:web: namespace shop, name web. A pod that names no ServiceAccount runs as the default one, so give every app its own (see non-human identities). A new ServiceAccount has no power until somebody grants it.
The token in every pod
Long ago, a ServiceAccount's credential was a token that never expired, kept in a Secret. Today the kubelet, the agent on each node, asks the API server's TokenRequest API for a token. It writes the token into the pod as a file. This is a bound token: a signed JWT with three limits.
- Tied to the pod. Delete the pod, and its token stops working. Other pods of the same ServiceAccount keep working.
- Time-limited. A token lasts one hour by default. Your app must re-read the file.
- Audience-scoped. The
audclaim names who the token is for. Another audience is refused.
an exhibition hall. The office prints a day badge for one stall, with its name and today's date. It opens only that stall's back door, and when the stall closes, the badge is dead.
RBAC: who may do what, and where
RBAC decides what a caller may do (see RBAC, ABAC & ReBAC). A rule names resources (pods, secrets) and verbs (get, list, create). Rules only add power; there are no deny rules. A Role lists rules for one namespace, and a RoleBinding gives it to a ServiceAccount.
The permissions that are secretly admin
Some grants look small and are not.
- Reading Secrets.
get,listandwatchall reveal a Secret, so "read-only" is not safe. A legacy token inside is someone else's identity. - Creating pods. Anyone who can create pods in a namespace can run one as any ServiceAccount there. They can also mount any Secret there.
- Exec into pods.
pods/execcan read that container's token files. - Wildcards and cluster-admin.
resources: ["*"]covers every resource type, now and later. Members ofsystem:mastersskip RBAC checks. Cluster-admin "just to make it work" is the Kubernetesadmin:*from least privilege for machines.
Namespaces are the real trust boundary.
Pods that call the cloud: federation, not keys
Do not paste a cloud access key into a Kubernetes Secret: anyone who can read Secrets there would hold a long-lived key. Workload identity federation exists to kill that.
The fix is federation, with the cluster in the role of the CI system. The API server is an OIDC issuer and publishes its public keys. The pod gets a token for the cloud's token service. The cloud checks the signature, then its trust policy. The issuer must be this cluster. The subject must be exactly system:serviceaccount:shop:payments. The audience must be right. If all match, it returns short-lived credentials.
Go deeper: legacy tokens and the version history
- Bound to the pod. The pod's name and unique ID are inside the token. The API server stops accepting it within 60 seconds of the pod's deletion time. That kills one token. Re-creating the ServiceAccount kills every token for it.
- Time-limited. The kubelet asks for a fresh token when the old one is 80% used.
- Audience-scoped. By default the audience is the API server.
Before Kubernetes v1.22, pods got a token from a Secret. Since v1.22, pods get a bound token through the TokenRequest API. Since v1.24, the cluster no longer creates token Secrets by itself. Since v1.29, an auto-generated legacy token that stays unused for a year (the default) is marked invalid, and later deleted. You can still create a never-expiring token Secret by hand. The Kubernetes docs advise it only when the short-lived way truly will not work.
| Legacy Secret token | Bound token (TokenRequest) | |
|---|---|---|
| Stored | In a Secret object in the cluster | Delivered to the pod's file by the kubelet |
| Expires | Never: valid until the ServiceAccount is deleted | Time-limited, refreshed automatically |
| Tied to | Nothing but the ServiceAccount | One pod (name and unique ID) and an audience |
| Who can read it | Anyone who can read Secrets in that namespace | Whatever runs in that pod |
| How to kill it | Delete the Secret or the ServiceAccount | Delete the pod, or wait for it to expire |
A pod that never talks to the API server should carry no API token. Set automountServiceAccountToken: false on the pod or on its ServiceAccount. The token file sits at /var/run/secrets/kubernetes.io/serviceaccount/token.
Go deeper: the four RBAC objects, and the built-in roles
A Role holds rules for one namespace. A RoleBinding gives a Role to a user, group or ServiceAccount in that namespace. Prefer RoleBindings. A ClusterRoleBinding gives power in every namespace, even ones made later.
A ClusterRole is a set of rules for cluster-wide things like nodes, or a reusable template. A RoleBinding can point at a ClusterRole and still grant it in one namespace only. A ClusterRoleBinding grants a ClusterRole in every namespace.
Kubernetes ships four user-facing roles: view, edit, admin and cluster-admin. The view role leaves out Secrets on purpose. The edit role can read them.
Watch more grants. bind and escalate get around the rule that you cannot grant powers you lack, and impersonate acts as someone else.
Watch two more. create on serviceaccounts/token makes tokens for existing accounts. A "read-only" wildcard still reads every Secret.
Go deeper: how the cloud checks a cluster's tokens
Also, do not let pods borrow the node's cloud identity from the machine's metadata service: then all pods share one identity.
Two lessons follow. First, pin the subject. A condition that matches system:serviceaccount:* trusts every pod in the cluster. Second, anyone who can create pods in shop can run one as payments and get its cloud role. Your Kubernetes RBAC is now part of your cloud IAM.
The API server publishes a discovery document at /.well-known/openid-configuration and its public keys at /openid/v1/jwks. The cloud fetches the keys and checks each token's signature with them.
Between services inside the cluster, SPIFFE fits the same way. The SPIRE Kubernetes attestor asks the kubelet which pod is calling, and issues an SVID for mutual TLS.
Go deeper: Zara's audit
Zara asks the cluster what each shop ServiceAccount can do. She uses kubectl auth can-i --list, which works if she may impersonate. Two answers worry her. The CI deployer holds cluster-admin through a ClusterRoleBinding, added "because a deploy failed once". A debugging helper can exec into any pod in shop, including payments, whose token opens the cloud database.
She rebinds the deployer to a Role in shop. She removes the helper's exec right. She moves payments into its own namespace. No code changed, but the blast radius did.
Go deeper: managed Kubernetes and client certificates
Kubernetes does not store people. It has no user objects. The API server trusts something outside to vouch for each person, usually your identity provider using OpenID Connect. To Kubernetes, a person is just a username plus groups.
Each major cloud packages the federation pattern under its own name (as of October 2026). Examples are IAM roles for service accounts and EKS Pod Identity on Amazon EKS, Workload Identity Federation for GKE, and Microsoft Entra Workload ID on AKS. The names differ. The idea is the same: a ServiceAccount maps to a cloud identity, with no stored key.
Client certificates can also log people in. But Kubernetes cannot revoke one certificate on its own. A leaked certificate works until it expires. So the project's hardening guide calls them a poor fit for everyday user logins.
🧪 Interactive lab — enable JavaScript to play with this one.
Start a throwaway cluster on your laptop with kind (open source; it runs Kubernetes inside Docker). Create a ServiceAccount. Run kubectl create token <name> --duration 10m and decode the result with any JWT decoder. Look at its sub, aud and exp. Give the account the view role in one namespace with a RoleBinding. Then ask kubectl auth can-i --list -n <namespace> --as=system:serviceaccount:<namespace>:<name> what it may do. Look for Secrets in the answer. Last, hold your own cluster up against the Kubernetes RBAC good practices.