Hedronite · Cert-Prep Lesson · CNCF / CKA · Track K8s Day 13 · Tue 2026-08-04 · Trio #79

CKA — RBAC, ServiceAccounts, and Certificate-Based Auth — the domain's other half

The exam does not ask you to explain RBAC. It gives you a terminal and six minutes.

Lesson Class: Cert-Prep (CKA, CNCF)
Blueprint Domain: Cluster Architecture, Installation & Configuration — 25% (access-control half)
Track / Day: K8s — round-robin day 13, fifth K8s visit · k8s_day_counter 4 (even → CKA-emphasis)
Word Count: ~2,300
Grounding: Poulton, The Kubernetes Book — ch. 13 (pp. 176-186) · Burns et al., K8s: Up & Running 3rd ed — ch. 14 (p. 247) · Sovereign-Bootcamp CKS-PREP-2025 q24 user-csr-rbac
Paired Ops: Kubernetes Identity and Authorization on GKE
Paired Dev: Python and the Kubernetes Authorization Surface
Discipline: ROD v3 · aether-accent meta-card · interactive q-card drill · dual-corpus tome_refs · bundle shape
CKA Domain: Cluster Architecture 25% RBAC · ServiceAccounts · CSR · kubeconfig Performance-based · 2 hrs CKA-before-CKS sit order
Authentication proves who. Authorization decides what. Admission enforces how. Every RBAC task lives in the middle one, and every failure feels like the first.

§ IWhere This Sits on the Blueprint

Cluster Architecture, Installation & Configuration is the CKA's largest domain at 25%, and it holds two different bodies of work. One is machinery: control-plane components, etcd backup and restore, kubeadm install and upgrade, high availability. The 07-23 lesson took that half.

The other is access control, on the curriculum as manage role based access control (RBAC). That single line is worth several questions on a scored attempt, and every one of them is answered by typing rather than by reasoning.

Today's Ops lesson carried the concepts at depth. This lesson assumes them and drills the exam surface: four objects cold, imperative creation, the verification commands the grader effectively runs, kubeconfig context surgery, and the CSR flow that mints a human credential. Poulton's chapter summary is the frame to carry into the terminal (ch. 13, p. 186).

§ IIThe Four Objects, Cold

Enumerates permissionsAttaches to subjects
NamespacedRoleRoleBinding
Cluster-wideClusterRoleClusterRoleBinding

Three combinations are valid and one is not. Role plus RoleBinding is the everyday grant. ClusterRole plus ClusterRoleBinding is the cluster-wide grant. ClusterRole plus RoleBinding is the reuse case: the ClusterRole defines the permission set once, the RoleBinding scopes it into one namespace. A Role can never be bound by a ClusterRoleBinding, because its rules mean nothing outside its own namespace.

Commit that asymmetry. Questions phrase it as grant the built-in view permissions to user dev in namespace apps only, and the answer is ClusterRole view plus a namespaced RoleBinding. Reaching for a ClusterRoleBinding grants view cluster-wide and fails the check.

Subject kinds

A binding names subjects with three kinds: User, Group, ServiceAccount. Users and Groups are strings the authenticator produced, with no object behind them (Poulton, p. 177). ServiceAccounts are real objects and require a namespace field in the subject entry, which is the single most common YAML mistake in this domain.

The built-in gradient

view reads most things but not Secrets. edit writes workloads but cannot touch RBAC objects. admin adds RBAC management within a namespace. cluster-admin is * on * in *. If a question asks for read-only across a namespace, the answer is view, not a hand-written Role.

§ IIIImperative Creation, Because the Clock Is Real

Nobody passes this domain writing YAML by hand. The imperative forms are the answer, and --dry-run=client -o yaml is how you get an editable manifest when the question wants a field the flags do not expose.

kubectl create role ledger-writer \
  --verb=get,list,watch --resource=configmaps -n trading

kubectl create rolebinding ledger-writer-binding \
  --role=ledger-writer \
  --serviceaccount=trading:ledger-writer -n trading

