Prerequisites: kubernetes, serviceaccount
Authentication establishes who is asking; authorization decides whether the answer is yes. On devata (and virtually every modern cluster) the authorizer is RBAC, Role-Based Access Control: the apiserver runs with --authorization-mode=...,RBAC and consults RBAC objects for every request. Official reference: Using RBAC authorization.
The model is deny by default, and there are no deny rules at all. An identity can do exactly the union of what has been granted to it, and a grant can only add. This makes reasoning simple: to know what an identity can do, list its bindings; to revoke, remove a binding. Prove the default with an identity nothing was ever granted to:
kubectl auth can-i list nodes --as=system:serviceaccount:default:defaultThe answer is no. The default ServiceAccount in every namespace authenticates fine and is authorized for nothing.
Four objects, two pairs
RBAC is four kinds that come in two pairs: rules, and attachments of rules to identities.
- A Role is a named list of rules, valid inside one namespace.
- A ClusterRole is the same list of rules, defined cluster-wide.
- A RoleBinding attaches a Role (or a ClusterRole) to subjects within one namespace.
- A ClusterRoleBinding attaches a ClusterRole to subjects across the whole cluster.
A rule names three things: apiGroups, resources, and verbs. The read-only verbs are get (one object by name), list (all of them), and watch (a change stream); the write verbs are create, update, patch, delete. “Read-only” as a grant means listing only read verbs, and nothing implies anything else: list does not grant get, and neither grants watch.
Which pair you need is decided by scope. Some resources are namespaced (pods, services, secrets); others exist outside any namespace entirely: nodes, namespaces themselves, PersistentVolumes. A Role cannot describe cluster-scoped resources, so an identity that must read nodes needs a ClusterRole and a ClusterRoleBinding. The same pair is also the way to grant reads across all namespaces at once, as opposed to one RoleBinding per namespace.
A worked grant: the snapshot publisher
The publishing-the-cluster-snapshot job needs to read exactly four resource kinds, cluster-wide, and write nothing:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: snapshot-reader
rules:
- apiGroups: [""]
resources: ["nodes", "namespaces", "services", "persistentvolumeclaims"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: snapshot-publisher-can-read
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: snapshot-reader
subjects:
- kind: ServiceAccount
name: snapshot-publisher
namespace: showcaseRead what is absent. secrets is not in the resource list, so this identity cannot read any Secret in any namespace, which is the guarantee the sealedsecret note leans on when it says an unsealed Secret is readable by “anyone RBAC lets read Secrets”. watch is absent because a batch job has no use for a change stream. There are no write verbs, so even a fully compromised publisher pod can deface nothing.
Built-in ClusterRoles exist (view, edit, admin, cluster-admin; run kubectl get clusterroles | head -20 to see the zoo) and it is tempting to bind view and move on. Do not: view grants dozens of resource kinds including configmaps, which routinely carry things that should not leak. Four resources and two verbs is a rule you can hold in your head, and least privilege is exactly that discipline.
Verifying a grant
kubectl auth can-i asks the apiserver’s authorizer directly, and --as lets you ask on behalf of another identity. After the publisher’s RBAC is applied:
kubectl auth can-i list nodes --as=system:serviceaccount:showcase:snapshot-publisher # yes
kubectl auth can-i get secrets --as=system:serviceaccount:showcase:snapshot-publisher # no
kubectl auth can-i --list --as=system:serviceaccount:showcase:snapshot-publisherThe last form prints the complete grant surface. It should be four resources, two verbs, plus the trivial self-review permissions every identity has. If anything else appears, a binding is attaching more than you think, and the time to notice is now, not after the identity’s token leaks.