Skip to main content
RunBook Academy

KubernetesLVIII · RBACRBAC

Role and ClusterRole — the verbs the cluster allows

Advanced⏱ ~16 minkubectl

What you'll learn

  • Write a minimal Role and ClusterRole for a real workload
  • Distinguish resourceName (one object) from resources (a kind)
  • Use aggregation rules to compose ClusterRoles from common building blocks
  • Identify the failure modes of wildcard RBAC

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

A Role is a named set of permissions within a namespace. A ClusterRole is the same, cluster-wide. Every production RBAC implementation starts with the smallest possible Role that allows the work, then composes larger roles out of smaller ones via aggregation. This lesson covers the shape of a Role, the verbs and resources, the aggregation pattern, and the wildcard failure modes.

The anatomy of a Role

A Role has three fields: apiGroups, resources, and verbs. The intersection is what is allowed.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: prod
rules:
- apiGroups: [""]         # the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
- apiGroups: [""]         # can also include resourceNames
  resources: ["pods/log"]
  verbs: ["get"]
  resourceNames: ["api", "worker"]   # only these pods

A user bound to this Role can get, watch, and list Pods in the prod namespace, and get the logs of two specific pods (api and worker). No other resources, no other verbs, no other namespaces.

flowchart LR
    A[Identity] --> B[RoleBinding]
    B --> C[Role]
    C --> D[Verbs on resources]
    D --> E[Within namespace]

The verbs

The Kubernetes API has seven verbs that apply to almost every resource:

VerbWhat it allows
getRead a single object by name
listRead all objects of a kind
watchSubscribe to changes (used by informers)
createCreate a new object
updateReplace an existing object (full overwrite)
patchApply a partial change
deleteDelete an object
deletecollectionDelete all objects of a kind

Plus three special verbs that appear on subresources:

VerbWhat it allows
bindBind a RoleBinding to a Role (rare)
escalateCreate a RoleBinding that grants permissions the caller does not have
impersonateAct as another user (rare)
useUse a resource (e.g., pods/exec)

The resources

The resources field is the API kind: pods, services, configmaps, secrets, nodes, persistentvolumeclaims, etc. The apiGroups field is the API group: "" (core), apps, batch, rbac.authorization.k8s.io, etc.

rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
  resources: ["jobs", "cronjobs"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["configmaps", "secrets"]
  verbs: ["get", "list", "watch"]

A role for a typical CI/CD pipeline that needs to read deployments, jobs, configmaps, and secrets — but never write — has exactly these rules. Adding update to any of them expands the surface significantly.

ClusterRole

A ClusterRole is the same shape as a Role, but cluster-wide:

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

A ClusterRoleBinding to this ClusterRole grants the permissions in every namespace. A role for a monitoring workload that scrapes every Pod’s metrics is typically a ClusterRole.

Aggregation rules

A ClusterRole can aggregate permissions from other ClusterRoles via label selectors:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-all
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: []  # populated automatically

Any ClusterRole with the label rbac.example.com/aggregate-to-monitoring: "true" is included. The aggregated role’s rules are the union of all matching ClusterRoles. Adding a new ClusterRole with the label automatically extends the aggregated role.

flowchart LR
    A[ClusterRole: monitoring-pods] -->|label match| D[ClusterRole: monitoring-all]
    B[ClusterRole: monitoring-services] -->|label match| D
    C[ClusterRole: monitoring-configmaps] -->|label match| D
    D --> E[ClusterRoleBinding]

This is the pattern used by the upstream cluster-admin role’s aggregation — every add-on controller adds a ClusterRole with the right label, and cluster-admin automatically grants it.

Wildcards

A Role can use * for apiGroups, resources, or verbs. Each wildcard dramatically expands the surface:

# Worst-case ClusterRole
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]

This is cluster-admin. A user bound to this is God. A misconfiguration that grants this to a ServiceAccount is the most common root cause of cluster compromises.

# Less awful but still wrong
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["*"]

This allows the user to create, update, patch, and delete Deployments in the namespace. Better than cluster-admin, but still wide. A pattern to avoid.

# Minimum surface
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch"]
  resourceNames: ["myapp"]  # only this one Deployment

This allows read of one specific Deployment. The smallest possible role.

Common production patterns

Three patterns recur:

  1. CI/CD readget, list, watch on deployments, pods, services, configmaps, secrets. No write verbs.
  2. Application runtimeget, list, watch on a specific ConfigMap or Secret (via resourceNames). No other resources.
  3. Operator — full verbs on the CRD it manages, plus get/list/watch on the underlying resources (Deployments, Services). Often uses aggregation to compose the role.

Production failure modes

  1. verbs: ["*"] instead of ["get", "list", "watch"]. A role with all verbs is a write role. The team intended read-only.
  2. resources: ["*"]. A role with all resources includes Secrets, ConfigMaps, and CRDs. The team intended a specific set.
  3. apiGroups: ["*"]. A role with all API groups includes rbac.authorization.k8s.io and admissionregistration.k8s.io. The user can create RoleBindings and admission policies — RBAC escalation.
  4. Aggregation that grows unbounded. An aggregated role that pulls in every role with a label can grant permissions the operator never intended. Audit the aggregation chain.

Cross-course references

  • The Linux course covers the file permissions (0600, 0640) that Kubernetes RBAC does not enforce — file permissions on Secret data are a separate concern.
  • The Observability course covers the audit log that records every RBAC decision.

Quiz

Knowledge check · 4 questions

  1. Q1. Which is the smallest possible RBAC rule that allows a workload to read the contents of a specific ConfigMap named `app-config` in the `prod` namespace?

  2. Q2. An RBAC role that grants `update` on Pods allows the caller to modify the Pod's container image and security context, but does not allow Pod creation or deletion.

  3. Q3. Your team's Helm chart ships a ServiceAccount with a ClusterRoleBinding to a ClusterRole called `read-everything`. The ClusterRole has `apiGroups: ['*'], resources: ['*'], verbs: ['get', 'list', 'watch']`. An attacker compromises a Pod that uses this SA. What is the blast radius?

    The ClusterRole has full read on every resource in every API group in the cluster. The SA is `default` in the `ci` namespace. The cluster has 80 nodes, 1,200 workloads, 200 ServiceAccounts. The SA's token is projected into the Pod.

  4. Q4. Explain how RBAC aggregation works and give one production use case.

Passing score: 75%. Answers are checked in this browser.

Production discipline

A defensible RBAC implementation writes the smallest Role that allows the work, scopes it to a namespace unless cluster-wide is required, and avoids wildcards. Aggregation is the right pattern for composing roles from common building blocks (monitoring, observability, CI/CD), but every aggregation chain must be audited — the aggregated role’s surface is the union of every contributing role. A cluster that has apiGroups: ['*'], resources: ['*'], verbs: ['*'] in a ClusterRoleBinding to a ServiceAccount is one compromised Pod away from a full cluster compromise. The discipline is to write the minimum, audit the aggregation, and test with kubectl auth can-i.