Skip to content

RBAC

Role-Based Access Control is how Kubernetes answers “may this identity do this thing to this object?” It is allow-only: everything is denied unless a rule allows it, and there is no way to write a denial.

RBAC is genuinely important and genuinely simple once the four object types are clear. Most mistakes come from mixing up the two dimensions: namespaced or cluster-wide for the object being accessed, and namespaced or cluster-wide for the permission being granted.

Reference

Terminology

  • Role — A set of rules in one namespace: verbs on resources. Cannot grant anything cluster-scoped.
  • ClusterRole — The same, but not namespaced. Used for cluster-scoped resources (nodes, persistent volumes, namespaces) or as a reusable set of rules to be bound somewhere.
  • RoleBinding — Grants a Role, or a ClusterRole, to subjects in one namespace.
  • ClusterRoleBinding — Grants a ClusterRole to subjects across the whole cluster.
  • Subject — Who the rules apply to: a User, a Group, or a ServiceAccount.
  • RuleapiGroups, resources, verbs. Which API group, which object types, and what you may do to them.

The Two Dimensions

Object being accessedNamespaced?Grant it with
Pods, Services, ConfigMaps, SecretsYesRole + RoleBinding, or ClusterRole + RoleBinding
Nodes, PersistentVolumes, Namespaces, ClusterRolesNoClusterRole + ClusterRoleBinding only
Custom resourcesUsually yesRole + RoleBinding

The combination worth remembering is ClusterRole + RoleBinding: define the rules once, grant them in a single namespace. That is how you give a team view in one namespace without giving it view everywhere.

ClusterRole "pod-reader"  ──┐
                            ├── RoleBinding (namespace: dev)  ──▶ may read pods in dev
                            └── RoleBinding (namespace: prod) ──▶ may read pods in prod

The Four Object Types, Together

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
  - apiGroups: [""]              # "" is the core group: pods, services, configmaps…
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: default
  name: pod-reader
subjects:
  - kind: ServiceAccount
    name: app
    namespace: default
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Note roleRef cannot be changed after creation — you edit the binding by deleting and recreating it.

Built-In ClusterRoles

ClusterRoleRoughly
viewRead everything except Secrets, in the namespace it is bound to
editview plus create, update and delete most objects
adminedit plus managing roles and bindings within the namespace
cluster-adminEverything, everywhere. Bind it deliberately or not at all

Asking Rather Than Guessing

This is the single most useful RBAC command:

kubectl auth can-i get pods
kubectl auth can-i get pods --as=system:serviceaccount:default:app
kubectl auth can-i --list                                   # everything I may do, here
kubectl auth can-i --list --as=system:serviceaccount:default:app -n default

--as lets you check permissions on behalf of any identity, which turns “I think this will work” into a fact you can verify before applying anything.

Exercises

  1. Read your own permissions.

    kubectl auth can-i --list
    kubectl auth can-i create customresourcedefinitions

    The second is the check for cluster administrator — labctl env reports it too, because some later exercises need it.

  2. Grant, then verify, then test. Create the Role and RoleBinding above, then:

    kubectl auth can-i list pods --as=system:serviceaccount:default:app       # yes
    kubectl auth can-i delete pods --as=system:serviceaccount:default:app     # no
    kubectl auth can-i list secrets --as=system:serviceaccount:default:app    # no

    Now prove it from inside a Pod running as that ServiceAccount, using the token from the Service accounts page. A 403 that disappears when you add a verb is a lesson you will remember.

  3. Grant a cluster-scoped read with a namespaced binding. Create a ClusterRole for nodes with get, list, watch, bind it with a RoleBinding in default, and check:

    kubectl auth can-i list nodes --as=system:serviceaccount:default:app

    A ClusterRole can only grant what the RoleBinding’s namespace allows, so a cluster-scoped resource cannot be granted this way. This is the experiment that makes the two dimensions click.

  4. See how permissions add up. Bind view as well as your own Role and list the effective permissions again. RBAC is purely additive; there is no rule that takes something away.

Gotchas Worth Knowing

  • bind and escalate are privilege-escalation verbs. Someone who may create RoleBindings can grant themselves any permission they can already reference. If you delegate RBAC administration, exclude those verbs or you have delegated everything.
  • A ClusterRoleBinding to a ServiceAccount is cluster-wide. It is easy to write by accident when you meant a RoleBinding.
  • Wildcards are convenient and permanent. resources: ["*"] will include the custom resources and the new API versions nobody has invented yet.
  • view does not include Secrets, deliberately.
  • RBAC is namespaced for the objects, not for the subject. A User or Group has no namespace; only ServiceAccount subjects are namespaced.
  • Permissions are cached briefly. A 403 immediately after a binding change can be a stale authoriser cache rather than a mistake.

What to Take Away

  • Everything is denied unless a rule allows it.
  • Role and ClusterRole describe rules; RoleBinding and ClusterRoleBinding attach them to subjects, in one namespace or across the cluster.
  • ClusterRole + RoleBinding is the pattern that grants a reusable rule set in exactly one namespace.
  • kubectl auth can-i --as turns a permission question into a yes or a no.

Where This Is Practised

Lab 09 in the workbook is built around this. Run labctl list to see whether it is ready yet.