kubectl create clusterrole node-reader \
  --verb=get,list,watch --resource=nodes

kubectl create clusterrolebinding dev-view \
  --clusterrole=view --user=dev

kubectl create rolebinding dev-view-apps \
  --clusterrole=view --user=dev -n apps

Read the flag grammar closely, because it is where marks are lost. --role names a Role and --clusterrole names a ClusterRole; the last two commands differ only in which flag and which scope. Subject flags are --user, --group, --serviceaccount, and the ServiceAccount form takes namespace:name in one string.

kubectl create serviceaccount ledger-writer -n trading

kubectl create role patcher \
  --verb=get,patch --resource=deployments \
  --resource-name=ledger -n trading

--resource-name is the narrowing flag. A question saying allow patching only the deployment named ledger is testing whether you know it exists.

§ IVVerification — the Commands That Decide the Mark

The grader checks behavior, not YAML. Check the same way.

kubectl auth can-i list secrets -n trading \
  --as system:serviceaccount:trading:ledger-writer

kubectl auth can-i '*' '*' --as dev

kubectl auth can-i create deployments -n apps --as dev --list

The ServiceAccount username format is exact: system:serviceaccount:<namespace>:<name>. Every ServiceAccount also joins the group system:serviceaccounts:<namespace> and the cluster-wide system:serviceaccounts. A binding to either group hits every ServiceAccount in scope, which is how an over-grant question is usually built.

Impersonation needs permission. --as requires the impersonate verb on users, groups, or serviceaccounts. In the exam you are cluster-admin so it works. In a real cluster, impersonate on groups is effectively a path to whatever those groups hold.

Check the negative cases. A grant-read-only question tests both that get returns yes and that create returns no. A cluster-admin binding satisfies the first and fails the task.

kubectl describe rolebinding ledger-writer-binding -n trading

roleRef is immutable after creation. A wrong roleRef is deleted and recreated, never patched. Attempting the patch wastes a minute you do not have.

§ VWorked Scenario — Minting a Human Identity with a CSR

This question separates candidates and runs across both CKA and CKS. The Sovereign-Bootcamp corpus carries it as a scored lab at CKS-PREP-2025/questions/24-user-csr-rbac with its own LabSetUp.bash, Questions.bash, and Verify.bash. Below is the same flow at exam pace.

Task: create a credential for user sasha who may read pods in namespace trading and nothing else. Deliver a kubeconfig context that works.

Kubernetes has no User object, so the identity comes from a certificate the cluster CA signed. The Common Name becomes the username; the Organization becomes the group.

openssl genrsa -out sasha.key 2048
openssl req -new -key sasha.key -out sasha.csr \
  -subj "/CN=sasha/O=traders"
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: sasha
spec:
  request: <base64 of sasha.csr, no wrapping>
  signerName: kubernetes.io/kube-apiserver-client
  expirationSeconds: 86400
  usages:
    - client auth

signerName must be kubernetes.io/kube-apiserver-client for a human client credential. The other signers exist for kubelet serving and kubelet client certificates; choosing one produces a certificate the API server will not accept for user authentication. Base64-encode the CSR with no line wraps, which is what base64 -w 0 buys.

kubectl apply -f sasha-csr.yaml
kubectl certificate approve sasha
kubectl get csr sasha -o jsonpath='{.status.certificate}' | base64 -d > sasha.crt

The certificate proves who. It grants nothing. Authorize separately:

kubectl create role pod-reader \
  --verb=get,list,watch --resource=pods -n trading

kubectl create rolebinding sasha-pod-reader \
  --role=pod-reader --user=sasha -n trading
kubectl config set-credentials sasha \
  --client-key=sasha.key --client-certificate=sasha.crt --embed-certs=true

kubectl config set-context sasha-trading \
  --cluster=kubernetes --user=sasha --namespace=trading

kubectl config use-context sasha-trading
kubectl get pods
kubectl get secrets

