Module 11 ยท Lesson 11.1
RBAC & ServiceAccounts
Every pod runs as someone. Not a human someone โ a ServiceAccount, the
identity a pod presents whenever it talks to the Kubernetes API itself. If
you've never set one, your pods use the default ServiceAccount of their
namespace, which (sensibly) can do almost nothing.
The permission chain
RBAC (Role-Based Access Control) decides what a given identity can do, through three objects chained together:
- Role (or ClusterRole) โ a list of permissions: which verbs
(
get,list,watch,create,delete, ...) are allowed on which resources (pods,deployments,secrets, ...), optionally scoped to specific resource names. ARoleis namespaced; aClusterRoleapplies cluster-wide or can be reused across namespaces. - RoleBinding (or ClusterRoleBinding) โ grants a Role to a subject: a User, a Group, or โ the one that matters here โ a ServiceAccount.
- ServiceAccount โ the identity itself, attached to a pod via
spec.serviceAccountName. Kubernetes mounts its token into the pod automatically, which is how the pod authenticates.
The RBAC permission chain
Nothing is granted by default beyond this chain. No Role, no RoleBinding,
no permission โ full stop. This is "default deny": the safest possible
starting position, but it means a pod that needs to talk to the API (an
operator, a controller, a CI job calling kubectl from inside the cluster)
needs this chain deliberately wired up, every time.
Reading a Forbidden error
When the chain is missing or incomplete, the API server's rejection is unusually specific โ it names the exact subject, verb, resource, and scope it refused:
Error from server (Forbidden): pods is forbidden: User
"system:serviceaccount:my-ns:report-reader" cannot list resource "pods"
in API group "" in the namespace "my-ns"
That single line is almost always enough to write the fix: a Role
granting list on pods, bound to report-reader via a RoleBinding, in
my-ns. The fastest way to test a fix before touching a real pod is
kubectl auth can-i <verb> <resource> --as=system:serviceaccount:<ns>:<name> -n <ns>
โ it asks the API server to evaluate the same decision without needing a
running pod at all.
The infrastructure analogy
This is IAM, reshaped for an API instead of a cloud console: a Role is a policy document, a RoleBinding is a policy attachment, a ServiceAccount is closest to an instance role โ an identity your compute assumes, not a human's credentials. If you've attached an IAM role to an EC2 instance or an IRSA role to an EKS pod (more on that in Module 15), this chain will feel immediately familiar โ right down to "default deny" being the only sane starting position.
In the lab, you'll hit exactly the Forbidden error above, from inside a real pod using its own mounted identity, and fix it the same way you would in production.
๐งช Lab: lab-22-rbac-and-serviceaccounts
Preview onlyGoal
Hit a real RBAC Forbidden error from inside a pod using its own
in-cluster identity, then fix it with the minimal Role + RoleBinding.
Tasks
- Apply the starting manifests:
kubectl apply -f manifests/ - Confirm the
reporterpod isRunning:kubectl get pods -n lab-22-rbac-and-serviceaccounts - Try to use its identity from inside the pod:
kubectl exec -n lab-22-rbac-and-serviceaccounts reporter -- kubectl get pods -n lab-22-rbac-and-serviceaccountsโ this fails withForbidden. Read the message carefully: it names the exact subject, verb, and resource that's missing. - Before touching anything, confirm you understand why without needing
a pod at all:
kubectl auth can-i list pods --as=system:serviceaccount:lab-22-rbac-and-serviceaccounts:report-reader -n lab-22-rbac-and-serviceaccountsโ should printno. - Create a
Rolegrantingget,list,watchonpods, and aRoleBindinggranting that Role to thereport-readerServiceAccount, both inlab-22-rbac-and-serviceaccounts. Add them as a new file undermanifests/and apply it. - Re-run the
kubectl auth can-icommand from step 4 โ it should now printyes. - Re-run the
kubectl exec ... kubectl get podscommand from step 3 โ it should now succeed.
Check
Run the check once step 7 succeeds.
This lab runs against a real local Kubernetes cluster with an automated grader โ clone the repo and run make start to do it for real.
๐ Quiz
1. A pod needs to call the Kubernetes API from inside the cluster. What identity does it authenticate as by default, if you've set nothing?
2. What's the difference between a Role and a RoleBinding?
3. A pod's log shows: `Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:billing:worker" cannot list resource "pods" in API group "" in the namespace "billing"`. What's the minimal fix?scenario
4. What's the fastest way to check whether a ServiceAccount can perform an action, without touching a real pod?
5. Compared to attaching an IAM role to an EC2 instance, a Kubernetes ServiceAccount bound via a RoleBinding is closest to:scenario
Progress isn't saved in this preview โ run the course locally to track completion and grade labs for real.