Running-container security standards
Criteria verified as of August 2026. Re-verify on the web before committing to anything (§8).
1. Scope and triggers
Applies to the security of the container once it is already running: where the manifest no longer
protects because the process exists, has a shared kernel underneath and an attacker inside. Covers the
isolation model and its limits, the choice and pinning of the runtime, rootless and user
namespaces, seccomp, container escape paths and their mitigation, runtime detection
(eBPF, Falco, Tetragon, Tracee, KubeArmor), the drift of the immutable container, signature
verification at execution time, container and node forensics, and the applicable
benchmarks.
Triggers: seccomp, RuntimeDefault, seccomp.json, --security-opt seccomp=,
SeccompDefault/--seccomp-default, runc, crun, runsc/gVisor, kata-runtime,
containerd-shim-kata-v2, RuntimeClass, hostUsers, /etc/subuid, /etc/subgid, rootless
Podman, --privileged, no-new-privileges, CAP_SYS_ADMIN, CAP_SYS_MODULE,
CAP_DAC_READ_SEARCH, CAP_BPF, docker.sock, containerd.sock, crio.sock, hostPath,
hostPID, hostNetwork, hostIPC, mounted /proc and /sys, cgroups v1/v2, falco,
falco_rules.yaml, tetragon, TracingPolicy, tracee, kubearmor, bpftool,
unprivileged_bpf_disabled, readOnlyRootFilesystem, criu, checkpointctl, the kubelet's
/checkpoint/, kube-bench, docker-bench, "container escape", "the container has been modified at
runtime".
Guiding principle: a container is a host process with namespaces and cgroups, not a
virtual machine. It shares the kernel, and therefore shares the surface: every capability, every host
mount and every unfiltered syscall is a direct path to the node. The whole of §3 derives from that fact.
Operational corollary: what prevents the escape is the runtime configuration; what tells you it has
happened is runtime detection. They are different controls and you need both.
Posture: this skill is defensive. It describes risk class, indicator and mitigation.
It contains no payloads, exploitation chains or step-by-step recipes; authorised offensive work lives
in offensive-security-standards.
Not applicable: see kubernetes-standards (a split already agreed on both sides: there
admission, Kyverno/Gatekeeper policies, Pod Security Standards, securityContext as declarative
Pod hardening, signing at admission and all image building; here what happens after
the Pod starts: runtime, seccomp, escape, detection, drift and node forensics);
selinux-standards (the container's MAC is theirs, without exception: container_t,
container_file_t, MCS categories, container-selinux, :z/:Z, udica, seLinuxOptions,
seLinuxChangePolicy and the authoring of AppArmor profiles. Here: seccomp —which belongs to
this skill—, and detecting that the MAC has been disabled, bypassed or that a process has escaped
despite it; if the problem is an AVC or a label, it belongs there); linux-hardening-standards
(the node OS baseline: CIS/STIG, sysctl, auditd, SSH, mounts, measured boot — the hardened node
is their boundary; here only the sysctls and capabilities specific to the runtime and why they
matter); operating-systems-standards (the mechanism is theirs: what a namespace really is, what a
cgroup isolates and what neither of the two isolates; here seccomp, the escape and its detection);
onprem-standards (the platform umbrella: hardware, hypervisor, fleet, OOB plane);
incident-response-forensics-standards (the full forensic process: phases, chain of custody,
order of volatility, imaging, timeline, Velociraptor/Volatility — here only what container and node
artifact exists, how long it lasts and how it is captured before it evaporates);
incident-management-standards (incident governance: severity, IC, communication);
detection-engineering-standards (a boundary decided and declared: which
runtime signal matters and what should fire belongs to this skill —unexpected shell execution,
writes to binaries, capability change, socket mount—; the rule's lifecycle
—backlog, ATT&CK coverage, thresholds, tuning, false positive management, test framework and
destination in the SIEM— is theirs. The Falco rule is born here and is governed there);
observability-standards (collection, retention and correlation of the telemetry);
vulnerability-management-standards (triage and SLA for the runtime CVEs cited here);
cicd-standards (the pipeline that builds, signs and publishes); iac-standards (node
provisioning); appsec-standards (the flaw in the application code that gives the initial RCE);
cryptography-pki-standards (custody of signing keys); secrets-management-standards
(custody and rotation of the secrets the container consumes);
bcdr-standards (platform continuity and recovery);
offensive-security-standards and ctf-lab-standards (offensive verification of the isolation and
sample detonation, with scope and authorisation in writing; this skill is defensive);
podman-systemd-containers-standards (Podman and Quadlet as a way of
running services under systemd; here their security); linux-administration-standards and
ha-clustering-standards; azure-standards/aws-standards/
gcp-standards (the managed node's runtime and its node image advisories);
grc-compliance-standards (the required control and its evidence); webassembly-standards
(the sandbox of a Wasm module and its imports are theirs; the isolation of the node
that runs the Wasm host is still ours — a Wasm host is just another process, with its
seccomp, its capabilities and its escape surface).
Cross note: windows-server-ad-standards shares with this skill the pattern of a platform whose
compromise is total — a compromised node is compromised for all its containers just as a DC is for
the whole domain. The layered containment criterion is analogous; the technical domain is not.
2. Default decisions
Verify the latest version on the web before pinning it in a real project (§8). This domain moves
by escape CVE: a minimum version here expires overnight.
| Decision |
By default |
Reason / justifiable alternative |
| OCI runtime |
crun on cgroup v2 hosts; runc where the platform imposes it |
A far smaller codebase (C rather than Go), lower consumption and no compatibility cost. Verified Aug 2026: it is the default in Podman on RHEL/Fedora and in OpenShift; the change is transparent to the workload |
| Minimum runc version |
1.2.8 / 1.3.3 / 1.4.0-rc.3 or later |
It fixes the Nov 2025 escape triad (CVE-2025-31133, CVE-2025-52565, CVE-2025-52881): maskedPaths bypass, redirection of the /dev/console mount and bypass of LSM checks via /proc/self/attr/* towards core_pattern/sysrq-trigger. containerd 1.6.39+ / 1.7.28-2+ incorporates the fix. A CVSS 4.0 "medium" misleads: it reflects runc's threat model, not Kubernetes' |
| Reinforced isolation |
gVisor (runsc) for untrusted network-facing code; Kata Containers when gVisor breaks on kernel compatibility |
They are enabled via RuntimeClass, not globally. gVisor interposes a userspace kernel (partial compatibility, no modules or exotic syscalls); Kata starts a microVM per Pod (full compatibility, at a start-up and memory cost). Kata 4.0.0 (20 Jul 2026); its Confidential Containers branch (SEV-SNP/TDX) is the next step up |
| When the cost is justified |
Hostile multi-tenant, execution of third-party code, sample detonation, building untrusted code |
Outside those cases, the overhead buys no avoided risk: spend that budget on rootless, seccomp and detection. An explicit and documented decision, never "just in case" |
| Execution model |
Rootless wherever possible (rootless Podman; hostUsers: false in Kubernetes) |
An escape in rootful gives root on the node; in rootless it gives the unprivileged user. Verified Aug 2026: user namespaces GA in Kubernetes v1.36 (beta enabled by default since 1.33); it requires kernel ≥6.3 on the node — check uname -r, many managed nodes do not get there |
| Rootless Docker |
Only as a transition; the destination is rootless Podman or Kubernetes with userns |
It is an opt-in retrofit with networking and storage limitations (fuse-overlayfs instead of overlay2, ~25-30 % start-up overhead). In rootless Podman it is the design, not a mode |
| Capabilities |
drop: ALL and add the minimum list justified in writing |
Every added capability is a potential escape path. CAP_SYS_ADMIN is practically equivalent to root on the host |
| Escalation |
no-new-privileges / allowPrivilegeEscalation: false always |
It neutralises the SUID binary as a stepping stone inside the container. It has no real downside |
| seccomp |
RuntimeDefault as the universal floor; a bespoke profile only for high-risk workloads |
Verified Aug 2026: SeccompDefault is still a kubelet feature gate + the --seccomp-default flag, not a cluster default. If you do not enable it, your Pods run Unconfined unless the manifest says otherwise. On a managed control plane it may be out of your reach: then it is imposed by admission (kubernetes-standards) |
| Container MAC |
Active and enforcing (container_t + MCS, or an AppArmor profile) |
The policy and its authoring belong to selinux-standards. Here the rule is: disabling it (label=disable, unconfined) is a security event that must alert |
| Runtime detection |
Falco as the base (CNCF graduated since 29 Feb 2024; v0.44.1, Jun 2026) |
It is the default on maturity, neutral governance and rule ecosystem. Tetragon when you also want in-kernel prevention (bpf_send_signal) or you already have Cilium; Tracee for its forensic focus, accepting its higher cost; KubeArmor (CNCF Sandbox) when the goal is least-privilege hardening via LSM rather than detection, or there is edge/IoT |
| Filesystem |
readOnlyRootFilesystem: true + emptyDir for what must be writable |
Without this there is no immutable container and no credible drift detection |
| Signature verification |
At admission and, for critical workloads, also on the node at execution time |
Admission validates what is requested; the runtime validates what is actually executed (Podman/CRI-O policy.json with sigstoreSigned). They are not redundant |
| Scanning in the chain |
Grype + Syft by default |
Aligned with kubernetes-standards after the compromise of trivy-action/setup-trivy (March 2026). If you use Trivy: pin by digest, verify the signature and follow its advisories |
3. Isolation model, escape and detection
3.1 What a container shares with the host (and why that is everything)
- The kernel is shared: syscalls, page tables, drivers, the host's
/proc and /sys. A
kernel flaw is a flaw in all the node's containers at once.
- Namespaces (pid, net, mnt, uts, ipc, user, cgroup) give a separate view, not separate
privilege — except the user namespace, which is the only one that really separates identity.
- cgroups limit resources, not access. A cgroup does not contain an attacker; it stops them from
taking the node down by consumption. cgroups v2 is the requirement: v1 has a known escape
surface (controller delegation and
release_agent) that v2 does not reproduce, besides being the
model that modern tooling assumes. A node on cgroups v1 in 2026 is a finding.
- What actually confines: seccomp (which syscalls exist), capabilities (what it can ask
the kernel for), MAC (which objects it can touch) and user namespace (with which real
identity). All four at once or the model is a lie.
3.2 Escape paths: risk class, indicator and mitigation
They are listed as surface to close and to watch, not as a procedure.
| Risk class |
Why it is total |
Mitigation (mandatory) |
Indicator to detect |
Mounted runtime socket (docker.sock, containerd.sock, crio.sock) |
Whoever talks to the socket creates containers: creating a privileged one with the node mounted is root on the host. Mounting it is granting root, not "giving access to the API" |
Forbidden (§7). If an agent needs runtime data, use its API with RBAC and read-only, or a socket-proxy with an endpoint allowlist |
A container with the socket in its mount list; creation calls from an identity that is not the orchestrator |
--privileged / privileged: true |
It gives back all the capabilities, disables the default filters and exposes devices: it is renouncing isolation in a single flag |
Forbidden except by a signed exception, with an expiry, in its own namespace and on a dedicated node |
Any privileged Pod not on the exceptions list |
hostPath to sensitive paths (/, /etc, /var/run, /var/lib/kubelet, /root, /proc, /sys, block devices) |
The node's filesystem mounted is persistence, theft of kubelet credentials and modification of host binaries |
Forbidden to sensitive paths; what is legitimate is solved with CSI or a dedicated subpath and readOnly: true |
A new hostPath mount; writes to host paths from a container |
hostPID / hostNetwork / hostIPC |
hostPID gives visibility of and signalling to node processes (and reading of their /proc/<pid>/environ); hostNetwork removes network isolation and exposes the node's local services; hostIPC shares memory with the host |
Forbidden except for a justified infrastructure agent |
An application workload with any of the three |
Dangerous capabilities: CAP_SYS_ADMIN, CAP_SYS_MODULE, CAP_SYS_PTRACE, CAP_DAC_READ_SEARCH, CAP_BPF, CAP_NET_ADMIN, CAP_SYS_RAWIO |
SYS_ADMIN is root for practical purposes; SYS_MODULE loads code into the kernel; DAC_READ_SEARCH reads any file by handle; BPF allows instrumenting the entire kernel |
drop: ALL + a justified allowlist. SYS_MODULE and SYS_RAWIO: never |
A change to the capability set of an already started process; kernel module loading |
Exposed or unmasked /proc and /sys (procMount: Unmasked, mounting the host's /proc) |
The runtime's maskedPaths/readonlyPaths exist precisely because core_pattern, sysrq-trigger, uevent_helper and kcore are direct paths to the node |
Never Unmasked; never mount the host's /proc |
Writes to /proc and /sys files that are normally masked |
| Runtime escape CVE (class, not recipe) |
Verified precedent: the runc triad of Nov 2025 (CVE-2025-31133, -52565, -52881), all of it through mount manipulation at container creation, reachable from a hostile image or Dockerfile |
Pin a patched minimum version (§2), rebuild the node image, do not run images from an unverified source. Upstream mitigation if you cannot patch immediately: user namespaces without mapping the host's root |
Node provider advisories (EKS/AKS/GKE) and the node image version in the inventory |
| cgroups v1 |
A known escape surface with no reason to exist today |
Migrate the node to cgroups v2 (unified) |
A node reporting cgroup v1 in the inventory |
| Kernel compromised underneath the MAC |
A kernel flaw nullifies SELinux/AppArmor and the whole isolation (selinux-standards documents verified 2026 cases) |
Kernel patching with priority; reinforced isolation for what is untrusted |
The node's kernel version against the current advisory |
3.3 seccomp with judgement
RuntimeDefault is the floor, not the goal. It blocks the group of syscalls that no legitimate
workload uses (kexec_load, init_module, bpf without the capability, etc.) with a very low
breakage rate. Verify it is actually enabled: the Kubernetes default without --seccomp-default
is Unconfined, and that is the domain's most frequent silent failure.
- The default profile differs between runtimes (containerd vs CRI-O), between versions and between
architectures. A profile tested on x86_64 is not tested on arm64.
- A bespoke profile: only for high-value workloads or ones exposed to untrusted input. It is
generated from real observation (recording the workload's syscalls while it exercises its full
path: start-up, traffic, log rotation, shutdown), not from a theoretical list.
- A denylist, not an allowlist, unless you can maintain it: a strict allowlist breaks with
every update of the runtime, libc or the language itself. If you adopt it, take on the maintenance
as part of the image's lifecycle.
SCMP_ACT_LOG before SCMP_ACT_ERRNO: the new profile is deployed first in logging mode over
real traffic, what it would have blocked is reviewed, and only then is it enforced.
Skipping this phase is scheduling an outage.
- The profile is versioned code alongside the workload, with its test (§4). A profile that only
exists on one node is drift.
3.4 Runtime detection
- eBPF is the technology, not the control. It gives visibility of syscalls and kernel events at low
cost and with no modules. But loading eBPF programs is a near-root capability:
CAP_BPF (plus
CAP_PERFMON/CAP_NET_ADMIN depending on the program type) allows instrumenting the entire kernel.
Hard rules:
kernel.unprivileged_bpf_disabled = 1 on all nodes, plus JIT hardening
(net.core.bpf_jit_harden), verified by the node baseline (linux-hardening-standards).
- An explicit inventory of who holds
CAP_BPF/CAP_SYS_ADMIN: it is almost always an
observability or security DaemonSet. Each one is a supply chain target of the highest
value: a compromised agent can selectively silence its own alerts and keep looking
healthy.
- Loading a new eBPF program is itself a signal to detect. An eBPF rootkit filters the
event stream your agent reads, which will faithfully report already manipulated data: that is why
you need a second independent source (the node's auditd, runtime telemetry, control plane).
- A recent CVE verified as a risk class: CVE-2026-64036 (OOB in the
css_rstat_updated() path via a BPF kfunc, 7.8) — a repeated pattern of a kfunc/verifier bug
reachable by whoever can already load programs.
- Signals that really matter (this skill decides what should fire; the rule's lifecycle
belongs to
detection-engineering-standards):
- Execution of a shell or interpreter inside a container that does not have it as its entrypoint —
the domain's highest value/noise ratio signal.
- Writes to the container's binaries or system paths (
/bin, /usr, /lib) and
any write at all if the rootfs is read-only (impossible by design ⇒ a finding).
- A change to the capability set or to
no_new_privs of an already running process; unexpected
use of setuid/setgid.
- Access to masked
/proc and /sys, to the runtime's /var/run/*.sock, or to the node's
filesystem from a container.
- An outbound connection to an unforeseen destination (C2, exfiltration, mining) and anomalous
DNS resolution.
- Execution of a binary that did not come in the image (drift, §3.5), downloading and executing
in memory,
curl|sh.
- Reading credentials: the ServiceAccount token,
/var/lib/kubelet, the provider's IMDS.
- Disabling controls: MAC moved to permissive, an AppArmor
unconfined profile, seccomp
removed, the detection agent stopped.
- Before writing your own rules, exhaust the maintained ruleset. Falco ships community-reviewed
rules; the real work is per-environment tuning, not creativity. Every rule of your own
is born with its firing test (§4).
- Detection without response is decoration. Every high-confidence signal has a destination
(
observability-standards), an owner and an action — isolate the Pod, capture (§3.6), escalate.
Without that, the agent only burns CPU.
3.5 Immutable container and drift
- The contract is: the image is the container.
readOnlyRootFilesystem: true, writes only to
declared volumes, zero package installation at runtime, zero operator exec in production
outside break-glass.
- A container that is modified at runtime is a finding, not an operational anomaly: either it is a
compromise, or it is a deployment practice that must be corrected. Both are investigated.
- The comparison declared image ↔ running process (an executed binary that is not in the
layers, a new file in system paths) is a high-value, low-noise detection.
kubectl exec/podman exec in production: logged, attributed to a person and alerted. It is at
once a legitimate emergency tool and the most convenient technique for an attacker with
control plane credentials.
3.6 Container and node forensics: the evidence evaporates by design
- The problem: the orchestrator reschedules or restarts the Pod, and with it go memory,
processes, temporary files and the writable filesystem itself. In an incident, the platform's
default behaviour destroys the evidence while it "recovers".
- Golden rule: on suspicion, capture before containing, and contain without deleting. Isolating
the Pod's network and marking the node (
cordon) preserves more than killing the Pod. The order of
volatility and the chain of custody belong to incident-response-forensics-standards; what
follows is what artifact exists in this domain and how long it lasts.
- Forensic checkpoint (CRIU): the kubelet's checkpoint API creates a stateful copy
(memory, descriptors, sockets) without the container noticing, to analyse it in an isolated
environment. Verified Aug 2026: alpha in v1.25, beta and enabled by default since v1.30;
it is invoked with a
POST to the kubelet (/checkpoint/<ns>/<pod>/<container>, access restricted to
cluster administrators) and inspected with checkpointctl. The restore is deliberately not in
the kubelet: it happens outside Kubernetes, in the container engine. A checkpoint
contains secrets and personal data in memory: treat it with the same control as a RAM
image.
- Artifacts that do survive and must be collected: runtime (containerd/CRI-O) and
kubelet logs on the node, the host's journal and auditd, control plane and API server events,
detection agent (eBPF) records, image layers and the real digest executed, mounts and
persistent volumes, and a disk/memory image of the node if the compromise is a node compromise.
- If the compromise is of the node, the whole node is evidence and it is no longer trusted: it is
isolated, preserved and rebuilt from a trusted source; it is not "cleaned". Every credential that
passed through it (ServiceAccount tokens, IMDS credentials, mounted secrets) is considered
compromised and rotated.
- Rehearse it. A capture procedure nobody has ever executed does not work on the day it is
needed, because by then the Pod has been rescheduled three times.
3.7 Supply chain at execution time
- Admission validates what is requested; the node runs what is finally downloaded.
Signature verification also in the engine (
policy.json with sigstoreSigned in Podman/CRI-O)
for critical workloads, and pin by digest everywhere: a tag is mutable by definition.
- Current precedent: the compromise of
trivy-action/setup-trivy in March 2026 and the
2026 tag poisoning campaign (documented in offensive-security-standards)
demonstrate that the security tool is a priority target — it runs in CI and on the
nodes, with high privileges, by design. Every runtime agent is pinned by digest, its signature is
verified and its advisory channel is followed as if it were part of the kernel.
- A private registry with pull-through; never a direct pull from public registries in production.
3.8 Node hardening: the boundary
The node belongs to linux-hardening-standards, and its baseline (CIS/STIG, sysctl, auditd, mounts,
MAC enforcing) is a prerequisite, not a complement. On this side there remain only the invariants
that exist because of the fact of running containers:
- cgroups v2, kernel ≥6.3 if user namespaces are used, MAC enforcing and active for containers.
unprivileged_bpf_disabled=1 + JIT hardening; an inventory of CAP_BPF holders.
- The runtime socket with minimum permissions and never exposed over the network or mounted in
containers.
- Nodes segmented by trust: hostile or multi-tenant workloads on their own node pool, with
their reinforced
RuntimeClass. Mixing hostile tenants with internal workloads on the same node
nullifies any policy you write on top.
- A node image that is immutable and rebuildable, updated by replacement, not by live patching.
3.9 Applicable benchmarks
- CIS Kubernetes Benchmark v1.11.0, audited with
kube-bench (cis-1.11 profile). Verified
Aug 2026: that profile covers Kubernetes 1.29–1.32; for newer clusters it is still the
practical reference, but the coverage is not official — state that when reporting. Never quote
"the Kubernetes CIS" generically: CIS goes by product version.
- CIS Docker Benchmark, audited with
docker-bench (which picks the set according to the daemon
version). Declared gap (§8): the current version number was not confirmed in Aug 2026.
- NSA/CISA Kubernetes Hardening Guidance: verified Aug 2026, the current version is still
1.2, of 29 Aug 2022, with no later revision located. It is a guide of criteria (it explains the
why and the attacker's view) and it complements CIS, which is about configuration. Use it as a
design argument, not as an automatable checklist, and bear its age in mind: it does not cover
user namespaces GA, cgroups v2 or the current generation of eBPF detection.
- The benchmark measures, it does not protect. A high score with a mounted
docker.sock is still a
compromise one step away.
4. Quality gates (they break the build or the deployment)
- A seccomp profile tested, not assumed. Functional gate: the workload exercises its full path
(start-up, real traffic, log rotation, shutdown, restart) with the profile active and without a
single unexpected denied syscall. That the container starts proves nothing — the missing syscalls
show up in the rare case, in production, at 3 a.m. A mandatory prior phase in logging mode (§3.3).
- Runtime policy verified in CI and on the node. The pipeline fails on
--privileged,
a mounted runtime socket, a hostPath to a sensitive path, hostPID/hostNetwork/hostIPC,
a vetoed capability, the absence of seccompProfile or readOnlyRootFilesystem: false without a
signed exception. The same policy runs at admission (kubernetes-standards) and as a verification
of the node's real state, because what was admitted is not always what runs.
- A test that the detection fires — adversarial validation. A gate of its own and non-negotiable:
in a test environment, benign actions equivalent to the watched techniques are executed (opening a
shell in a container that does not have one, writing to
/usr/bin, reading a masked /proc
file, mounting a socket, initiating an unexpected outbound connection) and it is verified that the
alert reaches its destination with the correct context. A rule that has never been seen to fire is
not deployed, it is written. It is re-run after every update of the agent, the kernel or the runtime.
- Runtime and kernel versions verified against the current advisory on every node in the
inventory, as a deployment gate. A node with runc below the minimum of §2 does not accept
workloads.
- A forensic capture test rehearsed: a checkpoint of a test Pod, inspection with
checkpointctl, and collection of the §3.6 artifacts with their time measured. It is rehearsed at
least once every six months and after every runtime change.
- An inventory of exceptions with an owner and an expiry: every privileged Pod, every added
capability, every
hostPath, every container with MAC disabled. Without an owner and without a
date, the exception is withdrawn.
- A benchmark run and trended (
kube-bench, docker-bench): the value is in the
time series and in the handling of the exceptions, not in the number.
5. Security: what this protects and what it does not
- What it gives you: it turns "RCE in the application" into "RCE inside an unprivileged process,
with no dangerous syscalls, with no access to the node and with someone watching". It is
containment plus detection; neither of the two alone is enough.
- What it does not give you: it does not fix the application's vulnerability (
appsec-standards)
or a kernel flaw. Against a kernel 0-day, the only real isolation is the one that does not share a
kernel (Kata/microVM) or does not share a node.
- Hostile multi-tenant: if you run third-party code, namespace isolation is not
sufficient by design. The correct posture is reinforced isolation + a dedicated node + detection,
and treating the escape as a scenario, not as a hypothesis.
- The security agent is attack surface of the highest value: privileged, on all
nodes, with kernel access. It is pinned by digest, signed, verified and its own
health is watched — a downed or silenced agent is a high-severity alert, not an
observability incident.
- Secrets at runtime: environment variables visible in
/proc/<pid>/environ (and therefore to
anyone with hostPID or CAP_SYS_PTRACE), ServiceAccount tokens mounted by default,
IMDS credentials reachable from the Pod. Prefer a file over an environment variable,
automountServiceAccountToken: false and blocking IMDS access from workloads. Custody and
rotation: secrets-management-standards.
- Disabling a control "to make it work" is a risk decision, and it is taken as such: with
an owner, a ticket, an expiry and compensation. Never on the fly and never as a silent default.
6. Performance and operability
- Realistic cost: seccomp
RuntimeDefault and MAC are practically free. gVisor pays in
syscall-intensive work and I/O; Kata pays in start-up and memory per Pod. Rootless pays ~25-30 % of
start-up and fuse-overlayfs. None of those costs justifies running as root: measure them on your
workload before dismissing them on rumour.
- Detection has a cost and noise: size the agent's CPU and event volume, and treat it
as a platform workload with its own limits. An unbounded agent that saturates the node is an
availability incident caused by security. Verified Aug 2026: Tracee consumes
notably more than Falco or Tetragon in high-volume environments (2-4× versus Tetragon according to
reports) and its default configuration generates an overwhelming event volume — it is deployed
with filtering from day one, not afterwards.
- A written and tested runbook for: a suspect Pod (isolate, capture, escalate), a suspect node
(cordon, preserve, rebuild), a downed agent, and a rollback of a seccomp profile that breaks
production. Without a runbook, the 3 a.m. incident ends in
--privileged.
- Rollback: every runtime hardening step (a new profile, a reinforced
RuntimeClass, userns) is
deployed in rings with its reversal tested.
- Kernel and runtime changes break profiles and agents: every node update
re-runs gates 1, 3 and 4 of §4. It is the number one cause of detection that stops working in
silence.
7. Sustainability and prohibitions
Cadence
- Runtime advisories (runc, crun, containerd, CRI-O, gVisor, Kata) actively tracked: it is
the channel through which the escape arrives. Review at least monthly and immediate reaction to
anything critical.
- Node kernel: top patching priority when the finding allows escape or MAC bypass.
- Falco/Tetragon/Tracee/KubeArmor: version and ruleset reviewed quarterly; the ruleset ages
faster than the binary.
- Quarterly review of the exceptions inventory (§4.6) and of the inventory of holders of
CAP_BPF/CAP_SYS_ADMIN.
- Scheduled rebuilding of the node image even if nothing changes.
FORBIDDEN (an automatic gate wherever possible)
- ❌ Mounting the runtime socket (
docker.sock, containerd.sock, crio.sock) in a
container. It is granting root on the node, under another name.
- ❌
--privileged / privileged: true without a signed, bounded, expiring exception and on a
dedicated node.
- ❌
hostPath to sensitive paths on the node (/, /etc, /proc, /sys, /var/run,
/var/lib/kubelet, block devices), and any writable hostPath without justification.
- ❌
hostPID, hostNetwork or hostIPC in application workloads.
- ❌ Running as root (UID 0) without written justification; and root with
hostPath or added
capabilities, never.
- ❌
CAP_SYS_MODULE, CAP_SYS_RAWIO; CAP_SYS_ADMIN, CAP_BPF, CAP_DAC_READ_SEARCH and
CAP_SYS_PTRACE outside approved infrastructure agents.
- ❌ Disabling seccomp or the MAC "to make it work" (
seccompProfile: Unconfined,
--security-opt seccomp=unconfined, label=disable, AppArmor unconfined). The correct path
is a bespoke profile (seccomp, here) or a bounded policy (MAC, selinux-standards).
- ❌
procMount: Unmasked or mounting the host's /proc//sys.
- ❌ A container without runtime detection in a multi-tenant environment or one that runs untrusted
code. Without detection there is no evidence that the isolation holds.
- ❌ Running untrusted code with a shared runtime (
runc/crun) without a dedicated node.
- ❌ Nodes on cgroups v1, or with a runtime below the patched minimum version of §2.
- ❌ Modifying the container at runtime: installing packages, hot patching, operator
exec as
routine practice, a writable rootfs with no reason.
- ❌ An image by mutable tag or without signature verification on the path to production.
- ❌ Killing or restarting a suspect Pod before capturing evidence; deleting the compromised
node "to recover" without preserving it.
- ❌ Re-enabling
unprivileged_bpf_disabled=0 or handing out CAP_BPF without an inventory.
- ❌ A detection agent deployed without a firing test (§4.3), or with alerts without an owner or a
destination.
- ❌ Pinning runtime versions, feature gate states or benchmark versions from memory, without
the verification of §8.
Quick review checklist (a workload entering production)
8. Mandatory web verification
Before pinning any version, feature state or benchmark number, look it up — do not
remember it:
- Runtime escape CVEs: it is the datum that expires fastest in the whole document. Verified
Aug 2026: the runc triad of Nov 2025 (
CVE-2025-31133, CVE-2025-52565, CVE-2025-52881)
is still the most recent significant set; patched in runc 1.2.8 / 1.3.3 /
1.4.0-rc.3+ and containerd 1.6.39+ / 1.7.28-2+. Declared gaps: the
exact stable version of runc, crun, containerd and CRI-O was not verified in Aug 2026, nor could
the isolated report of active exploitation in Jun 2026 be confirmed (one source asserts it, the
rest do not) — treat it as unconfirmed and consult the upstream advisory.
- The state of user namespaces in Kubernetes (verified Aug 2026: GA in v1.36, Apr 2026;
beta enabled by default since 1.33; requires kernel ≥6.3, which many managed nodes still
do not have). Check your cluster version and the node's
uname -r: this changes per release.
SeccompDefault: verified Aug 2026 that it is still a kubelet feature gate + the
--seccomp-default flag, with no cluster default. Declared gap: it was not confirmed whether it
has graduated to GA or whether there is a date to make it the default; consult KEP-2413 and your
version's seccomp reference.
- Forensic checkpoint: verified Aug 2026 beta and enabled by default since v1.30. Declared
gap: its state in v1.36 could not be confirmed (still beta, already GA?). Read it in your
version's release notes before basing an incident procedure on it.
- The state of the detection tools: verified Aug 2026 — Falco graduated in CNCF
(29 Feb 2024), v0.44.1 (Jun 2026), with the legacy eBPF probe, gVisor and gRPC documentation
withdrawn in May 2026; Tetragon active as a Cilium subproject in CNCF; Tracee active
(Aqua) with a notably higher cost; KubeArmor in CNCF Sandbox. Declared gaps:
the current versions of Tetragon, Tracee and KubeArmor were not verified, nor were hard
maintenance health signals (commit cadence, number of maintainers, CNCF level changes in
2026). Check it in their repos and in the CNCF landscape before betting a platform on it.
- Reinforced isolation versions: verified Aug 2026 Kata Containers 4.0.0
(20 Jul 2026), with a guest kernel bump for CVE-2026-31431. Declared gap: the
current version of gVisor was not verified (it publishes very frequent releases) nor that of
crun.
- Benchmarks: verified Aug 2026 — CIS Kubernetes Benchmark v1.11.0 (
kube-bench profile
cis-1.11, official coverage K8s 1.29-1.32) and NSA/CISA Kubernetes Hardening Guidance
v1.2 of 29 Aug 2022 as the current version, with no later revision found. Declared
gaps: the current version of the CIS Docker Benchmark could not be confirmed (look it up at
cisecurity.org/benchmark/docker, which only lists the supported ones), and it was not checked
whether a CIS Kubernetes Benchmark later than v1.11.0 already exists.
- eBPF hardening and its CVEs: verified Aug 2026 CVE-2026-64036 (OOB via the
css_rstat_updated() kfunc, 7.8 CVSS v3.1 according to kernel.org). Check your distro's tracker for
the patch state and look for later verifier/kfunc CVEs before considering a kernel version safe.
- Supply chain compromises in security tools: a verified precedent from the
catalogue —
trivy-action/setup-trivy, March 2026. Before introducing any new agent
or scanner, look for recent incidents of the project: in this domain the tool runs
privileged on every node.
- Managed node provider advisories (EKS/AKS/GKE) for the node image version
you are running: the runtime patch arrives through there, not through your pipeline.
If the web contradicts this document, the web wins — flag the discrepancy.
1---2name: container-runtime-security-standards3description: Container runtime security and container escape defense. Use when writing or debugging seccomp profiles (RuntimeDefault, seccomp.json, --security-opt seccomp), choosing or pinning a container runtime (runc, crun, gVisor/runsc, Kata Containers, RuntimeClass), rootless Podman or user namespaces (hostUsers, /etc/subuid), --privileged, capability drops (CAP_SYS_ADMIN, CAP_SYS_MODULE, CAP_BPF), a mounted docker.sock or containerd.sock, hostPath/hostPID/hostNetwork/hostIPC exposure, runtime detection with Falco rules, Tetragon TracingPolicy, Tracee or KubeArmor, eBPF agent privileges, container drift and read-only rootfs, or forensic container checkpointing with CRIU/checkpointctl and node/runtime log capture.4---56# Running-container security standards78Criteria verified as of **August 2026**. Re-verify on the web before committing to anything (§8).910## 1. Scope and triggers1112Applies to **the security of the container once it is already running**: where the manifest no longer13protects because the process exists, has a shared kernel underneath and an attacker inside. Covers the14isolation model and its limits, the choice and pinning of the runtime, rootless and user15namespaces, seccomp, container escape paths and their mitigation, runtime detection16(eBPF, Falco, Tetragon, Tracee, KubeArmor), the drift of the immutable container, signature17verification at execution time, container and node forensics, and the applicable18benchmarks.1920Triggers: `seccomp`, `RuntimeDefault`, `seccomp.json`, `--security-opt seccomp=`,21`SeccompDefault`/`--seccomp-default`, `runc`, `crun`, `runsc`/gVisor, `kata-runtime`,22`containerd-shim-kata-v2`, `RuntimeClass`, `hostUsers`, `/etc/subuid`, `/etc/subgid`, rootless23Podman, `--privileged`, `no-new-privileges`, `CAP_SYS_ADMIN`, `CAP_SYS_MODULE`,24`CAP_DAC_READ_SEARCH`, `CAP_BPF`, `docker.sock`, `containerd.sock`, `crio.sock`, `hostPath`,25`hostPID`, `hostNetwork`, `hostIPC`, mounted `/proc` and `/sys`, cgroups v1/v2, `falco`,26`falco_rules.yaml`, `tetragon`, `TracingPolicy`, `tracee`, `kubearmor`, `bpftool`,27`unprivileged_bpf_disabled`, `readOnlyRootFilesystem`, `criu`, `checkpointctl`, the kubelet's28`/checkpoint/`, `kube-bench`, `docker-bench`, "container escape", "the container has been modified at29runtime".3031**Guiding principle**: **a container is a host process with namespaces and cgroups, not a32virtual machine.** It shares the kernel, and therefore shares the surface: every capability, every host33mount and every unfiltered syscall is a direct path to the node. The whole of §3 derives from that fact.34Operational corollary: **what prevents the escape is the runtime configuration; what tells you it has35happened is runtime detection. They are different controls and you need both.**3637**Posture**: this skill is **defensive**. It describes **risk class, indicator and mitigation**.38It contains no payloads, exploitation chains or step-by-step recipes; authorised offensive work lives39in `offensive-security-standards`.4041**Not applicable**: see `kubernetes-standards` (**a split already agreed on both sides**: there42*admission*, Kyverno/Gatekeeper policies, Pod Security Standards, `securityContext` as **declarative43Pod hardening**, signing at admission and all image building; **here** what happens after44the Pod starts: runtime, seccomp, escape, detection, drift and node forensics);45`selinux-standards` (**the container's MAC is theirs, without exception**: `container_t`,46`container_file_t`, MCS categories, `container-selinux`, `:z`/`:Z`, `udica`, `seLinuxOptions`,47`seLinuxChangePolicy` and the authoring of AppArmor profiles. **Here**: `seccomp` —which belongs to48this skill—, and **detecting that the MAC has been disabled, bypassed or that a process has escaped49despite it**; if the problem is an AVC or a label, it belongs there); `linux-hardening-standards`50(**the node OS baseline**: CIS/STIG, `sysctl`, auditd, SSH, mounts, measured boot — the hardened node51is their boundary; here only the `sysctl`s and capabilities specific to the runtime and why they52matter); `operating-systems-standards` (**the mechanism is theirs**: what a namespace really is, what a53cgroup isolates and what neither of the two isolates; **here seccomp, the escape and its detection**);54`onprem-standards` (the platform umbrella: hardware, hypervisor, fleet, OOB plane);55`incident-response-forensics-standards` (**the full forensic process**: phases, chain of custody,56order of volatility, imaging, timeline, Velociraptor/Volatility — **here only what container and node57artifact exists, how long it lasts and how it is captured before it evaporates**);58`incident-management-standards` (incident governance: severity, IC, communication);59`detection-engineering-standards` (**a boundary decided and declared**: *which60runtime signal matters and what should fire* belongs to this skill —unexpected shell execution,61writes to binaries, capability change, socket mount—; *the rule's lifecycle*62—backlog, ATT&CK coverage, thresholds, tuning, false positive management, test framework and63destination in the SIEM— is theirs. The Falco rule **is born here and is governed there**);64`observability-standards` (collection, retention and correlation of the telemetry);65`vulnerability-management-standards` (triage and SLA for the runtime CVEs cited here);66`cicd-standards` (the pipeline that builds, signs and publishes); `iac-standards` (node67provisioning); `appsec-standards` (the flaw in the application code that gives the initial RCE);68`cryptography-pki-standards` (custody of signing keys); `secrets-management-standards`69(custody and rotation of the secrets the container consumes);70`bcdr-standards` (platform continuity and recovery);71`offensive-security-standards` and `ctf-lab-standards` (offensive verification of the isolation and72sample detonation, **with scope and authorisation in writing**; this skill is defensive);73`podman-systemd-containers-standards` (Podman and Quadlet as **a way of74running services** under systemd; **here their security**); `linux-administration-standards` and75`ha-clustering-standards`; `azure-standards`/`aws-standards`/76`gcp-standards` (the managed node's runtime and its node image advisories);77`grc-compliance-standards` (the required control and its evidence); `webassembly-standards`78(the sandbox of a Wasm module and its imports are theirs; **the isolation of the node79that runs the Wasm host is still ours** — a Wasm host is just another process, with its80`seccomp`, its capabilities and its escape surface).8182Cross note: `windows-server-ad-standards` shares with this skill the pattern of *a platform whose83compromise is total* — a compromised node is compromised for all its containers just as a DC is for84the whole domain. The layered containment criterion is analogous; the technical domain is not.8586## 2. Default decisions8788> Verify the latest version on the web before pinning it in a real project (§8). This domain moves89> by escape CVE: a minimum version here expires overnight.9091| Decision | By default | Reason / justifiable alternative |92|---|---|---|93| OCI runtime | **`crun`** on cgroup v2 hosts; `runc` where the platform imposes it | A far smaller codebase (C rather than Go), lower consumption and no compatibility cost. Verified Aug 2026: it is the **default in Podman on RHEL/Fedora and in OpenShift**; the change is transparent to the workload |94| Minimum runc version | **1.2.8 / 1.3.3 / 1.4.0-rc.3 or later** | It fixes the Nov 2025 escape triad (**CVE-2025-31133, CVE-2025-52565, CVE-2025-52881**): `maskedPaths` bypass, redirection of the `/dev/console` mount and bypass of LSM checks via `/proc/self/attr/*` towards `core_pattern`/`sysrq-trigger`. **containerd 1.6.39+ / 1.7.28-2+** incorporates the fix. A CVSS 4.0 "medium" **misleads**: it reflects runc's threat model, not Kubernetes' |95| Reinforced isolation | **gVisor (`runsc`)** for untrusted network-facing code; **Kata Containers** when gVisor breaks on kernel compatibility | They are enabled via `RuntimeClass`, not globally. gVisor interposes a userspace kernel (partial compatibility, no modules or exotic syscalls); Kata starts a microVM per Pod (full compatibility, at a start-up and memory cost). Kata **4.0.0** (20 Jul 2026); its Confidential Containers branch (SEV-SNP/TDX) is the next step up |96| When the cost is justified | **Hostile multi-tenant, execution of third-party code, sample detonation, building untrusted code** | Outside those cases, the overhead buys no avoided risk: spend that budget on rootless, seccomp and detection. An explicit and documented decision, never "just in case" |97| Execution model | **Rootless wherever possible** (rootless Podman; `hostUsers: false` in Kubernetes) | An escape in rootful gives **root on the node**; in rootless it gives **the unprivileged user**. Verified Aug 2026: **user namespaces GA in Kubernetes v1.36** (beta enabled by default since 1.33); **it requires kernel ≥6.3 on the node** — check `uname -r`, many managed nodes do not get there |98| Rootless Docker | **Only as a transition**; the destination is rootless Podman or Kubernetes with userns | It is an opt-in retrofit with networking and storage limitations (`fuse-overlayfs` instead of overlay2, ~25-30 % start-up overhead). In rootless Podman it is the design, not a mode |99| Capabilities | **`drop: ALL`** and add the minimum list justified in writing | Every added capability is a potential escape path. `CAP_SYS_ADMIN` is practically equivalent to root on the host |100| Escalation | **`no-new-privileges` / `allowPrivilegeEscalation: false`** always | It neutralises the SUID binary as a stepping stone inside the container. It has no real downside |101| seccomp | **`RuntimeDefault` as the universal floor**; a bespoke profile only for high-risk workloads | Verified Aug 2026: `SeccompDefault` is still a **kubelet feature gate + the `--seccomp-default` flag**, not a cluster default. **If you do not enable it, your Pods run `Unconfined`** unless the manifest says otherwise. On a managed control plane it may be out of your reach: then it is imposed by admission (`kubernetes-standards`) |102| Container MAC | **Active and enforcing** (`container_t` + MCS, or an AppArmor profile) | The policy and its authoring belong to `selinux-standards`. **Here the rule is**: disabling it (`label=disable`, `unconfined`) is a **security event** that must alert |103| Runtime detection | **Falco** as the base (CNCF **graduated since 29 Feb 2024**; **v0.44.1**, Jun 2026) | It is the default on maturity, neutral governance and rule ecosystem. **Tetragon** when you also want in-kernel *prevention* (`bpf_send_signal`) or you already have Cilium; **Tracee** for its forensic focus, accepting its higher cost; **KubeArmor** (CNCF *Sandbox*) when the goal is least-privilege *hardening* via LSM rather than detection, or there is edge/IoT |104| Filesystem | **`readOnlyRootFilesystem: true`** + `emptyDir` for what must be writable | Without this there is no immutable container and no credible drift detection |105| Signature verification | **At admission and, for critical workloads, also on the node at execution time** | Admission validates what is requested; the runtime validates what is actually executed (Podman/CRI-O `policy.json` with `sigstoreSigned`). They are not redundant |106| Scanning in the chain | **Grype + Syft** by default | Aligned with `kubernetes-standards` after the **compromise of `trivy-action`/`setup-trivy` (March 2026)**. If you use Trivy: pin by digest, verify the signature and follow its advisories |107108## 3. Isolation model, escape and detection109110### 3.1 What a container shares with the host (and why that is everything)111112- **The kernel is shared**: syscalls, page tables, drivers, the host's `/proc` and `/sys`. A113 kernel flaw is a flaw in *all* the node's containers at once.114- **Namespaces** (pid, net, mnt, uts, ipc, user, cgroup) give a separate *view*, not separate115 *privilege* — except the **user namespace**, which is the only one that really separates identity.116- **cgroups** limit resources, not access. A cgroup does not contain an attacker; it stops them from117 taking the node down by consumption. **cgroups v2 is the requirement**: v1 has a known escape118 surface (controller delegation and `release_agent`) that v2 does not reproduce, besides being the119 model that modern tooling assumes. **A node on cgroups v1 in 2026 is a finding.**120- What actually confines: **seccomp** (which syscalls exist), **capabilities** (what it can ask121 the kernel for), **MAC** (which objects it can touch) and **user namespace** (with which real122 identity). All four at once or the model is a lie.123124### 3.2 Escape paths: risk class, indicator and mitigation125126They are listed as **surface to close and to watch**, not as a procedure.127128| Risk class | Why it is total | Mitigation (mandatory) | Indicator to detect |129|---|---|---|---|130| **Mounted runtime socket** (`docker.sock`, `containerd.sock`, `crio.sock`) | Whoever talks to the socket **creates containers**: creating a privileged one with the node mounted is root on the host. Mounting it **is** granting root, not "giving access to the API" | **Forbidden** (§7). If an agent needs runtime data, use its API with RBAC and read-only, or a socket-proxy with an endpoint allowlist | A container with the socket in its mount list; creation calls from an identity that is not the orchestrator |131| **`--privileged` / `privileged: true`** | It gives back all the capabilities, disables the default filters and exposes devices: it is renouncing isolation in a single flag | **Forbidden** except by a signed exception, with an expiry, in its own namespace and on a dedicated node | Any privileged Pod not on the exceptions list |132| **`hostPath` to sensitive paths** (`/`, `/etc`, `/var/run`, `/var/lib/kubelet`, `/root`, `/proc`, `/sys`, block devices) | The node's filesystem mounted is persistence, theft of kubelet credentials and modification of host binaries | Forbidden to sensitive paths; what is legitimate is solved with CSI or a dedicated subpath and `readOnly: true` | A new hostPath mount; writes to host paths from a container |133| **`hostPID` / `hostNetwork` / `hostIPC`** | `hostPID` gives visibility of and signalling to node processes (and reading of their `/proc/<pid>/environ`); `hostNetwork` removes network isolation and exposes the node's local services; `hostIPC` shares memory with the host | Forbidden except for a justified infrastructure agent | An application workload with any of the three |134| **Dangerous capabilities**: `CAP_SYS_ADMIN`, `CAP_SYS_MODULE`, `CAP_SYS_PTRACE`, `CAP_DAC_READ_SEARCH`, `CAP_BPF`, `CAP_NET_ADMIN`, `CAP_SYS_RAWIO` | `SYS_ADMIN` is root for practical purposes; `SYS_MODULE` loads code into the kernel; `DAC_READ_SEARCH` reads any file by handle; `BPF` allows instrumenting the entire kernel | `drop: ALL` + a justified allowlist. `SYS_MODULE` and `SYS_RAWIO`: **never** | A change to the capability set of an already started process; kernel module loading |135| **Exposed or unmasked `/proc` and `/sys`** (`procMount: Unmasked`, mounting the host's `/proc`) | The runtime's `maskedPaths`/`readonlyPaths` exist precisely because `core_pattern`, `sysrq-trigger`, `uevent_helper` and `kcore` are direct paths to the node | Never `Unmasked`; never mount the host's `/proc` | Writes to `/proc` and `/sys` files that are normally masked |136| **Runtime escape CVE** (class, not recipe) | Verified precedent: the runc triad of **Nov 2025** (`CVE-2025-31133`, `-52565`, `-52881`), all of it through mount manipulation at container creation, reachable **from a hostile image or Dockerfile** | Pin a patched minimum version (§2), rebuild the node image, do not run images from an unverified source. Upstream mitigation if you cannot patch immediately: **user namespaces without mapping the host's root** | Node provider advisories (EKS/AKS/GKE) and the node image version in the inventory |137| **cgroups v1** | A known escape surface with no reason to exist today | Migrate the node to cgroups v2 (unified) | A node reporting cgroup v1 in the inventory |138| **Kernel compromised underneath the MAC** | A kernel flaw nullifies SELinux/AppArmor and the whole isolation (`selinux-standards` documents verified 2026 cases) | Kernel patching with priority; reinforced isolation for what is untrusted | The node's kernel version against the current advisory |139140### 3.3 seccomp with judgement141142- **`RuntimeDefault` is the floor, not the goal.** It blocks the group of syscalls that no legitimate143 workload uses (`kexec_load`, `init_module`, `bpf` without the capability, etc.) with a very low144 breakage rate. **Verify it is actually enabled**: the Kubernetes default without `--seccomp-default`145 is `Unconfined`, and that is the domain's most frequent silent failure.146- **The default profile differs between runtimes** (containerd vs CRI-O), between versions and between147 architectures. A profile tested on x86_64 is not tested on arm64.148- **A bespoke profile**: only for high-value workloads or ones exposed to untrusted input. It is149 generated from **real observation** (recording the workload's syscalls while it exercises its full150 path: start-up, traffic, log rotation, shutdown), not from a theoretical list.151- **A denylist, not an allowlist, unless you can maintain it**: a strict allowlist breaks with152 every update of the runtime, libc or the language itself. If you adopt it, take on the maintenance153 as part of the image's lifecycle.154- **`SCMP_ACT_LOG` before `SCMP_ACT_ERRNO`**: the new profile is deployed first in logging mode over155 real traffic, what it would have blocked is reviewed, and only then is it enforced.156 Skipping this phase is scheduling an outage.157- The profile is **versioned code** alongside the workload, with its test (§4). A profile that only158 exists on one node is drift.159160### 3.4 Runtime detection161162- **eBPF is the technology, not the control.** It gives visibility of syscalls and kernel events at low163 cost and with no modules. But **loading eBPF programs is a near-root capability**: `CAP_BPF` (plus164 `CAP_PERFMON`/`CAP_NET_ADMIN` depending on the program type) allows instrumenting the entire kernel.165 Hard rules:166 - `kernel.unprivileged_bpf_disabled = 1` on all nodes, plus JIT hardening167 (`net.core.bpf_jit_harden`), verified by the node baseline (`linux-hardening-standards`).168 - **An explicit inventory of who holds `CAP_BPF`/`CAP_SYS_ADMIN`**: it is almost always an169 observability or security DaemonSet. Each one is a supply chain target of the highest170 value: a compromised agent **can selectively silence its own alerts and keep looking171 healthy**.172 - **Loading a new eBPF program is itself a signal to detect.** An eBPF rootkit filters the173 event stream your agent reads, which will faithfully report already manipulated data: that is why174 you need a second independent source (the node's auditd, runtime telemetry, control plane).175 - A recent CVE verified as a risk class: **CVE-2026-64036** (OOB in the176 `css_rstat_updated()` path via a BPF kfunc, 7.8) — a repeated pattern of a kfunc/verifier bug177 reachable by whoever can already load programs.178- **Signals that really matter** (this skill decides *what* should fire; the rule's lifecycle179 belongs to `detection-engineering-standards`):180 - **Execution of a shell or interpreter inside a container** that does not have it as its entrypoint —181 the domain's highest value/noise ratio signal.182 - **Writes to the container's binaries or system paths** (`/bin`, `/usr`, `/lib`) and183 any write at all if the rootfs is read-only (impossible by design ⇒ a finding).184 - **A change to the capability set or to `no_new_privs`** of an already running process; unexpected185 use of `setuid`/`setgid`.186 - **Access to masked `/proc` and `/sys`**, to the runtime's `/var/run/*.sock`, or to the node's187 filesystem from a container.188 - **An outbound connection to an unforeseen destination** (C2, exfiltration, mining) and anomalous189 DNS resolution.190 - **Execution of a binary that did not come in the image** (drift, §3.5), downloading and executing191 in memory, `curl|sh`.192 - **Reading credentials**: the ServiceAccount token, `/var/lib/kubelet`, the provider's IMDS.193 - **Disabling controls**: MAC moved to permissive, an AppArmor `unconfined` profile, seccomp194 removed, the detection agent stopped.195- **Before writing your own rules, exhaust the maintained ruleset.** Falco ships community-reviewed196 rules; the real work is per-environment *tuning*, not creativity. Every rule of your own197 is born with its firing test (§4).198- **Detection without response is decoration.** Every high-confidence signal has a destination199 (`observability-standards`), an owner and an action — isolate the Pod, capture (§3.6), escalate.200 Without that, the agent only burns CPU.201202### 3.5 Immutable container and drift203204- The contract is: **the image is the container**. `readOnlyRootFilesystem: true`, writes only to205 declared volumes, zero package installation at runtime, zero operator `exec` in production206 outside break-glass.207- **A container that is modified at runtime is a finding**, not an operational anomaly: either it is a208 compromise, or it is a deployment practice that must be corrected. Both are investigated.209- The comparison *declared image ↔ running process* (an executed binary that is not in the210 layers, a new file in system paths) is a high-value, low-noise detection.211- `kubectl exec`/`podman exec` in production: logged, attributed to a person and alerted. It is at212 once a legitimate emergency tool and the most convenient technique for an attacker with213 control plane credentials.214215### 3.6 Container and node forensics: the evidence evaporates by design216217- **The problem**: the orchestrator **reschedules or restarts the Pod**, and with it go memory,218 processes, temporary files and the writable filesystem itself. In an incident, the platform's219 default behaviour **destroys the evidence** while it "recovers".220- **Golden rule**: on suspicion, **capture before containing**, and contain without deleting. Isolating221 the Pod's network and marking the node (`cordon`) preserves more than killing the Pod. The order of222 volatility and the chain of custody belong to `incident-response-forensics-standards`; **what223 follows is what artifact exists in this domain and how long it lasts**.224- **Forensic checkpoint (CRIU)**: the kubelet's checkpoint API creates a stateful copy225 (memory, descriptors, sockets) **without the container noticing**, to analyse it in an isolated226 environment. Verified Aug 2026: **alpha in v1.25, beta and enabled by default since v1.30**;227 it is invoked with a `POST` to the kubelet (`/checkpoint/<ns>/<pod>/<container>`, access restricted to228 cluster administrators) and inspected with **`checkpointctl`**. The **restore is deliberately not in229 the kubelet**: it happens outside Kubernetes, in the container engine. **A checkpoint230 contains secrets and personal data in memory: treat it with the same control as a RAM231 image.**232- **Artifacts that do survive and must be collected**: runtime (containerd/CRI-O) and233 kubelet logs on the node, the host's journal and auditd, control plane and API server events,234 detection agent (eBPF) records, image layers and the real digest executed, mounts and235 persistent volumes, and a disk/memory image of the **node** if the compromise is a node compromise.236- **If the compromise is of the node, the whole node is evidence and it is no longer trusted**: it is237 isolated, preserved and **rebuilt from a trusted source**; it is not "cleaned". Every credential that238 passed through it (ServiceAccount tokens, IMDS credentials, mounted secrets) is considered239 compromised and rotated.240- **Rehearse it.** A capture procedure nobody has ever executed does not work on the day it is241 needed, because by then the Pod has been rescheduled three times.242243### 3.7 Supply chain at execution time244245- Admission validates **what is requested**; the node runs **what is finally downloaded**.246 Signature verification also in the engine (`policy.json` with `sigstoreSigned` in Podman/CRI-O)247 for critical workloads, and **pin by digest** everywhere: a tag is mutable by definition.248- **Current precedent**: the compromise of `trivy-action`/`setup-trivy` in **March 2026** and the249 2026 tag poisoning campaign (documented in `offensive-security-standards`)250 demonstrate that **the security tool is a priority target** — it runs in CI and on the251 nodes, with high privileges, by design. Every runtime agent is pinned by digest, its signature is252 verified and its advisory channel is followed as if it were part of the kernel.253- A private registry with pull-through; never a direct pull from public registries in production.254255### 3.8 Node hardening: the boundary256257The node belongs to `linux-hardening-standards`, and its baseline (CIS/STIG, `sysctl`, auditd, mounts,258MAC enforcing) is a prerequisite, not a complement. **On this side there remain only** the invariants259that exist because of the fact of running containers:260261- cgroups **v2**, kernel ≥6.3 if user namespaces are used, MAC enforcing and active for containers.262- `unprivileged_bpf_disabled=1` + JIT hardening; an inventory of `CAP_BPF` holders.263- The runtime socket with minimum permissions and **never** exposed over the network or mounted in264 containers.265- Nodes segmented by trust: hostile or multi-tenant workloads on **their own node pool**, with266 their reinforced `RuntimeClass`. Mixing hostile tenants with internal workloads on the same node267 nullifies any policy you write on top.268- A node image that is **immutable and rebuildable**, updated by replacement, not by live patching.269270### 3.9 Applicable benchmarks271272- **CIS Kubernetes Benchmark v1.11.0**, audited with `kube-bench` (`cis-1.11` profile). Verified273 Aug 2026: that profile **covers Kubernetes 1.29–1.32**; for newer clusters it is still the274 practical reference, but **the coverage is not official** — state that when reporting. **Never quote275 "the Kubernetes CIS" generically: CIS goes by product version.**276- **CIS Docker Benchmark**, audited with `docker-bench` (which picks the set according to the daemon277 version). **Declared gap (§8)**: the current version number was not confirmed in Aug 2026.278- **NSA/CISA Kubernetes Hardening Guidance**: verified Aug 2026, **the current version is still279 1.2, of 29 Aug 2022**, with no later revision located. It is a guide of *criteria* (it explains the280 why and the attacker's view) and it **complements** CIS, which is about *configuration*. Use it as a281 design argument, not as an automatable checklist, and **bear its age in mind**: it does not cover282 user namespaces GA, cgroups v2 or the current generation of eBPF detection.283- The benchmark **measures**, it does not protect. A high score with a mounted `docker.sock` is still a284 compromise one step away.285286## 4. Quality gates (they break the build or the deployment)2872881. **A seccomp profile tested, not assumed.** Functional gate: the workload exercises its full path289 (start-up, real traffic, log rotation, shutdown, restart) with the profile active and **without a290 single unexpected denied syscall**. That the container starts proves nothing — the missing syscalls291 show up in the rare case, in production, at 3 a.m. A mandatory prior phase in logging mode (§3.3).2922. **Runtime policy verified in CI and on the node.** The pipeline fails on `--privileged`,293 a mounted runtime socket, a `hostPath` to a sensitive path, `hostPID`/`hostNetwork`/`hostIPC`,294 a vetoed capability, the absence of `seccompProfile` or `readOnlyRootFilesystem: false` without a295 signed exception. The same policy runs at admission (`kubernetes-standards`) and **as a verification296 of the node's real state**, because what was admitted is not always what runs.2973. **A test that the detection fires — adversarial validation.** A gate of its own and non-negotiable:298 in a test environment, benign actions equivalent to the watched techniques are executed (opening a299 shell in a container that does not have one, writing to `/usr/bin`, reading a masked `/proc`300 file, mounting a socket, initiating an unexpected outbound connection) and **it is verified that the301 alert reaches its destination with the correct context**. A rule that has never been seen to fire is302 not deployed, it is written. It is re-run after every update of the agent, the kernel or the runtime.3034. **Runtime and kernel versions verified against the current advisory** on every node in the304 inventory, as a deployment gate. A node with runc below the minimum of §2 does not accept305 workloads.3065. **A forensic capture test rehearsed**: a checkpoint of a test Pod, inspection with307 `checkpointctl`, and collection of the §3.6 artifacts with their time measured. It is rehearsed at308 least once every six months and after every runtime change.3096. **An inventory of exceptions with an owner and an expiry**: every privileged Pod, every added310 capability, every `hostPath`, every container with MAC disabled. Without an owner and without a311 date, the exception is withdrawn.3127. **A benchmark run and trended** (`kube-bench`, `docker-bench`): the value is in the313 **time series and in the handling of the exceptions**, not in the number.314315## 5. Security: what this protects and what it does not316317- **What it gives you**: it turns "RCE in the application" into "RCE inside an unprivileged process,318 with no dangerous syscalls, with no access to the node and **with someone watching**". It is319 containment plus detection; neither of the two alone is enough.320- **What it does not give you**: it does not fix the application's vulnerability (`appsec-standards`)321 or a kernel flaw. **Against a kernel 0-day, the only real isolation is the one that does not share a322 kernel** (Kata/microVM) or does not share a node.323- **Hostile multi-tenant**: if you run third-party code, namespace isolation **is not324 sufficient by design**. The correct posture is reinforced isolation + a dedicated node + detection,325 and treating the escape as a scenario, not as a hypothesis.326- **The security agent is attack surface of the highest value**: privileged, on all327 nodes, with kernel access. It is pinned by digest, signed, verified and its own328 health is watched — a downed or silenced agent is a high-severity alert, not an329 observability incident.330- **Secrets at runtime**: environment variables visible in `/proc/<pid>/environ` (and therefore to331 anyone with `hostPID` or `CAP_SYS_PTRACE`), ServiceAccount tokens mounted by default,332 IMDS credentials reachable from the Pod. Prefer a file over an environment variable,333 `automountServiceAccountToken: false` and blocking IMDS access from workloads. Custody and334 rotation: `secrets-management-standards`.335- **Disabling a control "to make it work" is a risk decision**, and it is taken as such: with336 an owner, a ticket, an expiry and compensation. Never on the fly and never as a silent default.337338## 6. Performance and operability339340- **Realistic cost**: seccomp `RuntimeDefault` and MAC are practically free. gVisor pays in341 syscall-intensive work and I/O; Kata pays in start-up and memory per Pod. Rootless pays ~25-30 % of342 start-up and `fuse-overlayfs`. None of those costs justifies running as root: measure them on your343 workload before dismissing them on rumour.344- **Detection has a cost and noise**: size the agent's CPU and event volume, and treat it345 as a platform workload with its own limits. An unbounded agent that saturates the node is an346 availability incident caused by security. Verified Aug 2026: **Tracee consumes347 notably more than Falco or Tetragon** in high-volume environments (2-4× versus Tetragon according to348 reports) and its default configuration generates an overwhelming event volume — **it is deployed349 with filtering from day one, not afterwards**.350- **A written and tested runbook** for: a suspect Pod (isolate, capture, escalate), a suspect node351 (cordon, preserve, rebuild), a downed agent, and a rollback of a seccomp profile that breaks352 production. Without a runbook, the 3 a.m. incident ends in `--privileged`.353- **Rollback**: every runtime hardening step (a new profile, a reinforced `RuntimeClass`, userns) is354 deployed in rings with its reversal tested.355- **Kernel and runtime changes break profiles and agents**: every node update356 re-runs gates 1, 3 and 4 of §4. It is the number one cause of detection that stops working in357 silence.358359## 7. Sustainability and prohibitions360361**Cadence**362- **Runtime advisories (runc, crun, containerd, CRI-O, gVisor, Kata) actively tracked**: it is363 the channel through which the escape arrives. Review at least monthly and immediate reaction to364 anything critical.365- Node kernel: top patching priority when the finding allows escape or MAC bypass.366- Falco/Tetragon/Tracee/KubeArmor: version and ruleset reviewed quarterly; the ruleset ages367 faster than the binary.368- Quarterly review of the exceptions inventory (§4.6) and of the inventory of holders of369 `CAP_BPF`/`CAP_SYS_ADMIN`.370- Scheduled rebuilding of the node image even if nothing changes.371372**FORBIDDEN** (an automatic gate wherever possible)373- ❌ **Mounting the runtime socket** (`docker.sock`, `containerd.sock`, `crio.sock`) in a374 container. It is granting root on the node, under another name.375- ❌ **`--privileged` / `privileged: true`** without a signed, bounded, expiring exception and on a376 dedicated node.377- ❌ **`hostPath` to sensitive paths** on the node (`/`, `/etc`, `/proc`, `/sys`, `/var/run`,378 `/var/lib/kubelet`, block devices), and any writable `hostPath` without justification.379- ❌ `hostPID`, `hostNetwork` or `hostIPC` in application workloads.380- ❌ Running as **root** (UID 0) without written justification; and root **with** `hostPath` or added381 capabilities, never.382- ❌ `CAP_SYS_MODULE`, `CAP_SYS_RAWIO`; `CAP_SYS_ADMIN`, `CAP_BPF`, `CAP_DAC_READ_SEARCH` and383 `CAP_SYS_PTRACE` outside approved infrastructure agents.384- ❌ **Disabling seccomp or the MAC "to make it work"** (`seccompProfile: Unconfined`,385 `--security-opt seccomp=unconfined`, `label=disable`, AppArmor `unconfined`). The correct path386 is a bespoke profile (seccomp, here) or a bounded policy (MAC, `selinux-standards`).387- ❌ `procMount: Unmasked` or mounting the host's `/proc`/`/sys`.388- ❌ **A container without runtime detection in a multi-tenant environment** or one that runs untrusted389 code. Without detection there is no evidence that the isolation holds.390- ❌ Running untrusted code with a shared runtime (`runc`/`crun`) without a dedicated node.391- ❌ Nodes on **cgroups v1**, or with a runtime below the patched minimum version of §2.392- ❌ **Modifying the container at runtime**: installing packages, hot patching, operator `exec` as393 routine practice, a writable rootfs with no reason.394- ❌ An image by mutable tag or without signature verification on the path to production.395- ❌ **Killing or restarting a suspect Pod before capturing evidence**; deleting the compromised396 node "to recover" without preserving it.397- ❌ Re-enabling `unprivileged_bpf_disabled=0` or handing out `CAP_BPF` without an inventory.398- ❌ A detection agent deployed without a firing test (§4.3), or with alerts without an owner or a399 destination.400- ❌ Pinning runtime versions, feature gate states or benchmark versions **from memory**, without401 the verification of §8.402403### Quick review checklist (a workload entering production)404405- [ ] Runtime on a patched version; node on cgroups v2; kernel up to date and ≥6.3 if there is userns.406- [ ] Rootless or `hostUsers: false` where the node allows it; if not, a written reason.407- [ ] `drop: ALL` + a justified allowlist; `allowPrivilegeEscalation: false`; not root or a signed exception.408- [ ] An explicit `seccompProfile` and **verified active on the node**; a bespoke profile tested if applicable.409- [ ] MAC active and enforcing for the container (policy: `selinux-standards`).410- [ ] No runtime socket, no sensitive `hostPath`, no `hostPID`/`hostNetwork`/`hostIPC`, no `privileged`.411- [ ] `readOnlyRootFilesystem: true` + declared volumes; drift detection active.412- [ ] Image by digest and signature verified; runtime agent pinned by digest and signed.413- [ ] Runtime detection deployed, with a **passed firing test** and alerts with an owner.414- [ ] Reinforced isolation (`RuntimeClass`) if the workload is untrusted or hostile multi-tenant.415- [ ] A forensic capture procedure rehearsed and accessible in the runbook.416417## 8. Mandatory web verification418419Before pinning any version, feature state or benchmark number, **look it up — do not420remember it**:4214221. **Runtime escape CVEs**: it is the datum that expires fastest in the whole document. Verified423 Aug 2026: the **runc triad of Nov 2025** (`CVE-2025-31133`, `CVE-2025-52565`, `CVE-2025-52881`)424 is still the most recent significant set; patched in **runc 1.2.8 / 1.3.3 /425 1.4.0-rc.3+** and **containerd 1.6.39+ / 1.7.28-2+**. **Declared gaps**: the426 **exact stable version of runc, crun, containerd and CRI-O** was not verified in Aug 2026, nor could427 the isolated report of *active exploitation in Jun 2026* be confirmed (one source asserts it, the428 rest do not) — treat it as unconfirmed and consult the upstream advisory.4292. **The state of user namespaces in Kubernetes** (verified Aug 2026: **GA in v1.36**, Apr 2026;430 beta enabled by default since 1.33; **requires kernel ≥6.3**, which many managed nodes still431 do not have). Check your cluster version and the node's `uname -r`: this changes per release.4323. **`SeccompDefault`**: verified Aug 2026 that it **is still a kubelet feature gate + the433 `--seccomp-default` flag**, with no cluster default. **Declared gap**: it was not confirmed whether it434 has graduated to GA or whether there is a date to make it the default; consult **KEP-2413** and your435 version's seccomp reference.4364. **Forensic checkpoint**: verified Aug 2026 **beta and enabled by default since v1.30**. **Declared437 gap**: **its state in v1.36 could not be confirmed** (still beta, already GA?). Read it in your438 version's release notes before basing an incident procedure on it.4395. **The state of the detection tools**: verified Aug 2026 — **Falco graduated in CNCF440 (29 Feb 2024)**, **v0.44.1** (Jun 2026), with the legacy eBPF probe, gVisor and gRPC documentation441 withdrawn in May 2026; **Tetragon** active as a Cilium subproject in CNCF; **Tracee** active442 (Aqua) with a notably higher cost; **KubeArmor** in **CNCF Sandbox**. **Declared gaps**:443 **the current versions of Tetragon, Tracee and KubeArmor were not verified**, nor were hard444 maintenance health signals (commit cadence, number of maintainers, CNCF level changes in445 2026). Check it in their repos and in the CNCF landscape before betting a platform on it.4466. **Reinforced isolation versions**: verified Aug 2026 **Kata Containers 4.0.0**447 (20 Jul 2026), with a guest kernel bump for **CVE-2026-31431**. **Declared gap**: **the448 current version of gVisor was not verified** (it publishes very frequent releases) nor that of `crun`.4497. **Benchmarks**: verified Aug 2026 — **CIS Kubernetes Benchmark v1.11.0** (`kube-bench` profile450 `cis-1.11`, official coverage **K8s 1.29-1.32**) and **NSA/CISA Kubernetes Hardening Guidance451 v1.2 of 29 Aug 2022** as the current version, with no later revision found. **Declared452 gaps**: **the current version of the CIS Docker Benchmark could not be confirmed** (look it up at453 `cisecurity.org/benchmark/docker`, which only lists the supported ones), and **it was not checked454 whether a CIS Kubernetes Benchmark later than v1.11.0 already exists**.4558. **eBPF hardening and its CVEs**: verified Aug 2026 **CVE-2026-64036** (OOB via the456 `css_rstat_updated()` kfunc, 7.8 CVSS v3.1 according to kernel.org). Check your distro's tracker for457 the patch state and look for later verifier/kfunc CVEs before considering a kernel version safe.4589. **Supply chain compromises in security tools**: a verified precedent from the459 catalogue — **`trivy-action`/`setup-trivy`, March 2026**. Before introducing *any* new agent460 or scanner, look for recent incidents of the project: in this domain the tool runs461 privileged on every node.46210. **Managed node provider advisories** (EKS/AKS/GKE) for the node image version463 you are running: the runtime patch arrives through there, not through your pipeline.464465If the web contradicts this document, **the web wins** — flag the discrepancy.