KubernetesIII · Kubernetes APIKubernetes API
Authorisation — RBAC, the Node authoriser, and webhook delegation
What you'll learn
- Distinguish Role, ClusterRole, RoleBinding, ClusterRoleBinding and when each applies
- Trace a request through the authoriser chain and identify which authoriser allowed or denied it
- Identify common RBAC mistakes that lead to over-permissioned access
- Use `kubectl auth can-i` and audit logs to validate permissions
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
Once the API server has authenticated a request, it must decide whether the identity is allowed to perform the action. Authorisation is that decision. In production, RBAC is the default; this lesson covers how RBAC works, how it composes with the Node authoriser and webhook authorisers, and the operational patterns that arise from RBAC mistakes.
The authoriser chain
The API server runs authorisers in order; the first to explicitly allow or deny wins. If no authoriser allows, the request is denied by default.
flowchart LR
R[Request] --> N[Node<br/>kubelet only]
N -->|deny| RB[RBAC<br/>default]
RB -->|deny| W[Webhook<br/>OPA/Cerbos]
W -->|deny| AB[ABAC<br/>legacy]
AB -->|deny| AA[AlwaysDeny<br/>default]
The chain is configured by --authorization-mode. Production:
--authorization-mode=Node,RBAC
With this mode, kubelet requests are handled by the Node authoriser (fast, special-case), and everything else goes through RBAC. Webhook is added when an external policy engine makes decisions.
Role and ClusterRole
A Role grants permissions within a single namespace; a ClusterRole grants permissions cluster-wide.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: team-a-prod
name: developer
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "update", "patch"]
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-reader
rules:
- apiGroups: [""]
resources: ["nodes", "namespaces"]
verbs: ["get", "list", "watch"]
The rules field is the same for both. The difference is the
binding scope (RoleBinding vs ClusterRoleBinding).
A rule’s fields:
apiGroups— the API group (""for core, e.g.,apps,batch)resources— the resource kind (pods,services,deployments,secrets, …)verbs— the action (get,list,watch,create,update,patch,delete,deletecollection)resourceNames(optional) — limit to specific objectsnonResourceURLs(ClusterRole only) — for/healthz,/metrics, etc.
RoleBinding and ClusterRoleBinding
A RoleBinding grants a Role (or ClusterRole) to identities within a single namespace; a ClusterRoleBinding grants a ClusterRole cluster-wide.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: team-a-prod
name: developers
subjects:
- kind: User
name: alice@example.com
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io
Subjects:
- User — an authenticated identity (typically an OIDC user)
- Group — a collection of users (typically an OIDC group)
- ServiceAccount — a workload identity
# ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: monitoring
subjects:
- kind: ServiceAccount
name: prometheus
namespace: monitoring
roleRef:
kind: ClusterRole
name: cluster-reader
apiGroup: rbac.authorization.k8s.io
A RoleBinding can reference a ClusterRole — but only the
permissions of that ClusterRole that apply within the
binding’s namespace. This is how view, edit, admin are
implemented (they are ClusterRoles; bound per namespace).
Aggregated ClusterRoles
Some ClusterRoles have aggregationRule:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.authorization.k8s.io/aggregate-to-monitoring: "true"
rules: [] # aggregated from matching ClusterRoles
Other ClusterRoles label themselves with
rbac.authorization.k8s.io/aggregate-to-monitoring: "true",
and the monitoring ClusterRole’s rules become the union.
This is how view, edit, admin work in newer clusters:
they are aggregates of per-resource ClusterRoles.
The Node authoriser
The Node authoriser is a special-case authoriser that runs
first (when --authorization-mode=Node,RBAC). It allows
kubelets to perform a narrow set of operations on their
own node only:
- Read Pods assigned to their node
- Write Pod status for Pods on their node
- Read Node status
- Write Node status (including lease renewal)
A kubelet cannot read Pods on other nodes or write to its own Node object’s metadata. The Node authoriser’s permission is implicit; it does not require an RBAC binding.
Webhook authoriser
Delegates authorisation to an external service. The webhook
returns allowed: true or false based on its own policy
(OPA, Cerbos, custom logic).
kind: AuthenticationConfiguration # similar structure for auth-config
# For webhook authoriser:
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthorizationConfiguration
authorizers:
- name: Webhook
webhook:
authorizedTTL: 5m
unauthorizedTTL: 30s
kubeConfigFile: /etc/kubernetes/authz-webhook-kubeconfig
# ...
Webhook authoriser is used for:
- Centralised policy (OPA/Gatekeeper, Cerbos)
- Time-bound access (e.g., “developers can delete Pods only during business hours”)
- Compliance-mandated controls
A failing webhook (timeout, connection error) is treated as
deny unless --authorization-webhook-failure-policy=Allow
(rarely safe).
How to test permissions
# Substitute your own values before running:
VERB=delete
RESOURCE=pods
NS=team-a-prod
AS_USER=alice@example.com
AS_GROUP=developers
kubectl auth can-i "$VERB" "$RESOURCE" -n "$NS"
kubectl auth can-i "$VERB" "$RESOURCE" --all-namespaces
kubectl auth can-i "$VERB" "$RESOURCE" --as="$AS_USER" --as-group="$AS_GROUP"
The --as and --as-group flags impersonate an identity.
Useful for testing:
# What can the developer group do in team-a-prod?
kubectl auth can-i delete pods -n team-a-prod \
--as=alice@example.com --as-group=developers
# What would a developer in a different group be able to do?
kubectl auth can-i delete pods -n team-a-prod \
--as=mallory@example.com --as-group=contractors
kubectl auth whoami shows the identity of the current
kubeconfig.
Common RBAC mistakes
Mistake 1: Cluster-admin by default
kind: ClusterRoleBinding
metadata:
name: everyone-is-admin
subjects:
- kind: Group
name: system:authenticated
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
This binds cluster-admin to every authenticated identity.
A compromised ServiceAccount or a leaked kubeconfig becomes
full cluster compromise. Never do this in production.
Mistake 2: cluster-admin in the bootstrapping kubeconfig
kubeadm produces an admin.conf with cluster-admin. This is
necessary for initial setup but should be replaced with a
narrower kubeconfig for normal operations.
Mistake 3: ServiceAccount with broad permissions
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
---
kind: ClusterRoleBinding
metadata:
name: my-app-cluster-admin
subjects:
- kind: ServiceAccount
name: my-app
namespace: my-app-prod
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
A Pod running with this SA can do anything. Production ServiceAccounts get only the permissions they need: read ConfigMaps in their namespace, write metrics, etc.
Mistake 4: verbs that allow mutations when reads were intended
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
create, update, patch are mutations. A “read Secrets”
role needs get, list, watch only. Including mutations
is a privilege escalation vector.
Mistake 5: wildcard apiGroups or resources
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
Equivalent to cluster-admin. Avoid.
How to audit RBAC
Production clusters audit RBAC regularly:
# List all ClusterRoleBindings to system:masters
kubectl get clusterrolebindings -o json | \
jq -r '.items[] | select(.roleRef.name == "cluster-admin") | .subjects[]'
# List every ServiceAccount with cluster-wide permissions
kubectl get clusterrolebindings -o json | \
jq -r '.items[] | select(.subjects[]?.kind == "ServiceAccount") |
select(.roleRef.name == "cluster-admin") |
.metadata.name + ": " + (.subjects[]?.namespace + "/" + .subjects[]?.name)'
Audit logs are the source of truth for “who did what”:
journalctl -u kube-apiserver | grep -i "forbidden\|denied" | tail -20
The audit log entry records the verb, the resource, the identity, the decision (allow/deny), and the reason.
How to remove access
To revoke RBAC permissions, delete the binding:
kubectl delete rolebinding developers -n team-a-prod
A user with no bindings cannot do anything in the cluster (via RBAC). They may still authenticate, but every action is denied.
For compromised ServiceAccount tokens, the immediate response is to:
- Delete the ServiceAccount token Secret
- Delete the ServiceAccount’s SessionList (if OIDC)
- Delete the Pods that use the compromised token
Projected tokens are short-lived; deleting the Secret that the Pod mounts invalidates it within seconds.
Cross-course references
- The Linux course part
XXVII-Linux-Authcovers central identity (LDAP, Kerberos) which often backs OIDC groups used in RBAC. - The Linux course part
XXIX-Linux-Hardeningcovers least-privilege principles that map directly onto RBAC. - The Observability course part
IX-Observability-Exporterscovers the metrics the API server exposes about RBAC decision latency. - The Docker course part
XXXVIII-Docker-Secretscovers secret management patterns that interact with RBAC (secretsresource permissions).
Quiz
Knowledge check · 4 questions
Q1. A RoleBinding in namespace `team-a-prod` references a ClusterRole. What does the binding grant?
Q2. Binding `cluster-admin` to the `system:authenticated` group is a safe default for production clusters because it ensures every user can manage their own workloads.
Q3. A developer runs `kubectl delete pod web-7c8 -n team-a-prod` and gets `Error from server (Forbidden): pods "web-7c8" is forbidden: User "alice@example.com" cannot delete resource "pods" in API group "" in the namespace "team-a-prod"`. The developer is in the `developers` group, which has a RoleBinding in team-a-prod granting `get, list, watch` on pods. Diagnose and remediate.
RoleBinding: ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: developers namespace: team-a-prod subjects: - kind: Group name: developers apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: view apiGroup: rbac.authorization.k8s.io ``` The `view` ClusterRole grants `get, list, watch` on most resources — including pods. It does not grant `delete`. `kubectl auth can-i delete pods -n team-a-prod --as=alice@example.com --as-group=developers` returns `no`.
Q4. Explain least-privilege RBAC for production ServiceAccounts. What permissions should a typical application ServiceAccount have, and what should it never have?
Passing score: 75%. Answers are checked in this browser.
Production discipline
- Bind specific permissions to specific identities. Never
grant
cluster-adminbroadly. - Test permissions with
kubectl auth can-i --as=...before granting. - Use the principle of least privilege: each ServiceAccount gets exactly the permissions its code needs.
- Audit RBAC regularly: list every ClusterRoleBinding to
system:masters, every ServiceAccount with broad permissions, every wildcard verb. - Treat the
viewClusterRole as the default for human developers; grant additional verbs only when justified.