TT Lab
Get started
Learn Learning paths Courses

KCSA — Kubernetes Security Associate

The Auditor Arrives, and There's No Proof It's Switched On

Continue in TT Lab

Goal

Directly verify several security control items (blocking anonymous access, enforcing Pod Security, disabling automatic service account token mounting, and resource quotas) in the cluster, and leave the results as a per-control-ID pass/fail report and a gap list.

Why it matters

Frameworks such as the CIS Kubernetes Benchmark, the NSA/CISA hardening guide, and SOC 2 and ISO 27001 are in the end lists of questions asking "is this control turned on?" What becomes a problem in an audit is not so much a missing control as a control recorded as on but actually off. So every line of the report must come not from a configuration file or memory but from the answer the cluster returns right now (auth can-i, object fields, and labels).

Also, you must not stop after looking only at what you hardened. Finding the places left outside control, like the default namespace that nobody touched, and recording them as gaps is half of a compliance inspection.

Steps

  1. Create the namespace kcsa-comp to be inspected.
  2. Ask whether system:anonymous can list secrets and record it in anon.txt.
  3. Attach the label pod-security.kubernetes.io/enforce=restricted to kcsa-comp.
  4. Create a Pod hardened that meets all the restricted requirements.
  5. Turn off automatic token mounting for the default service account.
  6. Set the namespace resource limits with the ResourceQuota quota.
  7. Write the actual results of the four control items as the report compliance.csv.
  8. Find the namespaces that have no enforce label and record them as gaps in gaps.txt.

Notes

Set up the namespace to audit

Create the namespace kcsa-comp that will be the target of the compliance inspection.

An audit starts with setting the scope. If you generate the namespace create with --dry-run=client and apply it, it is safe to run several times.

Can someone who has not logged in see secrets

Ask the cluster directly whether an unauthenticated user (system:anonymous) can list secrets in the namespace kcsa-comp, and write that answer to /root/kcsa-comp/anon.txt as one line anonymous-list-secrets=<yes|no>.

First try kubectl auth can-i with --as=system:anonymous. You get a refusal instead of an answer — can-i creates a SelfSubjectAccessReview as that user itself, and an anonymous user cannot even do that. So create a SubjectAccessReview (authorization.k8s.io/v1), with which an administrator asks on its behalf, fill in spec.user, groups (system:unauthenticated), and resourceAttributes, and read status.allowed. If false, it is no.

Put a restricted gatekeeper on the namespace

Attach the Pod Security Admission label pod-security.kubernetes.io/enforce=restricted to the namespace kcsa-comp (along with the warn and audit labels if you like).

Pod Security Standards are turned on with a single namespace label. The label key is pod-security.kubernetes.io/<모드> (the placeholder is the mode) and the value is one of privileged, baseline, and restricted. If you add --overwrite to kubectl label, it is safe to run again.

Admit a Pod that passes the restricted standard

Create a Pod hardened (image nginx:1.27-alpine) in the namespace kcsa-comp. Declare runAsNonRoot: true and seccompProfile.type: RuntimeDefault at the Pod level, and allowPrivilegeEscalation: false, capabilities.drop: [ALL], and runAsNonRoot: true at the container level, to meet all the requirements of the restricted profile.

The restricted profile requires not running as root, blocking privilege escalation, dropping all capabilities, and specifying a seccomp profile. securityContext exists in two places, the Pod level (spec.securityContext) and the container level (containers[].securityContext), and the places where each field may go differ. If even one requirement is missing, creation may be rejected because of the label from step 3.

A token you never use is plugged into every Pod

Set automountServiceAccountToken: false on the default service account of the namespace kcsa-comp, so that API tokens are not automatically mounted into Pods that did not separately request one.

When a namespace is created, a controller creates the default service account for you. If it is not there yet, wait a moment and check. This field is a top-level field of the service account object, so you can change it with patch.

So that one team cannot eat the whole cluster

Create a ResourceQuota quota in the namespace kcsa-comp and set pods=10, requests.cpu=2, and requests.memory=2Gi as the limits.

A ResourceQuota is an object that limits the total for the whole namespace, and you write the resource names and limits under spec.hard. With kubectl create quota, you can join several items with commas using --hard.

The control item report to hand to the auditor

Create /root/kcsa-comp/compliance.csv. The first line is the header control,status, and below it write the four control items anonymous-access, psa-enforce, sa-token-automount, and resource-quota, one per line, with the status holding the result you actually confirmed (pass or fail). All four items must be in the pass state.

The values in a report are evidence, not declarations. The grader rechecks the corresponding control in the cluster for each line and fails you if the written status differs from the actual state. Think about which control item each result you saw in steps 2, 3, 5, and 6 corresponds to, and check once more before writing.

Who is guarding the default namespace

A namespace without a PSA enforce label is a compliance gap. Check whether the cluster's default namespace has an enforce label, and write the name of the namespace with the gap to /root/kcsa-comp/gaps.txt as one line psa-missing-namespace=<네임스페이스> (the placeholder is the namespace). (Do not fix it by attaching the label — this step is for recording a finding.)

To see the labels of all namespaces at once, use the option that adds a label column to kubectl get ns. If you put the namespace you just hardened next to the namespace nobody touched and compare, the gap shows.