CKS projected tokens — ServiceAccounts and EKS IRSA least privilege
Opt-in short-lived tokens for the API. Narrow RBAC. On EKS, the same SA name appears in the IAM trust sub.
<!-- hal:authoritative:yaml -->
CKS Cluster Hardening asks you to stop handing every Pod a lasting API token. On EKS, the same ServiceAccount is also the handle IRSA uses for AWS. Harden both sides of one identity.
§I — Exam frame
CKS blueprint weight for Cluster Hardening is about 15%. Kubestronaut's Cluster Hardening note opens Service Accounts with three moves: disable default automount, minimize permissions, issue tokens deliberately. Bootcamp question 03 drills automount false plus an explicit token Secret mount. Bootcamp question 30 drills the modern projected serviceAccountToken volume with expirationSeconds and audience.
Kubernetes Up and Running, Chapter 14, Service Account Management, is the conceptual spine: the ServiceAccount is how the Pod authenticates to the API server. From 1.24 onward, long-lived Secret tokens are no longer the default mount story; projected bound tokens are.
Today's Ops lesson adds the cloud medium: the same ServiceAccount name appears in an EKS IRSA trust policy sub. CKS does not grade AWS console clicks, but the least-privilege instinct transfers. A projected short-lived token for Kubernetes plus a tightly trusted IRSA role for AWS is one identity story with two enforcement planes.
08-25 already spent anonymous auth and NodeRestriction. 08-31 already spent AppArmor and seccomp. Do not redo those. Stay on ServiceAccount tokens and IAM binding discipline.
§II — What the exam expects you to change
**Disable automount on default.**
kubectl patch serviceaccount default -n default \
-p '{"automountServiceAccountToken": false}'
Repeat per namespace you harden. Pods that need API access must opt in.
Prefer projected bound tokens over lingering Secret tokens.
Bootcamp question 30's target shape:
apiVersion: v1
kind: Pod
metadata:
name: token-demo
spec:
serviceAccountName: default
automountServiceAccountToken: false
containers:
- name: nginx
image: nginx
volumeMounts:
- name: token-vol
mountPath: /var/run/secrets/tokens
readOnly: true
volumes:
- name: token-vol
projected:
sources:
- serviceAccountToken:
path: token.jwt
expirationSeconds: 600
audience: https://kubernetes.default.svc.cluster.local
Key claims for the exam:
automountServiceAccountToken: falseremoves the silent default mount.serviceAccountTokenprojection mint a bound, time-limited token.- kubelet rotates before expiry when
expirationSecondsis set. audiencebecomes the OIDCaudclaim; wrong audience fails TokenReview for that relying party.
Question 03's older Secret-typed token path still appears in labs. Know both. Prefer projected tokens in greenfield answers unless the prompt demands kubernetes.io/service-account-token Secrets.
Minimize RBAC on the ServiceAccount.
Kubestronaut: create a dedicated SA, bind a narrow Role. Do not pile IRSA annotations onto default. Do not grant cluster-admin to a workload SA because an AWS role is already narrow. Two planes, two least-privilege documents.
§III — Practice questions (tap-to-reveal in HTML)
Q1. Automount default
A namespace wants no Pod in payments to receive an API token unless the PodSpec opts in. Which change is necessary on the namespace default ServiceAccount?
A. Delete the default ServiceAccount B. Set automountServiceAccountToken: false on ServiceAccount default C. Set allowPrivilegeEscalation: false on every Pod D. Remove the system:authenticated group
<details><summary>Reveal</summary>
B. Kubestronaut and Bootcamp Q03/Q30 both start by disabling automount on default. Deleting default breaks namespace assumptions. C and D are different controls.
</details>
Q2. Projected token fields
Which projected source fields bound a short-lived token for the Kubernetes API audience in the Bootcamp Q30 style?
A. path, expirationSeconds, audience under serviceAccountToken B. secretName only C. hostPath to /var/run/secrets D. configMap with key token
<details><summary>Reveal</summary>
A. Projected serviceAccountToken carries path, expiration, and audience. Secret name is the Q03 legacy path. hostPath and configMap are wrong for bound SA tokens.
</details>
Q3. IRSA trust subject
An EKS IRSA role trust policy should allow which sub for ServiceAccount invoice-reader in namespace billing?
A. billing:invoice-reader B. system:serviceaccount:billing:invoice-reader C. arn:aws:eks:us-east-1:111122223333:sa/billing/invoice-reader D. *
<details><summary>Reveal</summary>
B. IRSA trust matches the Kubernetes username form system:serviceaccount:{namespace}:{name}. Wildcards expand blast radius. A is missing the prefix. C invents an ARN form.
</details>
Q4. Two planes
A Pod has a correct IRSA annotation and assumes an IAM role that can read one S3 prefix. The same Pod's ServiceAccount is bound to a ClusterRole with resources: ["*"], verbs: ["*"]. What is still wrong for CKS posture?
A. Nothing; IRSA replaces RBAC B. Kubernetes API authorization remains overly broad C. S3 prefix policies are illegal on EKS D. Projected tokens cannot coexist with IRSA
<details><summary>Reveal</summary>
B. IRSA scopes AWS calls. RBAC still scopes Kubernetes API calls. CKS wants both tight. A and D are false. C is false.
</details>
Q5. Audience mismatch
You set projected audience: payments-api but the API server TokenReview for in-cluster clients expects the Kubernetes default audience. What fails first?
A. IAM AssumeRoleWithWebIdentity only B. Use of that token against the Kubernetes API as a normal SA credential C. Container image pulls D. CoreDNS
<details><summary>Reveal</summary>
B. Audience binds the token to a relying party. A token minted for payments-api is not automatically valid as a generic apiserver SA credential. IRSA uses its own audience (sts.amazonaws.com commonly) via the injection path Ops described. Image pulls and DNS are unrelated.
</details>
§IV — Lab sequence (Bootcamp-backed)
- Run Bootcamp
questions/03-serviceaccount-tokensetup. Disable automount. Decide whether the prompt wants Secret mount or whether you will answer with projected tokens if the grader accepts either. - Run
questions/30-projected-sa-token. Match pathtoken.jwt,expirationSeconds: 600, and the stated audience exactly. - On an EKS study cluster (outside the exam VM), annotate a dedicated SA with
eks.amazonaws.com/role-arn, set trustsub, and proveaws sts get-caller-identityshows the role. Then breaksuband observe STS denial. That failure mode will not appear as AWS UI on CKS, but it trains the same "identity string must match" reflex Kubestronaut wants for SA naming and RBAC subjects.
§IV.B — Answer hygiene on the exam VM
When a prompt says "disable automatic mounting," patch the ServiceAccount and also set automountServiceAccountToken: false on the Pod if you recreate it. Graders sometimes check both objects.
When a prompt gives exact mount path and filename, copy them. token vs token.jwt is a common miss on question-30 style tasks.
When creating a dedicated SA for least privilege:
kubectl -n default create serviceaccount netpol-reader
kubectl -n default create role netpol-reader --verb=get,list --resource=networkpolicies
kubectl -n default create rolebinding netpol-reader --role=netpol-reader --serviceaccount=default:netpol-reader
Then point the Pod at serviceAccountName: netpol-reader. Kubestronaut's "Minimize Permissions" section is exactly this loop.
Do not annotate default with an IRSA role in production study clusters used for CKS drills. Keep exam-style SA hygiene and cloud IAM demos on separate ServiceAccounts so your muscle memory stays clean.
§IV.C — Mapping CKS domains to the IRSA story
| CKS domain | Control in this lesson | EKS companion |
|---|---|---|
| Cluster Hardening | automount, projected tokens, RBAC on SA | IRSA trust sub matches SA |
| System Hardening | (covered 08-31) | node role stays minimal |
| Minimize Microservice Vulns | no token in image env | SDK uses injected files/env from webhook |
| Supply Chain | (covered 08-07) | digest-pin images that call AWS |
| Monitoring / Runtime | audit SA token use | CloudTrail on AssumeRoleWithWebIdentity |
The table is study glue, not an exam legend. Use it to remember that "ServiceAccount" is the join key across Kubernetes and AWS.
§V — Tie to recent CKS arc
08-25: anonymous auth and NodeRestriction shrink who can speak to the apiserver from the network and from nodes. Today's work shrinks what a admitted Pod carries once it is inside.
08-31: LSM and seccomp shrink syscalls and profiles on the container. Today's work shrinks credentials mounted into the container filesystem.
08-04 CKA RBAC lesson: subjects and RoleBindings. CKS reuses that vocabulary and adds token issuance posture.
§VI — Close
CKS Cluster Hardening for ServiceAccounts is opt-in tokens, short binding, narrow RBAC. On EKS, add IRSA so AWS calls do not inherit the node instance role. One ServiceAccount name should appear in three places that agree: PodSpec, RoleBinding subjects, and (off-exam but on-job) the IAM trust sub.
Examine automount, projection, and RBAC before you chase application 403s.
Related
- EKS IRSA Ops
- CKS anonymous auth and NodeRestriction
- Cross-References/Archmagus-Stack — Cert-Prep CNCF hub
- Grounding: Kubestronaut CKS Cluster Hardening SA sections; KUR Ch.14; Bootcamp CKS-PREP Q03 + Q30