RBAC and Service Accounts
The idea in one sentence
Section titled “The idea in one sentence”RBAC splits every permission decision into “what is allowed” (a Role or ClusterRole) and “who gets it” (a RoleBinding or ClusterRoleBinding), and every Pod acts through a ServiceAccount identity whether you configured one or not.
Role and ClusterRole: what is allowed
Section titled “Role and ClusterRole: what is allowed”RBAC (Role-Based Access Control) lives under the rbac.authorization.k8s.io/v1 API group. A Role defines a set of permissions scoped to a single namespace — verbs like get, list, watch, create, update, delete applied to specific resources:
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: namespace: checkout name: pod-readerrules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"]A ClusterRole is the same idea but cluster-scoped — it either grants access to cluster-scoped resources (like nodes or namespaces), or it grants access to namespaced resources in a way that can be reused across many namespaces via multiple RoleBindings:
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: secret-readerrules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list"]Neither of these grants anything on its own — a Role or ClusterRole is just a description of permissions sitting unattached until something binds it to a subject.
RoleBinding and ClusterRoleBinding: who gets it
Section titled “RoleBinding and ClusterRoleBinding: who gets it”A RoleBinding connects a Role (or a ClusterRole, reused in a single namespace) to a subject — a User, Group, or ServiceAccount — within one namespace:
apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: read-pods namespace: checkoutsubjects: - kind: ServiceAccount name: checkout-app namespace: checkoutroleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.ioA ClusterRoleBinding does the same thing but grants the ClusterRole’s permissions across the entire cluster, not just one namespace:
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: read-all-secretssubjects: - kind: Group name: platform-team apiGroup: rbac.authorization.k8s.ioroleRef: kind: ClusterRole name: secret-reader apiGroup: rbac.authorization.k8s.io# Check what a given identity can actually dokubectl auth can-i list pods --namespace checkout --as system:serviceaccount:checkout:checkout-appkubectl get rolebindings,clusterrolebindings -A -o wideServiceAccounts: identity for things running inside Pods
Section titled “ServiceAccounts: identity for things running inside Pods”Kubernetes does not natively manage human users — those authenticate through an external identity provider (OIDC, a cloud IAM integration, client certificates, and so on) at the API server’s authentication layer. What Kubernetes does manage natively is the ServiceAccount: the identity used by processes running inside Pods.
Every Pod runs as some ServiceAccount. If you never specify one, it runs as the namespace’s default ServiceAccount:
apiVersion: v1kind: Podmetadata: name: checkout namespace: checkoutspec: serviceAccountName: checkout-app containers: - name: checkout image: checkout:2.3.0By default, the ServiceAccount’s token is automatically mounted into the Pod’s filesystem so the application inside can authenticate to the API server as that identity. If a workload never talks to the Kubernetes API, that auto-mounted token is unnecessary attack surface — disable it explicitly:
apiVersion: v1kind: ServiceAccountmetadata: name: checkout-app namespace: checkoutautomountServiceAccountToken: falseLeast privilege in practice
Section titled “Least privilege in practice”The whole point of splitting Role/ClusterRole from RoleBinding/ClusterRoleBinding is to make it easy to scope access narrowly:
- Grant only the specific verbs a workload actually needs (
get/list/watchfor a read-only controller, notcreate/update/delete). - Grant access to specific resources, and where possible specific
resourceNames, instead of an entire resource type. - Prefer a namespaced Role plus RoleBinding over a ClusterRole plus ClusterRoleBinding whenever the workload only needs access within one namespace.
- Avoid binding any workload ServiceAccount to the built-in
cluster-adminClusterRole — it grants unrestricted access to every resource in the cluster, and almost nothing legitimately needs that.
flowchart LR pod["Pod\n(identity: ServiceAccount)"] --> req["Request to API server"] req --> authz["API server\nauthorization check"] sa["ServiceAccount"] --> rb["RoleBinding"] role["Role\n(verbs on resources)"] --> rb rb --> authz authz -- "allowed" --> ok["Request proceeds"] authz -- "denied" --> deny["403 Forbidden"]