KCSA — Kubernetes Security Associate
A Cluster-Wide RBAC Audit
Goal
Actually audit the RBAC of the entire cluster. Find ClusterRoles that have wildcard permissions and dangerous verbs,
measure a service account's effective permissions with kubectl auth can-i, create a new least-privilege Role,
prove that its boundary really works, and then write an audit report containing the evidence.
Why it matters
RBAC audits are hard because reading the manifests alone does not give the answer.
Several bindings attach to one subject, permissions are inherited through groups, and built-in ClusterRoles are sometimes
merged through aggregation. Do not compute it in your head; ask the apiserver.
kubectl auth can-i --list --as= is that tool.
There is a reason to count wildcards separately. resources: ["*"] allows not only the resources that exist now but also
resources that will be added later as CRDs. This is why the rebuttal "it's safe for now" does not hold.
escalate, bind, and impersonate are especially dangerous meta permissions. These verbs are "permissions to grant permissions,"
so with just one of them you can make yourself an administrator. They are the first thing to look for in an RBAC audit.
Finally, step 6. Least privilege does not mean "we gave what is needed" but "what is not needed does not work." If you stop after confirming only that reading works, you have done half the job. Only when you also confirm that deleting does not work and that it does not work in other namespaces have you proven the boundary.
Steps
- Create the directory
/root/kcsa-auditand the namespacekcsa-audit, and in that namespace create two ServiceAccounts,auditorandprobe. At this step, attach no permissions to either. - Among all ClusterRoles in the cluster, find those where within a single rule both
verbsandresourcesare*, and save only the names, one per line, to/root/kcsa-audit/wildcard-clusterroles.txt. - Run
kubectl auth can-i --listwith--as=system:serviceaccount:kcsa-audit:probeand-n kcsa-audit, and save the entire output as it is, unprocessed, to/root/kcsa-audit/probe-can-i.txt.probeis a comparison baseline to which no role is bound until the end of this lab, so you must not attach permissions to it even later. - Among all ClusterRoles in the cluster, find those whose
verbscontain even one, literally, ofescalate,bind, orimpersonate, and save only the names, one per line, to/root/kcsa-audit/dangerous-verbs.txt. - Ask three questions as
system:serviceaccount:kcsa-audit:defaultand save the results to/root/kcsa-audit/default-sa.txtas three lines in키=값format (the placeholders are the key and the value) —create-pods=(create Pods in the namespacekcsa-audit),get-secrets=(get secrets in the namespacekcsa-audit), andlist-nodes=(list nodes cluster-wide). Write the value asyesornoas it is. - In the namespace
kcsa-audit, create the Roleconfigmap-reader— it has exactly one rule, with apiGroups being just the core group, resources justconfigmaps, and verbs just the three ofget,list, andwatch. And bind it to the SAauditorwith the RoleBindingauditor-configmap-reader. After creating it, useauth can-ito check (a) that configmaps list works inkcsa-audit, (b) that delete does not work in the same place, and (c) that not even list works in thedefaultnamespace. - Write
/root/kcsa-audit/rbac-audit-report.md. The following four lines must appear exactly as section headings —## 와일드카드 권한,## 위험 동사,## 기본 서비스어카운트,## 최소 권한 적용(the Korean headings mean "Wildcard permissions", "Dangerous verbs", "Default service account", and "Applying least privilege"). The body must mention the following five words —cluster-admin,escalate,impersonate,configmap-reader,auditor. And include one line in the formwildcard-count=<숫자>(the placeholder is the number) with the number of wildcard ClusterRoles you counted in step 2.
Notes
- It is convenient to extract with the form
kubectl get clusterroles -o json | jq -r '.items[] | ... | .metadata.name'. Combineselectandanyto sweep the rules array. - Some rules have no
verbsorresourcesat all (such as non-resource URL rules), so it is safer to include default handling such as// []. - The core API group has an empty string as its name (
""). - Common mistake 1: including in step 2 even those where only
verbsis*. Both conditions must be satisfied at the same time within the same rule. - Common mistake 2: processing the output with grep or sorting before saving it in step 3. Grading reruns the same command and compares, so you must save it as it is.
- Common mistake 3: targeting
probefor the rolebinding in step 6.probeis the baseline, so if permissions are attached, step 3 fails again. Attach the role toauditor.
Prepare the audit workspace
Create the directory /root/kcsa-audit and the namespace kcsa-audit, and in that namespace create two ServiceAccounts, auditor and probe. At this step, attach no permissions to either.
Create the artifact directory, the namespace, and two service accounts. One is the subject that will later receive the least-privilege role, and the other is a comparison baseline that receives no permissions to the end. Without a baseline, you cannot say "permissions increased."
Find ClusterRoles with wildcard permissions
Among all ClusterRoles in the cluster, find those where within a single rule both verbs and resources are *, and save only the names, one per line, to /root/kcsa-audit/wildcard-clusterroles.txt.
Look for cases where both verbs and resources are * within a single rule. Rules are an array and a ClusterRole can have several, so if any one satisfies the condition, put that role on the list. Combining jq's select and any does it in one line.
Take the baseline of a service account with no permissions
Run kubectl auth can-i --list with --as=system:serviceaccount:kcsa-audit:probe and -n kcsa-audit, and save the entire output as it is, unprocessed, to /root/kcsa-audit/probe-can-i.txt. probe is a comparison baseline to which no role is bound until the end of this lab, so you must not attach permissions to it even later.
auth can-i has an option that prints the full list instead of individual questions. Specify the subject with --as, and to see namespace-scoped permissions too, you must also give -n. Do not process the output with grep or sorting; save it as it is. The few lines that appear here are the baseline of an "account with no permissions," and you must never attach any role to this account.
Detect ClusterRoles that hold dangerous verbs
Among all ClusterRoles in the cluster, find those whose verbs contain even one, literally, of escalate, bind, or impersonate, and save only the names, one per line, to /root/kcsa-audit/dangerous-verbs.txt.
If you recall what each of the three verbs makes possible, you understand why they are looked at together. Here, only those with the verb written literally are counted — wildcards were already tallied separately in an earlier step.
What can the default service account do
Ask three questions as system:serviceaccount:kcsa-audit:default and save the results to /root/kcsa-audit/default-sa.txt as three lines in 키=값 format (the placeholders are the key and the value) — create-pods= (create Pods in the namespace kcsa-audit), get-secrets= (get secrets in the namespace kcsa-audit), and list-nodes= (list nodes cluster-wide). Write the value as yes or no as it is.
Every namespace automatically has a default service account, and a Pod uses it if it does not specify an SA. Ask directly what this account can do and carry over the answers as they are. When asking about a cluster-scoped resource, you do not need the namespace option.
Create a least-privilege Role and prove its boundary
In the namespace kcsa-audit, create the Role configmap-reader — it has exactly one rule, with apiGroups being just the core group, resources just configmaps, and verbs just the three of get, list, and watch. And bind it to the SA auditor with the RoleBinding auditor-configmap-reader. After creating it, use auth can-i to check (a) that configmaps list works in kcsa-audit, (b) that delete does not work in the same place, and (c) that not even list works in the default namespace.
Put only what is needed in a single rule. The core API group has an empty string as its name. After creating it, you can call it least privilege only if you check not just that what you want works but also that what you do not want does not work — especially that it does not work in other namespaces.
Gather it into an audit report
Write /root/kcsa-audit/rbac-audit-report.md. The following four lines must appear exactly as section headings — ## 와일드카드 권한, ## 위험 동사, ## 기본 서비스어카운트, ## 최소 권한 적용 (the Korean headings mean "Wildcard permissions", "Dangerous verbs", "Default service account", and "Applying least privilege"). The body must mention the following five words — cluster-admin, escalate, impersonate, configmap-reader, auditor. And include one line in the form wildcard-count=<숫자> (the placeholder is the number) with the number of wildcard ClusterRoles you counted in step 2.
Make one document based on the artifacts of the earlier steps. The section headings must be exactly in the indicated form, and you need one line that writes the count from step 2 as a number. That number is recomputed and compared at grading time.