KCSA — Kubernetes Security Associate
If You Can Create Roles, Can You Become Admin?
Goal
Try RBAC privilege escalation with a low-privilege service account, and observe that the apiserver's escalation prevention (escalate and bind) actually blocks the attempt. Then confirm that only a legitimate administrator can hand over permissions and that impersonation is also a separately protected permission, and leave it as a report.
Why it matters
The most dangerous mistake in RBAC is when granting "permission to create roles" becomes effectively the same as granting "permission to become anything." If, with only role creation permission, one could create a role containing Secret permissions for oneself and attach it, that service account would be a cluster administrator.
Kubernetes blocks this at the apiserver level — you cannot create or bind a role containing permissions you do not currently hold (escalation prevention). So granting role creation permission does not lead straight to a privilege expansion. The point of this lab is to see with your own eyes that this boundary lives not in code but in the apiserver's admission step, and why a verb like impersonate is treated separately as a dangerous verb.
Steps
- Create the namespace
kcsa-privand the service accountlowpriv. - Bind to lowpriv the Role
rolemaker, which grants only permission to create roles and rolebindings. - As the administrator, create the target ClusterRole
secret-admin(secrets get/list). - Pretending to be lowpriv, try to create a role with secret permissions, and confirm and save that it is denied.
- Pretending to be lowpriv, try to bind secret-admin to itself, and confirm and save that it is denied.
- As the administrator, formally bind secret-admin to lowpriv so that it can now read secrets.
- Confirm that the service account
auditor2, which has no impersonation permission, cannot pretend to be someone else. - Record in
report.txtwhat was blocked and what got through.
Notes
- Use
kubectl auth can-i <동사> <리소스> --as=<주체>(the placeholders are the verb, the resource, and the subject) to check another subject's permissions from administrator privileges. - A self-escalation attempt is supposed to fail, so capture standard error in the file too by adding
2>&1after the command. - The rejection message "is attempting to grant RBAC permissions not currently held" is the evidence of escalation prevention.
- Official documentation: RBAC authorization · RBAC good practices.
Prepare a low-privilege subject
Create the namespace kcsa-priv and, inside it, the service account lowpriv. Do not grant any permissions yet.
A service account is a subject inside a namespace. If you generate the create with --dry-run=client and apply it, it is safe to run several times. At this step, do not bind any role to lowpriv.
Hand it only the permission to create roles
Create the Role rolemaker in the namespace kcsa-priv — apiGroups rbac.authorization.k8s.io, resources roles,rolebindings, verbs create,get,list. And bind this Role to the service account lowpriv with a RoleBinding (rolemaker-binding). lowpriv should be able to create roles and rolebindings but have no secret permissions.
RBAC is divided into roles (bundles of permissions) and rolebindings (connecting them to subjects). Use --verb, --resource, and --serviceaccount with kubectl create role and create rolebinding. Remember that after this step lowpriv gains permission to create roles but still has no permission on secrets — that is the key to the following steps.
The tempting target — permission to read secrets
As the administrator, create the ClusterRole secret-admin — get and list permission on secrets in the core group. It is the target that lowpriv will try to get hold of in later steps.
A ClusterRole is a bundle of permissions not tied to a namespace. Use --verb and --resource with kubectl create clusterrole. For this step, just create it with the default kubeconfig (cluster-admin).
Blocked when trying to give itself a permission it does not hold
Now, pretending to be lowpriv (--as=system:serviceaccount:kcsa-priv:lowpriv), try to create a Role sneaky containing get permission on secrets in the namespace kcsa-priv. lowpriv has no secret permissions, so the apiserver must reject this creation. Save the rejection message as it is to /root/kcsa-priv/escalate-denied.txt.
RBAC has an escalation prevention rule — you cannot create a role containing permissions you do not hold. Even if you have permission to create roles (step 2), this rule applies separately. The command failing is normal, so capture standard error in the file too (2>&1). You should see 'forbidden' and 'not currently held' in the message.
Blocked when trying to bind the target permission to itself
Pretending to be lowpriv (--as=system:serviceaccount:kcsa-priv:lowpriv), try to create a RoleBinding grab in the namespace kcsa-priv that binds the ClusterRole secret-admin to itself (lowpriv). This too hands over a permission lowpriv does not hold, so it must be rejected. Save the rejection message to /root/kcsa-priv/bind-denied.txt.
lowpriv can create rolebindings (step 2). But to bind a role containing permissions it does not hold itself, it must already hold those permissions or have bind permission on the target role. It has neither, so the apiserver blocks it. Failure is normal, so capture the error too with 2>&1.
The administrator formally hands over the permission
This time, with administrator permission (the default kubeconfig), create a RoleBinding granted in the namespace kcsa-priv that binds the ClusterRole secret-admin to the service account lowpriv. Now lowpriv must be able to get secrets in kcsa-priv.
The boundary was not lowpriv's "ability to create rolebindings" but the apiserver's escalation check. If an administrator who already holds that permission creates the binding, it passes. Create the binding with --clusterrole and --serviceaccount, and check that auth can-i get secrets --as=<lowpriv> changes to yes.
Pretending to be someone else is also separately guarded
Create the service account auditor2 in the namespace kcsa-priv (do not give it impersonation permission). Check whether auditor2 can pretend to be another user (impersonate users), and see that a subject without permission cannot impersonate.
Impersonation (--as) is itself a separate RBAC permission (the impersonate verb on the users, groups, and serviceaccounts resources). For a service account you did not give the permission to, auth can-i impersonate users --as=<그 SA> (the placeholder is that SA) must be no. Contrast this with the fact that the administrator (the default kubeconfig) is yes.
A ledger of what was blocked and what got through
Write the observed results to /root/kcsa-priv/report.txt in exactly four lines — self-escalate-role=blocked, self-bind-clusterrole=blocked, admin-grant=allowed, impersonate-without-verb=blocked. The values must match the actual cluster behavior.
The grader rechecks these four lines in the cluster — whether a new self-escalation attempt with lowpriv fails, whether lowpriv can get secrets after the administrator binding, and whether auditor2 cannot impersonate. Do not write guesses; carry over the results you saw in the earlier steps as they are.