Hedronite · Cert-Prep Lesson · CKA + CKS (CNCF) · Wed 2026-06-17

Kubernetes Runtime Security and the SecurityContext — CKA Pod Configuration Meets CKS seccomp, AppArmor, and Capability Dropping

One pod-spec stanza. The CKA writes it correctly; the CKS hardens it against an attacker.

Lesson Class: Cert-Prep Synthesis
Cert Track: CKA + CKS (CNCF / Linux Foundation)
Day: Wed — Kubernetes through a security lens
Word Count: ~2,580
Paired Ops: Runtime Confinement — Capability Drop, Syscall Filter, Read-Only Root (β-Trust)
Paired Dev: Confining the Node.js Runtime — Permission Model + Branded Types (JS + TS)
Grounding: Poulton K8s Book Ch15 · Burns K8s Up & Running Ch14 · Docker Deep Dive
Discipline: ROD v3 (universal-application)

§ IFrame

The Wednesday CNCF lessons have followed the same arc as the Ops Wednesdays. Admission control decided which pods the cluster would run. Image signing proved where the running bytes came from. Network policy decided which pods could talk. Each gate sits at a different moment in the workload's life, and the network policy from last week was the last one that governed the pod from outside. Once a permitted pod is running, the cluster has one more place to confine it: the securityContext, the block of the pod spec that tells the kernel what the container's processes may do.

This lesson works that block from both exam angles. The CKA treats the securityContext as configuration: the fields that set the user a container runs as, drop its Linux capabilities, and freeze its filesystem. The CKS treats it as defense: seccomp profiles that filter syscalls, AppArmor profiles that confine file and capability access, and the Pod Security Standards that enforce the whole set across a namespace. The shared substance is one pod-spec stanza; the CKA writes it correctly and the CKS hardens it against an attacker. The same securityContext that today's Ops lesson described in kernel terms is the YAML both exams ask you to write.

§ IIDomain Foundations

A container runs as a user, and by default, too often, that user is root. A process running as root inside a container that escapes its namespace (through a kernel vulnerability or a misconfigured mount) is root on the host. The first and cheapest confinement is to run the container as a non-root user, which both exams test through runAsNonRoot, runAsUser, and runAsGroup. Burns and his coauthors put this first in their securing-pods chapter: most of the privilege a container holds it never needed, and running as an unprivileged user removes the largest single piece of it.

Below the user sits the capability set. Poulton's threat-modeling chapter lays out the model the Ops lesson named: Linux breaks root's power into roughly forty discrete capabilities, a runtime grants a default subset to every container, and the default is chosen for compatibility rather than for any workload's need. The securityContext.capabilities field adds and drops from that default. The disciplined form drops ALL and adds back only the named few, and Docker's capability documentation, which Poulton summarizes, lists exactly which capabilities a container starts with so the operator can reason about what dropping ALL removes.

Two more mechanisms sit alongside capabilities. Seccomp filters the syscall interface, narrowing which of the kernel's three-hundred-odd calls the container may make. AppArmor, where the host runs it, confines a process by a named profile that restricts file access, capability use, and network operations through a Linux Security Module. The pod spec references each by field — seccompProfile in the securityContext, and an AppArmor profile through securityContext.appArmorProfile on modern Kubernetes. Capabilities, seccomp, and AppArmor are the kernel-level confinements the Ops lesson described, expressed as the fields the exam asks you to fill in.

§ IIICKA Flavor: SecurityContext as Configuration

The CKA exam is hands-on and graded on whether the cluster ends in the state the task described. Runtime-security tasks at the CKA tier are configuration tasks: a question hands you a pod that runs as root with default capabilities and asks you to reconfigure it to run as a named non-root user, drop its capabilities, and mount its root filesystem read-only. The work is entirely in the securityContext, and the exam tests whether you place the fields at the right level, because the context exists at two scopes.

