TT Lab
Get started
Learn Learning paths Courses

CKS — Kubernetes Security Specialist

Revoking permission is not the same as closing a connection

Continue in TT Lab

In one line

Revoking permissions is a control that blocks new API requests, while ending commands that are already running is a response you must confirm separately.

Why this was needed

An operator temporarily opened terminal permissions. When the work was done, they removed the exec permission from the Role, and even confirmed that new terminals no longer open. At that point, can the existing window no longer run commands either? Even for terminals that look the same on screen, a request that creates a new connection and input sent over an already open connection are different paths. If you declare the response complete too soon, you can miss a session that is still running.

On LabHub's dedicated k3s v1.36.4 probe, even after a new exec was rejected, the existing stream responded to new input created after that. This doesn't mean earlier output remained on the screen. By matching different random nonces and ACKs that were created and sent after the permission was revoked, the actual execution was observed. This is the result for the version and path that were checked, and it doesn't guarantee the behavior of every proxy or session manager.

How it works

A Kubernetes RBAC Role allows requests at namespace scope. Permissions are additive, so even if you narrow one Role, a request can still be allowed if another binding grants the same permission. So you must not just look at the YAML but also check whether actual requests succeed or are rejected.

Reading a Pod and executing in a container are also different permissions. The Pod is pods, and execution is specified by the subresource pods/exec. Unlike ordinary resource creation, a subresource like exec whose target name is in the URL can be limited to a specific Pod with resourceNames. In this lab, GET on target and control is allowed, and exec is allowed only on target. If you use a wildcard, the control group is opened up too, so you can't reproduce the problem narrowly. Specifying both get and create for exec is to account for differences in the client's connection method, and does not mean that every ordinary GET executes a process.

A terminal, after establishing a connection, exchanges stdin, stdout, and stderr for a long time. The official streaming documentation describes a structure that upgrades an HTTP connection and uses it as a bidirectional channel. So the rejection of a new request and the termination of an existing channel are not treated as the same event. auth can-i is a tool for asking about permissions; it is neither a command that cuts an existing connection nor evidence that stands in for an actual exec success.

The --as in this lab is impersonation, which acts on behalf of a service account identity for a request logged in with the admin certificate. The API server authenticates the original requester, checks the impersonation permission, and then authorizes as the impersonated identity. You can verify RBAC this way, but it doesn't verify the expiry or revocation of a service account bearer token. That topic is covered in KCSA's real token lab.

What it looks like in the field

In incident response, containment and preservation conflict. If you delete the Pod first, you may lose traces of execution and temporary files, and if you wait while only investigating, dangerous execution may continue. You must first decide what evidence to keep where and what business impact to accept. The lab's containment.json is a small response plan that records the target UID, the control UID to preserve, the pre-action evidence hash, and the scope of action. This hash is a means of detecting that a file changed because of an incident; it is not a digital signature that defends even against a case where a root user maliciously regenerates all the data.

Deleting by name alone is also dangerous. This is because between deletion and recreation, a different Pod could come in with the same name. A UID precondition ties the target you investigated to the target you actually change. The lab gives a graceful termination period and observes both the Pod's absence and the termination of the existing exec process. It doesn't conclude that the actual process on the node has ended just because the API object was deleted successfully.

In production response, the owning Deployment may recreate the Pod, or GitOps may put the permissions back. This lab uses only a standalone Pod inside a healthy single VM, so it doesn't simulate that automatic recovery. In a real service, you must also fix the controller and Git configuration that hold the desired state. The reason for keeping a control group is to distinguish whether the request blocking is because of a failure of the whole service, and to check that the scope of action hasn't widened.

Even after recovery is finished, you don't reopen all the permissions as they were. You confirm the new target's UID, Ready, and GET success, and check that exec is still rejected. Service recovery and re-granting temporary admin permissions are different approvals. In the response report, you write the new-connection rejection, the additional response from the existing connection, and the termination and recovery results separately, so that one green light doesn't stand in for the rest.

What you will do in the next lab

You investigate the scope, write a narrow Role, and then open a real exec stream. You revoke the permission and compare the rejection of a new request with the new response on the existing connection. After preserving the evidence, you terminate only the designated UID, and also check the permission boundaries of the control group and the new Pod. The helper runs only a harmless ACK program. The check files read persistent observations, so even if you regrade after the last step, past successes are not lost. If an intermediate run is cut off uncertainly, it doesn't restart automatically but guides you to download the data and reproduce it in a new session.

Official documentation