TT Lab
Get started
Learn Learning paths Courses

KCSA — Kubernetes Security Associate

Why do permissions remain after recreating an account?

Continue in TT Lab

Goal

Separate audience, authentication, and authorization with actual bearer tokens, and compare the behavior of the old token and the name-based binding when you recreate an SA under the same name.

Why it matters

Revoking permissions and revoking credentials are not the same thing. If you judge the response complete by looking only at the account name, existing permissions can apply again to the new identity. You confirm using TokenRequest and TokenReview rather than an administrator proxy request, and an actual HTTPS GET that verifies the CA. Use only the synthetic ConfigMap inside the personal k3s VM. Do not bring a production kubeconfig or real secrets. Preparing the environment may take several minutes. This is a 55-minute lab, and if you need more, extend it before it expires. The VM, files, and tokens are reclaimed when the session ends. Download only the observations you need in advance, and do not download or share the original token text.

Prepared environment and tools

You write only the input JSON yourself. The helper keeps the token issuance results with 0600 permissions in /opt/fixtures/kcsa-identity-tokens and does not print the original text. These files are materials internal to the personal lab, not answers to edit or paste into a report. The requested lifetime of a manually issued token is 3 hours, and you confirm the actual expiry from the server response. The internal baselines /opt/fixtures/kcsa-identity-context.json and /opt/fixtures/kcsa-identity-lab-context.json are managed by the helper. Do not delete or edit them. act performs only the specified changes. capture checks the observation conditions and then saves JSON to /root/kcsa-identity. observe prints only the current observation, and grade does not change resources or student files. TokenReview is a token validation API and does not create a stored object.

Steps

  1. Save /root/kcsa-identity/baseline.json with capture 1. Investigate the UIDs of the reader SA, the identity-client Pod, the named-config-reader Role and RoleBinding, and the lesson-policy ConfigMap in kcsa-identity. Automatic token mounting for the Pod and SA is false, and you must keep the normal access of the other team's kcsa-identity-other.
  2. Write an authentication.k8s.io/v1 TokenRequest in /root/kcsa-identity/old-request.json. spec.audiences is ["labhub-kcsa-api"], expirationSeconds is 10800, and boundObjectRef is apiVersion=v1, kind=Pod, name=identity-client and the actual Pod UID observed in step 1. Issue it with act 2 and record the hash, claims, and verified identity in bound-token.json with capture 2. Do not print the original token text.
  3. Save /root/kcsa-identity/scoped-request.json with capture 3. Check with the original bearer token that looking up your own lesson-policy is 200 and looking up the other team's is 403. Do not substitute an administrator certificate or an --as request for this evidence.
  4. In /root/kcsa-identity/report-request.json, write the same TokenRequest as step 2 but change audiences to ["labhub-kcsa-report"]. Save audience.json with act 4 and capture 4. This token's API GET must be 401, the default TokenReview false, and the TokenReview that explicitly names the report audience true.
  5. Write an rbac.authorization.k8s.io/v1 Role in /root/kcsa-identity/deny-role.json. The name is named-config-reader, the namespace is kcsa-identity, and rules is an empty array. Save rbac-revoked.json with act 5 and capture 5. The binding and SA UID are kept, the original token's authentication must be true, and the lookup must be 403.
  6. In /root/kcsa-identity/restore-role.json, write the same Role but put in rules a single rule with apiGroups=[""], resources=["configmaps"], resourceNames=["lesson-policy"], verbs=["get"]. Restore the Role with act 6 and delete and recreate the original SA under the same name with a UID precondition. Save uid-revoked.json with capture 6. The existing Pod and binding UIDs are kept, the SA UID changes, and the old token must be authentication false and 401 before it expires.
  7. In /root/kcsa-identity/new-request.json, write a TokenRequest with the same API audience, lifetime, and original Pod UID as step 2. Save new-identity.json with act 7 and capture 7. The new token's sub is the same but the SA UID is different, the lookup through the existing binding is 200, and the old token must remain 401.
  8. With act 8, remove only the investigated original RoleBinding, using UID and resourceVersion preconditions. Save /root/kcsa-identity/closed.json with capture 8. Together confirm new token authentication true and lookup 403, old token 401, and the other team's identity and ConfigMap UID kept with a normal lookup 200.

Notes