A pod-level securityContext under spec.securityContext applies to every container and carries pod-wide fields: runAsNonRoot, runAsUser, fsGroup. A container-level securityContext under spec.containers[].securityContext applies to one container and carries process-scoped fields: capabilities, readOnlyRootFilesystem, allowPrivilegeEscalation. The exam's common trap is to set a container-scoped field at the pod level, where it is silently ignored — the manifest applies cleanly and the grader still marks the task failed because the capability drop never took effect. Knowing which field lives at which scope is the difference between a passing and a failing manifest.

The CKA also tests allowPrivilegeEscalation: false, which is easy to overlook. A process can sometimes gain more privileges than its parent through a setuid binary or a file capability, even when the container dropped capabilities at start. Setting the field tells the kernel the process may never gain privileges beyond what it launched with. It is one line, it belongs in every hardened pod spec, and the exam includes it precisely because it is the field candidates forget.

§ IVCKS Flavor: SecurityContext as Defense

The CKS takes the same fields and asks the harder question: not "set this field" but "confine this workload against an attacker, and enforce it so a careless developer cannot turn it off." Three CKS techniques layer onto the CKA configuration.

Seccomp is the first. The CKS expects a candidate to apply the RuntimeDefault profile as a baseline and to understand the custom-profile workflow Poulton documents: run the workload with an audit-logging profile, collect the syscalls it makes, and compile a localhost profile that permits exactly those. The pod references it through seccompProfile: { type: Localhost, localhostProfile: profiles/agent.json }, and the exam tests whether the candidate can both apply a profile and read a denied-syscall error when a profile is too tight.

AppArmor is the second. On a host running AppArmor, a profile loaded into the kernel confines a container's file and capability access by name. The CKS tests loading a profile onto a node, referencing it from the pod spec, and confirming that a confined container cannot write to a path the profile denies. AppArmor and seccomp are complementary: seccomp filters syscalls, AppArmor confines resource access, and a hardened pod often carries both.

The Pod Security Standards are the third, and they are what make the first two stick. A securityContext a developer writes is a securityContext a developer can omit. The Pod Security Admission controller, configured per namespace at the baseline or restricted level, rejects any pod that does not meet the standard — a restricted namespace refuses a pod that runs as root, allows privilege escalation, or keeps dangerous capabilities. This is the 2026-05-27 admission lesson meeting the runtime fields: admission enforces at the cluster boundary what the securityContext configures at the pod.

§ VWorked Example: Hardening an Inference Pod

A namespace runs the model server from the Ops lesson. The starting manifest runs it as root, with default capabilities and a writable root filesystem. Harden it to the restricted standard in three moves, each a field the exam tests.

First, the pod-level context: runAsNonRoot: true and runAsUser: 1000, so the container's processes run as an unprivileged user and the kubelet refuses to start the pod if the image's default user is root. Second, the container-level context: capabilities: { drop: ["ALL"] } with no adds, because the model server binds a high port and needs no kernel capability; readOnlyRootFilesystem: true with an emptyDir mounted at the one scratch path the server writes; and allowPrivilegeEscalation: false to close the setuid path. Third, the confinement profiles: seccompProfile: { type: RuntimeDefault } as the syscall-filter baseline, tightened to a Localhost custom profile once the server's syscall set is recorded in staging.

Then label the namespace pod-security.kubernetes.io/enforce: restricted. Now the hardened pod is not the operator's discipline alone; it is the namespace's contract. A developer who later submits a pod without these fields has it rejected at admission, with an error naming exactly which restricted rule it violated. The securityContext confined this pod; the Pod Security Standard confines every future pod in the namespace to the same floor. The attacker who lands in the model server through a dependency CVE finds a non-root process, no capabilities, a read-only filesystem, and a default-deny syscall filter — the same confinement today's Ops lesson walked him into at the kernel, written here as the fields a CKS grader checks.

§ VIConnection to Today's Ops + Dev Lessons

