Verbs and resources — the RBAC decision matrix
What you'll learn
- Map common operational tasks to the minimum RBAC verbs and resources
- Distinguish read-only verbs from write verbs and the danger of each
- Identify the dangerous combinations (`update` on Pods, `patch` on Deployments, `create` on ClusterRoleBindings)
- Build a Role for a real workload with concrete verbs and resources
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
A Role is a matrix of verbs × resources. The matrix is what determines what the subject can do. This lesson maps common production tasks to the minimum RBAC verbs, identifies the dangerous combinations, and shows how to build a Role for a real workload.
The verb × resource matrix
For a typical workload in a typical namespace, the matrix is:
| Resource | Read (get, list, watch) | Write (create, update, patch, delete) | Notes |
|---|---|---|---|
pods | OK | DANGEROUS | update swaps the image; delete kills the Pod |
pods/log | OK | — | Read-only subresource |
pods/exec | — | DANGEROUS | Requires pods/exec + verb create |
services | OK | OK | create/delete are common for operators |
configmaps | OK | OK | Often read-only at runtime; written at deploy |
secrets | OK | DANGEROUS | Read is dangerous if the Secret holds credentials |
deployments | OK | OK | update/patch triggers a rollout |
statefulsets | OK | OK | Same as Deployments |
daemonsets | OK | OK | Same as Deployments |
jobs | OK | OK | Operators create Jobs |
cronjobs | OK | OK | Operators create CronJobs |
events | OK | — | Read-only; usually granted to monitoring |
nodes | OK | DANGEROUS | Cluster-scoped; update allows node cordoning |
rbac.rolebindings | — | ESCALATION | Privilege escalation primitive |
rbac.clusterrolebindings | — | CRITICAL | cluster-admin if bound to the right role |
Read-only roles
A read-only role for a workload is:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: readonly
namespace: prod
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "endpoints"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "daemonsets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources: ["jobs", "cronjobs"]
verbs: ["get", "list", "watch"]
This role can read every workload-related object in the namespace but cannot write. The verbs are explicit; no wildcards.
# Verify the role
kubectl auth can-i --list --as=system:serviceaccount:prod:readonly-sa -n prod
Operator roles
An operator that creates Deployments and Services needs both read and write:
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get", "list", "watch"]
This operator can manage Deployments, Services, and
ConfigMaps in the namespace but cannot touch Secrets,
RBAC objects, or other namespaces. The
customresourcedefinitions read is for watching the
CRD’s status field — not for modifying the CRD
itself.
Privilege escalation
The escalate verb on RBAC objects is the
privilege-escalation primitive. The cluster’s built-in
protection:
- A user cannot create a
RoleBindingthat grants permissions they do not already have. The API server checks at admission time. - The
escalateverb allows a user to create aRoleBindingto aRolethey do not have access to. This is restricted to specific subjects.
# Can alice create a RoleBinding for cluster-admin?
kubectl auth can-i create clusterrolebindings \
--as=alice
# no
# Can alice escalate to a Role she doesn't have?
kubectl auth can-i escalate role \
--as=alice
# no
Subresources
Some resources have subresources that require separate permissions:
| Subresource | Verb needed | What it allows |
|---|---|---|
pods/log | get | Read container logs |
pods/exec | create | Execute a command in a container |
pods/portforward | create | Port-forward to a container |
pods/eviction | create | Evict a Pod (drain) |
pods/proxy | * | Proxy to a Pod’s HTTP endpoints |
deployments/scale | update | Scale a Deployment |
nodes/proxy | * | Proxy to a node’s HTTP endpoints |
A role that grants pods with get cannot get pods/log; the subresource must be added explicitly.
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
Production failure modes
- Granting
*verbs on Pods — every write verb on Pods. The caller can swap images, kill pods, and exec into containers. Almost never the right pattern. - Granting
*resources — every kind. Includes Secrets, RBAC objects, and CRDs. The caller can read every Secret and create RoleBindings. - Granting
updateon Deployments to a runtime SA — the SA can roll the workload. Operators should never have this; humans and CI/CD should, but carefully. - Granting
deletecollectionon a workload kind — the caller can delete every Pod, every ConfigMap, every Service in one call. Useful for cleanup, dangerous in steady-state. - Granting
createon ClusterRoleBindings — the caller can bind themselves tocluster-admin. Always restricted; never granted to a SA.
Cross-course references
- The Observability course covers the audit log that records every RBAC decision.
- The Linux course covers the file permissions on Secret data — Kubernetes RBAC does not enforce these; secrets stored as files are protected by filesystem ACLs only.
Quiz
Knowledge check · 4 questions
Q1. Which subresource and verb combination allows a ServiceAccount to exec into a Pod?
Q2. Any user can create a ClusterRoleBinding that grants `cluster-admin`, because RBAC objects are just API resources.
Q3. Your CI pipeline runs `kubectl set image deployment/api api=myapp:v1.1` to roll a Deployment. The CI SA has a Role with `apps/deployments: [get, list, watch, update]`. The roll succeeds. Six months later, an attacker compromises the CI SA and runs `kubectl set image deployment/api api=attacker/myapp:latest`. The image is pulled. What happened?
The role grants `update` on Deployments to the CI SA. The CI SA's token was leaked in a CI runner log. The attacker had network reach to the API server and the right token. The cluster has an admission policy that requires image signatures; the `attacker/myapp:latest` image is unsigned and should be rejected — but the policy was disabled two weeks ago for testing.
Q4. Name three Kubernetes subresources and the verb each one requires.
Passing score: 75%. Answers are checked in this browser.
Production discipline
A defensible Role grants the smallest set of verbs on
the smallest set of resources at the smallest scope.
Read verbs (get, list, watch) are the foundation;
write verbs (create, update, patch, delete,
deletecollection) are added one at a time with
explicit justification. The dangerous combinations
(update on Pods, create on ClusterRoleBindings,
* on RBAC objects) are restricted to the minimum
number of subjects. The subresources (pods/exec,
pods/log, deployments/scale) are explicit; the
parent resource does not imply the subresource. The
audit log is the proof: every verb × resource
combination should be explainable. A cluster whose
roles are not explainable has an RBAC programme that
is not defensible.