CKS — Kubernetes Security Specialist
Permission revoked, terminal still alive
Goal
You verify separately the blocking of new exec permission and the termination of an existing execution connection, and preserve evidence.
Why it matters
If you confirm only that new connections don't open, you can miss execution that remains. Conversely, if you unconditionally delete the Pod, you can lose investigation data and business operations. You observe that difference with a harmless ACK program on a dedicated VM, terminate only the designated UID, and then check the permission boundaries of the control group and the recovered Pod.
This is a 65-minute lab. The session is 60 minutes by default, so extend it with the +time button before it expires. The maximum is 180 minutes, and when the session ends, files and running connections disappear. Download the evidence you need before it ends.
Steps
- Read the baseline with inspect and record the four fields namespace, target_uid, control_uid, and identity_method in /root/cks-exec/scope.json. The namespace is cks-terminal, and the rest are the actual values for the current lab. Save the investigation scope with act 1.
- In /root/cks-exec/allow-role.json, write apiVersion=rbac.authorization.k8s.io/v1, kind=Role, metadata.name=terminal, and metadata.namespace=cks-terminal. The first rule is apiGroups=[""], resources=["pods"], resourceNames=["target","control"], verbs=["get"], and the second is resources=["pods/exec"], resourceNames=["target"], verbs=["get","create"], with apiGroups=[""]. Apply it with act 2 and confirm the actual exec rejection on control.
- With act 3, open a real exec connection to target and observe the ACK baseline for new input. nonce, reply, and at are saved in facts.stream of /root/cks-exec/evidence/03.json. Keep this connection until later steps and don't replace it with a new connection.
- In /root/cks-exec/revoke-role.json, write the same Role but remove only the pods/exec rule and keep the target and control GET rule. Revoke the permission with act 4 and confirm that new_exec in evidence/04.json is exactly a pods/exec permission denial.
- With act 5, send new input to the existing connection. The response in evidence/05.json must have a different nonce from step 3 and its time must be after the step 4 save. Compare it with the GET success for the same target UID and explain that it is not a failure of the whole API.
- In /root/cks-exec/containment.json, record namespace=cks-terminal, target=target, the actual target_uid, preserve_control_uid, evidence_sha256, action=delete-owned-pod, and force=false. The hash is the SHA-256 of evidence/05.json serialized as key-sorted, whitespace-free UTF-8 JSON (ensure_ascii=False). Preserve the pre-termination plan and evidence with act 6.
- With act 7, gracefully terminate only the original target UID verified in the plan. In evidence/07.json, confirm old_pod_absent=true, stream_rc as an actual integer exit code, control_ready=true with the original control_uid kept, and force=false. Do not delete any other Pod or namespace.
- In /root/cks-exec/report.json, record new_exec=denied, existing_exec=responded-after-revocation, the actual old_target_uid and control_uid, containment=owned-pod-terminated, authentication_tested=impersonation-not-token-revocation, and evidence_sha256. The hash is the SHA-256 of the step 7 evidence computed the same way. With act 8, recover target with a new UID and confirm Ready, GET success, and that the exec rejection is maintained.
Notes
All operations apply only to cks-terminal inside the VM. The helper usage is
python3 /opt/fixtures/cks_exec_lab.py inspect, act 1 through act 8, and grade 1 through grade 8.
act performs the actual work and observation, and grade only reads files. For an act you have already completed, your answer
is preserved and it isn't run again. Partial input isn't automatically overwritten either, so fix it yourself.
For a step interrupted midway with an uncertain result, preserve the data and reproduce it in a new session.
The identity in this lab is SA impersonation with an admin certificate. It is not an experiment in actual SA token revocation. Do not use privileged mode, host networking, or additional capabilities. Do not run this termination procedure as is on a production service; first review the owning controller, node state, evidence preservation, and business impact.
Investigate the target and control group identities
Read the baseline with inspect and record the four fields namespace, target_uid, control_uid, and identity_method in /root/cks-exec/scope.json. The namespace is cks-terminal, and the rest are the actual values for the current lab. Save the investigation scope with act 1.
A name can be reused, but a UID is this resource's identity. This is not a task of finding a token value.
Grant terminal permission only to the target Pod
In /root/cks-exec/allow-role.json, write apiVersion=rbac.authorization.k8s.io/v1, kind=Role, metadata.name=terminal, and metadata.namespace=cks-terminal. The first rule is apiGroups=[""], resources=["pods"], resourceNames=["target","control"], verbs=["get"], and the second is resources=["pods/exec"], resourceNames=["target"], verbs=["get","create"], with apiGroups=[""]. Apply it with act 2 and confirm the actual exec rejection on control.
Split pods and pods/exec into different rules and specify resourceNames. The control Pod must be read-only.
The baseline of a real bidirectional connection
With act 3, open a real exec connection to target and observe the ACK baseline for new input. nonce, reply, and at are saved in facts.stream of /root/cks-exec/evidence/03.json. Keep this connection until later steps and don't replace it with a new connection.
Only when the nonce and ACK match is it a real response to new input. Don't look only at old output left on the screen.
Revoke permission for new exec requests
In /root/cks-exec/revoke-role.json, write the same Role but remove only the pods/exec rule and keep the target and control GET rule. Revoke the permission with act 4 and confirm that new_exec in evidence/04.json is exactly a pods/exec permission denial.
If you empty the whole Role, Pod GET is blocked too and it is hard to tell the cause of the failure. Remove only the execution subresource.
The remaining connection and new input
With act 5, send new input to the existing connection. The response in evidence/05.json must have a different nonce from step 3 and its time must be after the step 4 save. Compare it with the GET success for the same target UID and explain that it is not a failure of the whole API.
The result of a permission question and the lifetime of an already open process are different. Look at the nonce created after the revocation and the response time.
Preserve the evidence and scope of impact before termination
In /root/cks-exec/containment.json, record namespace=cks-terminal, target=target, the actual target_uid, preserve_control_uid, evidence_sha256, action=delete-owned-pod, and force=false. The hash is the SHA-256 of evidence/05.json serialized as key-sorted, whitespace-free UTF-8 JSON (ensure_ascii=False). Preserve the pre-termination plan and evidence with act 6.
Even with the same content, if whitespace or key order differs, the raw hash is different. Compute the canonical JSON hash as required.
Terminate only the designated UID and check the control group
With act 7, gracefully terminate only the original target UID verified in the plan. In evidence/07.json, confirm old_pod_absent=true, stream_rc as an actual integer exit code, control_ready=true with the original control_uid kept, and force=false. Do not delete any other Pod or namespace.
A successful delete API call, the object's absence, and the connection's termination are different kinds of evidence. You must also keep the control group UID and Ready.
Recover the new Pod and write the response report
In /root/cks-exec/report.json, record new_exec=denied, existing_exec=responded-after-revocation, the actual old_target_uid and control_uid, containment=owned-pod-terminated, authentication_tested=impersonation-not-token-revocation, and evidence_sha256. The hash is the SHA-256 of the step 7 evidence computed the same way. With act 8, recover target with a new UID and confirm Ready, GET success, and that the exec rejection is maintained.
Recovering the service of the new Pod is not restoring temporary admin permissions. exec must remain rejected.