Lesson 15 of 32 · Level 2 — Operator
RBAC & service accounts
Decide who can do what in the cluster. Roles, ClusterRoles and bindings for people, ServiceAccounts for workloads, and how to test and audit permissions with 'kubectl auth can-i'.
Two questions every request answers
Every call to the API server goes through two gates:
- Authentication: who are you? A client certificate, a token, an OIDC login… (covered in depth in Kubernetes Security & Hardening).
- Authorization: are you allowed to do this? Almost always answered by RBAC (role-based access control).
Kubernetes has no user database. "Users" and "groups" are just names taken from the certificate or token. ServiceAccounts are the exception: real API objects, used by workloads.
Think of a hotel. Authentication is the front desk checking your ID. RBAC is your key card: it opens your room and the gym (a Role in one namespace), maybe every floor's gym (a ClusterRole). The card is programmed by a binding: "this guest gets these doors". Staff robots cleaning rooms get their own cards too. Those are ServiceAccounts.
The four objects
| Object | Scope | What it says |
|---|---|---|
| Role | One namespace | These verbs on these resources are allowed |
| ClusterRole | Cluster-wide definition | Same, reusable anywhere (and for cluster-scoped things like nodes) |
| RoleBinding | One namespace | Give a Role or ClusterRole to subjects in this namespace |
| ClusterRoleBinding | Whole cluster | Give a ClusterRole to subjects everywhere |
Subjects are Users, Groups or ServiceAccounts. And remember: RBAC only allows. There are no deny rules, so anything not granted is refused.
A read-only developer in one namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: shop
rules:
- apiGroups: [""] # "" = the core API group (pods, services, configmaps…)
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: asha-reads-pods
namespace: shop
subjects:
- kind: User
name: asha # as presented by her certificate or OIDC token
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Test it without logging in as Asha. Impersonation (--as) is itself a permission that admins have:
$ kubectl auth can-i list pods -n shop --as asha
yes
$ kubectl auth can-i delete pods -n shop --as asha
no
$ kubectl auth can-i list pods -n default --as asha
no
Use the built-in roles
Kubernetes ships user-facing ClusterRoles you should reuse rather than reinvent:
| ClusterRole | Typical use |
|---|---|
view |
Read most objects in a namespace (not Secrets) |
edit |
Create and change most objects (no RBAC changes) |
admin |
Full control of a namespace, including its RBAC |
cluster-admin |
Everything, everywhere. Keep it for break-glass only |
Grant edit to a team in its own namespace with a RoleBinding to a ClusterRole:
$ kubectl create rolebinding team-a-edit --clusterrole=edit --group=team-a -n team-a
ServiceAccounts: identities for workloads
Every namespace has a default ServiceAccount, and pods use it unless told otherwise. Give each app its own instead:
apiVersion: v1
kind: ServiceAccount
metadata:
name: config-watcher
namespace: shop
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: read-config
namespace: shop
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: config-watcher-read-config
namespace: shop
subjects:
- kind: ServiceAccount
name: config-watcher
namespace: shop
roleRef:
kind: Role
name: read-config
apiGroup: rbac.authorization.k8s.io
In the pod spec: serviceAccountName: config-watcher. The kubelet mounts a short-lived, automatically rotated token at /var/run/secrets/kubernetes.io/serviceaccount/token, and client libraries pick it up automatically.
Turn the token off when it isn't needed
Most application pods never call the Kubernetes API. Set automountServiceAccountToken: false on the ServiceAccount or the pod. A token that isn't there can't be stolen.
Try it: least privilege, verified
- Apply the ServiceAccount, Role and RoleBinding above in namespace
shop. - Check it:
kubectl auth can-i list configmaps -n shop --as=system:serviceaccount:shop:config-watcher→ yes.list secrets→ no. - Act as the ServiceAccount with a short-lived token stored as a separate kubeconfig user (so your admin certificate isn't sent too):
kubectl config set-credentials sa-test --token="$(kubectl create token config-watcher -n shop)"thenkubectl --user=sa-test get configmaps -n shop(works) andkubectl --user=sa-test get secrets -n shop(Forbidden). Clean up withkubectl config delete-user sa-test. - Read the error message: it names the user, verb, resource and namespace. That's everything you need to write the missing rule.
Going deeper: RBAC in real organisations
- Bind groups, not individual users. Groups come from your identity provider (OIDC claims), so joiners and leavers are handled there, not in YAML.
- Watch for escalation paths:
create podsin a namespace effectively grants whatever that namespace's ServiceAccounts can do;bind/escalateon roles,impersonate, andnodes/proxyare powerful verbs. Audit who has them. - Aggregated ClusterRoles (label-selected) let add-ons extend
view/edit/adminautomatically for their custom resources. - Review RBAC regularly:
kubectl get clusterrolebindings -o wideand tools such asrbac-toolorkubectl-who-cananswer "who can read Secrets?" quickly.
Recap
- Authentication says who you are; RBAC says what you may do. Rules are allow-only.
- Role / ClusterRole define permissions; RoleBinding / ClusterRoleBinding grant them to users, groups or ServiceAccounts.
- Reuse view / edit / admin; keep cluster-admin for emergencies.
- One ServiceAccount per workload, minimal verbs, and no token if it never calls the API.
kubectl auth can-i … --as …tests permissions before users hit them.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.