Token decoding is not signature validation, and an administrator --as request is not a check of that bearer token's audience or expiry. Files and configuration from steps already completed are preserved. Do not delete past records and then recreate the same success records from the current state. After moving on to later steps, past steps are verified with the token lifetime at the time they were saved, and you also separately check the actual state of the current step. Before observing a new step, preserve the records and input files of the immediately preceding step. The other team's SA, Role, binding, and ConfigMap and your own Pod, Role, and ConfigMap UIDs are preserved to the end. Step preparation creates only the previous inputs and observations that do not yet exist, and does not overwrite the current answer or an unfinished input. If you hit the limit the helper waits for, investigate observe and the state on the same VM. Do not solve it by repeating the installation or widening permissions. Deleting an SA can also affect other tokens. Do not use this experiment as an unconditional response order in production. Read 401 and 403 in the context of this authenticated request. It does not mean that all anonymous requests are 401. The observation records are learning material, not a remote attestation that controls root or an anti-cheating device. Official documentation: ServiceAccount tokens · Name- and namespace-based RBAC.

Investigate the baseline identities and security settings

Save /root/kcsa-identity/baseline.json with capture 1. Investigate the UIDs of the reader SA, the identity-client Pod, the named-config-reader Role and RoleBinding, and the lesson-policy ConfigMap in kcsa-identity. Automatic token mounting for the Pod and SA is false, and you must keep the normal access of the other team's kcsa-identity-other.

Look at metadata.uid in addition to names. Check the Pod's automatic mounting setting and the SA setting separately.

Issue a token bound to the actual Pod UID

Write an authentication.k8s.io/v1 TokenRequest in /root/kcsa-identity/old-request.json. spec.audiences is ["labhub-kcsa-api"], expirationSeconds is 10800, and boundObjectRef is apiVersion=v1, kind=Pod, name=identity-client and the actual Pod UID observed in step 1. Issue it with act 2 and record the hash, claims, and verified identity in bound-token.json with capture 2. Do not print the original token text.

Do not guess the TokenRequest's bound UID; read it from snapshot.pod.metadata.uid in baseline.json. The spec does not need the token's original text.

The namespace boundary of the actual request

Save /root/kcsa-identity/scoped-request.json with capture 3. Check with the original bearer token that looking up your own lesson-policy is 200 and looking up the other team's is 403. Do not substitute an administrator certificate or an --as request for this evidence.

Authentication true does not mean that request was allowed. Check the resource and namespace of the actual GET.

Compare a token for a different audience

In /root/kcsa-identity/report-request.json, write the same TokenRequest as step 2 but change audiences to ["labhub-kcsa-report"]. Save audience.json with act 4 and capture 4. This token's API GET must be 401, the default TokenReview false, and the TokenReview that explicitly names the report audience true.

Even if lifetime remains, the audience may differ. Instead of deploying a report server, compare validations that name the audience explicitly.

Revoke permissions and keep authentication

Write an rbac.authorization.k8s.io/v1 Role in /root/kcsa-identity/deny-role.json. The name is named-config-reader, the namespace is kcsa-identity, and rules is an empty array. Save rbac-revoked.json with act 5 and capture 5. The binding and SA UID are kept, the original token's authentication must be true, and the lookup must be 403.

Do not delete the Role; only empty its rules. Look at the fact that the account and binding UIDs are the same together with the 403.

A new service account with the same name

In /root/kcsa-identity/restore-role.json, write the same Role but put in rules a single rule with apiGroups=[""], resources=["configmaps"], resourceNames=["lesson-policy"], verbs=["get"]. Restore the Role with act 6 and delete and recreate the original SA under the same name with a UID precondition. Save uid-revoked.json with capture 6. The existing Pod and binding UIDs are kept, the SA UID changes, and the old token must be authentication false and 401 before it expires.

Do not restore the Role with unlimited permissions. Compare the UIDs of the new account with the same name and the original account.

Connect the new identity to the existing binding

In /root/kcsa-identity/new-request.json, write a TokenRequest with the same API audience, lifetime, and original Pod UID as step 2. Save new-identity.json with act 7 and capture 7. The new token's sub is the same but the SA UID is different, the lookup through the existing binding is 200, and the old token must remain 401.

A binding's SA subject is a name and a namespace. Check the success of the new token and the invalidation of the old token at the same time.

Exact permission revocation and impact check

With act 8, remove only the investigated original RoleBinding, using UID and resourceVersion preconditions. Save /root/kcsa-identity/closed.json with capture 8. Together confirm new token authentication true and lookup 403, old token 401, and the other team's identity and ConfigMap UID kept with a normal lookup 200.

Distinguish authentication false from authentication true with authorization failure. The other team's normal request is the comparison baseline for the scope of revocation.