click the screen · press Enter
← back to blog
Kubernetes Journey · Topic 10

A ClusterRole Is a Role That Forgot Its Namespace

Kubernetes ClusterRoles and ClusterRoleBindings explained - Kubernetes Journey

Last time I set up a role that let a user mess with pods in one namespace. Then I tried the same trick on a node and it just wouldn't work, no matter how I wrote the rule. Turns out a normal Role literally can't reach a node, because a node doesn't live in a namespace. That gap is the whole reason ClusterRoles exist, and it's what today is about.

If you've not read Topic 9 on RBAC yet, start there, because this builds straight on it. Same format as always: what it is, how it works, why it matters, one takeaway.

The split nobody tells you about first

Here's the thing that unlocks all of this: Kubernetes resources come in two flavours, and where a resource lives decides which RBAC objects can control it.

Namespaced resources live inside a namespace. Pods, deployments, services, secrets, jobs, replica sets. When you say "the pods in default", the namespace is part of the address. A Role and RoleBinding cover these, scoped to that one namespace.

Cluster-scoped resources don't belong to any namespace at all. A node is a physical or virtual machine, and it just is, cluster-wide. Same with persistent volumes (chunks of storage), namespaces themselves, and certificate signing requests. There's no namespace in their address, so a namespaced Role has nothing to grab onto.

You don't have to memorise which is which. Ask the cluster:

# everything that lives in a namespace
kubectl api-resources --namespaced=true

# everything that doesn't (nodes, PVs, namespaces, CSRs...)
kubectl api-resources --namespaced=false
Where a resource lives decides what can govern it
Namespaced · use Role + RoleBinding

pods, deployments, services, secrets, configmaps, jobs, replica sets. Addressed as "this resource, in this namespace".

Cluster-scoped · use ClusterRole + ClusterRoleBinding

nodes, persistent volumes, namespaces, certificate signing requests. No namespace in the address, so a plain Role can't touch them.

Pick the RBAC object to match the resource's scope, not the other way round.

If a resource has no namespace, a Role can't govern it. That's your cue to reach for a ClusterRole.

What a ClusterRole and ClusterRoleBinding actually are

Good news: if Topic 9 stuck, you already know 90% of this. A ClusterRole is the exact same idea as a Role, a bundle of permissions written as apiGroups, resources and verbs, except it isn't pinned to a namespace. A ClusterRoleBinding is the same idea as a RoleBinding, the link that hands that role to a user or group, except it applies across the whole cluster.

Two objects, same job as before, just one scope wider:

The four RBAC objects, side by side
ObjectScopeGoverns
Roleone namespacenamespaced resources in that namespace
RoleBindingone namespaceattaches a Role (or ClusterRole) to subjects there
ClusterRolewhole clustercluster-scoped resources, or namespaced ones everywhere
ClusterRoleBindingwhole clusterattaches a ClusterRole to subjects cluster-wide
Role is to RoleBinding what ClusterRole is to ClusterRoleBinding, one namespace vs the whole cluster.

Building one: a node admin

Say I want a user who can look at, create and delete nodes anywhere in the cluster. Nodes are cluster-scoped, so this has to be a ClusterRole. The YAML looks almost identical to a Role, the only real change is kind: ClusterRole and no namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: node-admin
rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "list", "create", "delete"]

Now bind it. A ClusterRoleBinding says "give this ClusterRole to this subject, everywhere":

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: node-admin-binding
subjects:
- kind: User
  name: cluster-admin-user
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: node-admin
  apiGroup: rbac.authorization.k8s.io

Apply both and you're done:

kubectl create -f node-admin-role.yaml
kubectl create -f node-admin-binding.yaml

Same shape for a storage admin: point the ClusterRole at persistentvolumes and persistentvolumeclaims instead of nodes, pick the verbs you want, bind it. The pattern doesn't change, only the resources do. A ClusterRole is really just a Role that forgot its namespace, and on paper that's the entire difference.

Watch the apiGroup line apiGroups: [""] is still the core group, the same one that holds nodes and persistent volumes. If you ever bind a ClusterRole to CSRs you'll switch to certificates.k8s.io. This is the API groups map from Topic 8 doing its job again.

The trick that catches everyone: scope comes from the binding

This is the part I got wrong in my head at first, so pay attention here. A ClusterRole doesn't have to mean "cluster-wide access". The binding decides the scope, not the role.

