Sign in
← Kubernetes: security
8 min · medium · Lesson 3 of 3

RBAC: Role and RoleBinding

The four RBAC objects, a field-by-field look at Role and RoleBinding, and kubectl auth can-i as a way to stop guessing at a 403.

The four RBAC objects, a field-by-field look at Role and RoleBinding, and kubectl auth can-i as a way to stop guessing about a 403.

Why this matters

In the last lesson, the Pod got its own identity, a ServiceAccount. By itself it grants no permissions: the apiserver knows who's asking, but denies everything. What's actually allowed is decided by RBAC.

It's easy to get this wrong in both directions. Miss one word in a rule and the app fails with a 403. Grant too much, say the built-in cluster-admin "just to make it work", and breaching one Pod opens up the whole cluster.

Four RBAC objects

ServiceAccount name: app-sa namespace: demo who RoleBinding subjects: app-sa roleRef: pod-reader what Role name: pod-reader verbs: get, list
RoleBinding doesn't store any permissions. It holds only two names: who and what.

RBAC (role-based access control) in Kubernetes rests on four kinds:

The usual combination, "grant a ServiceAccount these permissions on these resources in this namespace," is built from the first three: a Role describes the permissions, a RoleBinding grants them to a specific SA.

RBAC permissions only add up. There are no deny rules: anything not granted by some binding is denied (docs).

Role: what it lets you do

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: demo
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]

Breaking down the fields:

A Role can hold several rules, each with its own set of apiGroups/resources/verbs.

RoleBinding: who gets it

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-sa-can-read
  namespace: demo
subjects:
  - kind: ServiceAccount
    name: app-sa
    namespace: demo
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

subjects is an array of who gets the permissions. Kinds: ServiceAccount, User, Group. For a ServiceAccount, you give the namespace it lives in. In a RoleBinding, if that SA namespace is left empty the apiserver reads it as the binding's own namespace. In a ClusterRoleBinding, without it the object won't even get created: subjects[0].namespace: Required value.

A typo in the SA's name or its namespace gives no sign of itself: the RoleBinding gets created and grants permissions to a subject that doesn't exist. For example, namespace: default instead of demo. You check this with the command kubectl auth can-i get pods -n demo --as=system:serviceaccount:demo:app-sa.

roleRef defines exactly what's granted. You can't change your mind here after creation: the field is immutable, so changing the role means deleting and recreating the RoleBinding.

Where ServiceAccounts live

ServiceAccount was covered in the last lesson. Briefly: without spec.serviceAccountName, a Pod runs under SA default of its own namespace, and the SA's token sits at /var/run/secrets/kubernetes.io/serviceaccount/. That's where the app reads it to talk to the API server.

Create your own SA:

kubectl create serviceaccount app-sa -n demo

Attach it to a Pod:

spec:
  serviceAccountName: app-sa

kubectl auth can-i: your main diagnostic tool

Before guessing "why is it 403," it's worth asking the API server directly:

kubectl auth can-i get pods
kubectl auth can-i delete pods

This command answers for the current user. To check on behalf of another subject, use the --as flag (demo here is the namespace the SA lives in):

kubectl auth can-i get pods -n demo --as=system:serviceaccount:demo:app-sa
kubectl auth can-i delete pods -n demo --as=system:serviceaccount:demo:app-sa

A yes or no answer with no lengthy investigation (docs). A resource from another group is written with the group after a dot: kubectl auth can-i list deployments.apps -n demo --as=....

The text of a 403 also tells you what's missing. It has the subject, verb, resource, and group:

Error from server (Forbidden): deployments.apps is forbidden: User "system:serviceaccount:demo:app-sa" cannot list resource "deployments" in API group "apps" in the namespace "demo"

Role vs ClusterRole: when to use which

Role and bindingWhere the permissions apply
Role + RoleBindingresources in one namespace
ClusterRole + RoleBindingnamespaced resources, only in that namespace
ClusterRole + ClusterRoleBindingthe whole cluster, including nodes, PV, CRD

A common mistake: create a Role on nodes in some namespace and get denied anyway. Kubernetes accepts such a Role with no error, but nodes are a cluster-scoped resource: they don't exist at the namespace level at all.

ClusterRole with a RoleBinding most often shows up with the built-in roles. view gives read access to most objects in a namespace, except Secret, Role and RoleBinding. edit adds write access, including Secret. admin also adds managing Role and RoleBinding in the namespace (docs). Grant view to one SA in one namespace:

kubectl create rolebinding app-sa-view -n demo \
  --clusterrole=view --serviceaccount=demo:app-sa

Common mistakes

# Create SA, Role, RoleBinding imperatively
kubectl create serviceaccount app-sa -n demo
kubectl create role pod-reader -n demo \
  --verb=get,list,watch \
  --resource=pods,pods/log
kubectl create rolebinding app-sa-can-read -n demo \
  --role=pod-reader \
  --serviceaccount=demo:app-sa

# What's in the Role
kubectl get role pod-reader -n demo -o yaml
kubectl describe rolebinding app-sa-can-read -n demo

# Check effective permissions
kubectl auth can-i get pods -n demo --as=system:serviceaccount:demo:app-sa
kubectl auth can-i delete pods -n demo --as=system:serviceaccount:demo:app-sa

# Field structure
kubectl explain role.rules
kubectl explain rolebinding.subjects
kubectl explain rolebinding.roleRef

kubectl create role --resource=deployments fills in the group apps on its own, while in YAML you write the group yourself.

What you'll need for the tasks

Each lab brings up its own kind cluster on your machine, instructions are in its README.md.

In short

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words, gives you 3 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.

Get access for $49

Tell the agent: use MCP izi, give me lesson k8s-security-03-rbac