The last two lines are the verification. Pods list. Secrets return Forbidden. That pair answers the task as asked. --embed-certs=true is the flag worth remembering: without it the kubeconfig stores file paths, and the config stops working the moment it leaves the directory those files sit in.

§ VIFive Traps, Named

The empty apiGroup. Core resources (pods, services, configmaps, secrets, serviceaccounts) live in apiGroups: [""]. Deployments live in apps. RBAC objects live in rbac.authorization.k8s.io. Writing apiGroups: ["v1"] produces a Role granting nothing, with no error reported.

Missing namespace on a ServiceAccount subject. In a RoleBinding, a subject of kind ServiceAccount requires namespace. Omit it and the binding matches nothing.

get versus list. get needs the object name; list enumerates and returns contents. Granting list on Secrets where get was asked for is a silent over-grant a good question checks with a negative case.

Immutable roleRef. Delete and recreate. Do not patch.

No deny rules. RBAC is additive only. A question saying remove dev's ability to delete pods asks you to find and delete a binding, never to add a restricting one.

§ VIIPractice Drill

Tap a card to reveal its resolution. Score cold first.

Q1 · Scoping
User dev must have the built-in read-only permissions in namespace apps and in no other namespace. Which two objects, and what is the trap?
Resolution
ClusterRole view plus a namespaced RoleBinding in apps. The trap is reaching for a ClusterRoleBinding, which grants view cluster-wide and fails the scoping requirement. kubectl create rolebinding dev-view --clusterrole=view --user=dev -n apps.
Q2 · Imperative form
Write the command granting ServiceAccount ci in namespace build the verbs get, list, create on pods, using an existing Role named ci-runner.
Resolution
kubectl create rolebinding ci-runner-binding --role=ci-runner --serviceaccount=build:ci -n build. Note --role rather than --clusterrole, and the namespace:name form of the ServiceAccount flag.
Q3 · Debug
A pod using ServiceAccount agent in trading gets 403 on list secrets. can-i --as system:serviceaccount:trading:agent returns no. The RoleBinding points at Role agent-reader, which grants ["get","list"] on ["secrets"] in apiGroups: ["v1"]. Name the bug.
Resolution
The apiGroups value. Secrets are in the core group, written "", not "v1". The Role parses, applies to nothing, and the authorizer correctly answers no. Change to apiGroups: [""].
Q4 · Immutability
You created a RoleBinding with the wrong roleRef. What is the fix?
Resolution
Delete the RoleBinding and create it again. roleRef is immutable, so a patch is rejected.
Q5 · CSR flow
Which signerName mints a client credential for a human user, and what does the certificate's Organization field become?
Resolution
kubernetes.io/kube-apiserver-client. The Organization (O=) becomes the Kubernetes group; the Common Name (CN=) becomes the username.
Q6 · Blast radius
A ClusterRoleBinding attaches ClusterRole edit to group system:serviceaccounts:staging. What is the blast radius?
Resolution
Every ServiceAccount in namespace staging, present and future, holds edit across the entire cluster. edit cannot modify RBAC objects, so it is not a direct escalation path, but it can write workloads in every namespace. Fix by deleting the binding, not by adding a narrower one.

§ VIIIThe Trio Interlock

Ops carried the request path and the GKE cloud-identity bridge, which is where RBAC stops and IAM starts. Dev built the preflight: SelfSubjectAccessReview in Python, checked at controller startup so a missing verb crashes the deploy rather than a reconcile. This lesson is the same material at exam pace, by hand, against a clock.

Coverage note for the next CKA day: Workloads & Scheduling (15%) and Storage (10%) are the two blueprint domains still untouched by this arc.

Set a six-minute timer. Do the CSR scenario in §V start to finish without scrolling back. The minute you lose is always the same minute, and finding out which one is the point of the drill.

🫡 ⚖️ 📜
Leo.Syri — Praetor Consulate, Imperium Luminaura
Filed 2026-08-04 Fajr · Cert-Prep lesson · CKA-emphasis (k8s_day_counter 4, even) · K8s deep-mastery track day 13