Kubernetes Administration — Level by Level›15 · RBAC & service accounts

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'.

Practitioner
Key wordsRBACRoleClusterRoleRoleBindingServiceAccountleast privilegekubectl auth can-i

Two questions every request answers

Every call to the API server goes through two gates:

  1. Authentication: who are you? A client certificate, a token, an OIDC login… (covered in depth in Kubernetes Security & Hardening).
  2. 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

  1. Apply the ServiceAccount, Role and RoleBinding above in namespace shop.
  2. Check it: kubectl auth can-i list configmaps -n shop --as=system:serviceaccount:shop:config-watcher → yes. list secrets → no.
  3. 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)" then kubectl --user=sa-test get configmaps -n shop (works) and kubectl --user=sa-test get secrets -n shop (Forbidden). Clean up with kubectl config delete-user sa-test.
  4. 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 pods in a namespace effectively grants whatever that namespace's ServiceAccounts can do; bind/escalate on roles, impersonate, and nodes/proxy are powerful verbs. Audit who has them.
  • Aggregated ClusterRoles (label-selected) let add-ons extend view/edit/admin automatically for their custom resources.
  • Review RBAC regularly: kubectl get clusterrolebindings -o wide and tools such as rbac-tool or kubectl-who-can answer "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.