RBAC
Role-Based Access Control is how Kubernetes answers “may this identity do this thing to this object?” It is allow-only: everything is denied unless a rule allows it, and there is no way to write a denial.
RBAC is genuinely important and genuinely simple once the four object types are clear. Most mistakes come from mixing up the two dimensions: namespaced or cluster-wide for the object being accessed, and namespaced or cluster-wide for the permission being granted.
Reference
Terminology
- Role — A set of rules in one namespace: verbs on resources. Cannot grant anything cluster-scoped.
- ClusterRole — The same, but not namespaced. Used for cluster-scoped resources (nodes, persistent volumes, namespaces) or as a reusable set of rules to be bound somewhere.
- RoleBinding — Grants a Role, or a ClusterRole, to subjects in one namespace.
- ClusterRoleBinding — Grants a ClusterRole to subjects across the whole cluster.
- Subject — Who the rules apply to: a
User, aGroup, or aServiceAccount. - Rule —
apiGroups,resources,verbs. Which API group, which object types, and what you may do to them.
The Two Dimensions
| Object being accessed | Namespaced? | Grant it with |
|---|---|---|
| Pods, Services, ConfigMaps, Secrets | Yes | Role + RoleBinding, or ClusterRole + RoleBinding |
| Nodes, PersistentVolumes, Namespaces, ClusterRoles | No | ClusterRole + ClusterRoleBinding only |
| Custom resources | Usually yes | Role + RoleBinding |
The combination worth remembering is ClusterRole + RoleBinding: define the rules once, grant them in a single namespace. That is how you give a team view in one namespace without giving it view everywhere.
ClusterRole "pod-reader" ──┐
├── RoleBinding (namespace: dev) ──▶ may read pods in dev
└── RoleBinding (namespace: prod) ──▶ may read pods in prodThe Four Object Types, Together
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" is the core group: pods, services, configmaps…
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: default
name: pod-reader
subjects:
- kind: ServiceAccount
name: app
namespace: default
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.ioNote roleRef cannot be changed after creation — you edit the binding by deleting and recreating it.
Built-In ClusterRoles
| ClusterRole | Roughly |
|---|---|
view | Read everything except Secrets, in the namespace it is bound to |
edit | view plus create, update and delete most objects |
admin | edit plus managing roles and bindings within the namespace |
cluster-admin | Everything, everywhere. Bind it deliberately or not at all |
Asking Rather Than Guessing
This is the single most useful RBAC command:
kubectl auth can-i get pods
kubectl auth can-i get pods --as=system:serviceaccount:default:app
kubectl auth can-i --list # everything I may do, here
kubectl auth can-i --list --as=system:serviceaccount:default:app -n default--as lets you check permissions on behalf of any identity, which turns “I think this will work” into a fact you can verify before applying anything.
Exercises
Read your own permissions.
kubectl auth can-i --list kubectl auth can-i create customresourcedefinitionsThe second is the check for cluster administrator —
labctl envreports it too, because some later exercises need it.Grant, then verify, then test. Create the Role and RoleBinding above, then:
kubectl auth can-i list pods --as=system:serviceaccount:default:app # yes kubectl auth can-i delete pods --as=system:serviceaccount:default:app # no kubectl auth can-i list secrets --as=system:serviceaccount:default:app # noNow prove it from inside a Pod running as that ServiceAccount, using the token from the Service accounts page. A
403that disappears when you add a verb is a lesson you will remember.Grant a cluster-scoped read with a namespaced binding. Create a ClusterRole for
nodeswithget,list,watch, bind it with a RoleBinding indefault, and check:kubectl auth can-i list nodes --as=system:serviceaccount:default:appA ClusterRole can only grant what the RoleBinding’s namespace allows, so a cluster-scoped resource cannot be granted this way. This is the experiment that makes the two dimensions click.
See how permissions add up. Bind
viewas well as your own Role and list the effective permissions again. RBAC is purely additive; there is no rule that takes something away.
Gotchas Worth Knowing
bindandescalateare privilege-escalation verbs. Someone who may create RoleBindings can grant themselves any permission they can already reference. If you delegate RBAC administration, exclude those verbs or you have delegated everything.- A ClusterRoleBinding to a ServiceAccount is cluster-wide. It is easy to write by accident when you meant a RoleBinding.
- Wildcards are convenient and permanent.
resources: ["*"]will include the custom resources and the new API versions nobody has invented yet. viewdoes not include Secrets, deliberately.- RBAC is namespaced for the objects, not for the subject. A
UserorGrouphas no namespace; onlyServiceAccountsubjects are namespaced. - Permissions are cached briefly. A
403immediately after a binding change can be a stale authoriser cache rather than a mistake.
What to Take Away
- Everything is denied unless a rule allows it.
- Role and ClusterRole describe rules; RoleBinding and ClusterRoleBinding attach them to subjects, in one namespace or across the cluster.
- ClusterRole + RoleBinding is the pattern that grants a reusable rule set in exactly one namespace.
kubectl auth can-i --asturns a permission question into a yes or a no.
Where This Is Practised
Lab 09 in the workbook is built around this. Run labctl list to see whether it is ready yet.