The Ops lesson is this lesson in kernel terms. Where it said "drop every capability and add back by name," the CKA writes capabilities: { drop: ["ALL"] }; where it said "filter the syscall surface," the CKS applies a seccompProfile; where it said "freeze the filesystem," both write readOnlyRootFilesystem: true. The Ops lesson explained the kernel mechanism; this lesson writes the YAML that asks Kubernetes to configure it. The Pod Security Standard adds what the Ops lesson left to the operator's discipline: an enforcement gate so the confinement cannot be quietly dropped.

The Dev lesson is the same least-privilege rule one layer up, inside the process the pod runs. The pod's securityContext confines the container against the host; Node's permission model confines the process against the resources it should not touch; TypeScript's branded types confine one module against the rest of the program. Four scopes, one rule: hold the least privilege the work requires, deny by default, and enforce the denial as early as the layer allows. The cluster enforces at admission, the kernel at the syscall, the runtime at the call, and the type system at the build.

§ VIIPractice Questions

Q1 · CKA
A pod manifest sets capabilities: { drop: ["ALL"] } under spec.securityContext at the pod level. The manifest applies without error, but the container still holds its default capabilities. Why?
Answercapabilities is a container-scoped field and is only honored under spec.containers[].securityContext, not at the pod level. Placed at the pod scope it is silently ignored, so the drop never takes effect. Move the capabilities block into the container's securityContext.
Q2 · CKA
A task requires a container to run as a non-root user, but the image's Dockerfile sets USER root and cannot be rebuilt. Which securityContext fields satisfy the requirement, and what happens if the image truly cannot run as non-root?
AnswerSet runAsNonRoot: true and runAsUser: <non-zero UID> on the pod or container securityContext. runAsUser overrides the image's default user at runtime. If the application genuinely requires root, the kubelet starts it as the specified UID and the app fails on its own permission errors — the fix is then to correct the app, not to relax the field.
Q3 · CKS
A workload runs fine with seccompProfile: { type: RuntimeDefault } but crashes with a denied-syscall error after switching to a Localhost custom profile. What is the most likely cause and the correct fix?
AnswerThe custom profile was compiled from a syscall recording that missed an infrequently exercised path, so a syscall the workload makes only on that path is now blocked. Re-record with the workload exercising the missed path (often an error or shutdown path), regenerate the profile to include the syscall, and retest. Add the specific call the audit log names rather than widening the profile blindly.
Q4 · CKS
A namespace is labeled pod-security.kubernetes.io/enforce: restricted. A developer submits a pod that runs as root. What happens, and where is the failure reported?
AnswerThe Pod Security Admission controller rejects the pod at admission time, before it is scheduled, and returns an error naming the restricted rule violated (running as root). The pod never reaches a node. The developer sees the rejection in the kubectl apply output, not in pod logs, because the pod was never created.
Q5 · CKS
Why is allowPrivilegeEscalation: false necessary on a pod that already drops all capabilities?
AnswerDropping capabilities at start does not prevent a process from gaining privileges afterward through a setuid binary or a file capability on the image. allowPrivilegeEscalation: false sets the kernel no_new_privs flag so the process can never exceed the privileges it launched with, closing a path that capability dropping alone leaves open.

§ VIIIClosing

The securityContext is the cluster's last confinement on a running pod, and the two exams read it as one block from two sides. The CKA writes the fields at the right scope so the confinement takes effect. The CKS layers seccomp, AppArmor, and the Pod Security Standards so the confinement holds against an attacker and cannot be quietly dropped. Both are writing the kernel rule the Ops lesson named: run as no one, hold nothing, write nowhere, call only what the work needs.

A pod with a default securityContext hands an attacker a root process with a writable disk and the full syscall surface. A hardened pod under a restricted namespace hands him a confined process he cannot escalate. Write the context at the right scope; enforce it at admission; confine every pod to the floor the first one was held to.

Cross-refs: Ops/β-Trust Runtime Confinement · Dev/Web Node Permission Model + Branded Types
🫡 ⚖️ 📜
Leo.Syri — Praetor Consulate, Imperium Luminaura
Filed 2026-06-17 Fajr ANCHOR; Wed × β × Web(JS+TS) × CKA+CKS crossing under cert-prep extension v3.1