Bind a ClusterRole with a ClusterRoleBinding and the permissions apply everywhere. But bind that same ClusterRole with an ordinary RoleBinding inside one namespace, and the permissions only apply in that namespace. Same role, two very different blast radii.

Same ClusterRole, different binding, different reach
Binding usedWhere it appliesStatus
ClusterRoleBindingevery namespace, plus cluster-scoped resourceswidest reach
RoleBinding (in namespace X)only inside namespace Xcontained
Why this matters: teams define a ClusterRole once (say "read secrets") and reuse it per-namespace with RoleBindings, without granting it everywhere.

Why would anyone do that? Reuse. You define a ClusterRole like "secret-reader" once, then hand it out namespace by namespace with RoleBindings, so each team only gets it in their own space. One definition, controlled reach. The ClusterRole is the permission set; the binding type is the scope dial. Read the binding if you want to know the real reach.

The default ClusterRoles already on your cluster

You don't start from nothing. Every cluster ships with built-in ClusterRoles, and four are worth knowing by name because you'll bind to them constantly (and attackers look for them):

  • view reads most things in a namespace but not secrets.
  • edit can read and write most things, still no RBAC changes.
  • admin full control within a namespace, including RBAC, but not the cluster.
  • cluster-admin the god role. Every verb on every resource, everywhere. No limits.

These save you writing common roles from scratch. They're also the thing I check first on an engagement, because cluster-admin handed out casually is game over.

The pentest angle: who's holding the keys

Once I have any access to a cluster, ClusterRoleBindings are one of the first things I enumerate, because a single loose one can mean total control. The question is simple: who is bound to something powerful, especially cluster-admin?

who has the crown jewels?
$ kubectl get clusterrolebindings # list every cluster-wide binding $ kubectl describe clusterrolebinding cluster-admin Role: Kind: ClusterRole Name: cluster-admin Subjects: Kind Name Group system:masters == anyone in system:masters is effectively root of the cluster ==
Find who is bound to cluster-admin, then check your own reach with auth can-i.

Then I check my own effective access, which is the fastest way to know if I've landed somewhere useful:

# can I act on cluster-scoped resources?
kubectl auth can-i delete nodes
kubectl auth can-i '*' '*'          # can I do everything?

# dump a subject's full reach without logging in as them
kubectl auth can-i --list --as=some-service-account
Defender takeaway Audit your ClusterRoleBindings regularly. Anyone bound to cluster-admin owns the cluster, full stop. Prefer namespaced Roles, or a ClusterRole reused via per-namespace RoleBindings, over a blanket ClusterRoleBinding. And treat the ability to create ClusterRoleBindings as just as dangerous as being cluster-admin, because it's a one-step path to it.

The mental model I'm keeping

The mental model I'm walking away with: the resource's scope tells you which pair to use, and the binding's type tells you the real reach. Cluster-scoped thing? ClusterRole. Want it everywhere? ClusterRoleBinding. Want that same role in just one namespace? Bind it with a RoleBinding instead. I'm still fuzzy on how aggregation works, where a ClusterRole can pull in rules from other labelled ClusterRoles automatically, so if you've used aggregated roles in production I'd like to hear whether they're worth the complexity. Next I want to dig into service accounts and how pods get their own identity, because that's where a lot of real escalation actually starts.

If this made the Role vs ClusterRole thing finally click, come say hi on LinkedIn or the contact page, and tell me what you want next. More in the Kubernetes Journey.

Further reading

FAQ

What is the difference between a Role and a ClusterRole?

A Role grants permissions inside one namespace only. A ClusterRole is not tied to a namespace, so it can grant access to cluster-scoped resources like nodes and persistent volumes, or to namespaced resources across every namespace at once.

What are cluster-scoped resources in Kubernetes?

Cluster-scoped resources live outside any namespace: nodes, persistent volumes, namespaces themselves, and certificate signing requests. Run kubectl api-resources --namespaced=false to list them. A namespaced Role cannot control these, which is exactly why ClusterRoles exist.

Can a ClusterRole be limited to one namespace?

Yes. If you bind a ClusterRole with a RoleBinding instead of a ClusterRoleBinding, the permissions only apply inside that binding's namespace. The same ClusterRole bound with a ClusterRoleBinding applies across the whole cluster.