The Security Sandwich
In one sentence: Because containers share the host kernel, the engine wraps every container in stacked filters — dropped capabilities, a seccomp syscall filter, and mandatory access control — so that "root in the container" is far less than root.
You already know
- From Lesson 2: one shared kernel is the speed win and the security risk.
- From Lesson 4: the USER namespace can map container-root to an unprivileged host user.
Root, divided and filtered
Modern kernels split root's power into ~40 capabilities (bind low ports, load kernel modules, change file ownership…). A container engine drops most by default and keeps a small safe set — a containerized "root" that can chown its own files but cannot load a kernel module. Below that sits seccomp, a per-process allowlist over the ~350 syscalls the kernel exposes; the default profile blocks the dangerous tail (kernel keyring, raw clock manipulation, obscure module calls). Around everything, an LSM (AppArmor/SELinux) enforces path- and label-based rules. Three independent layers: an attacker must get through all of them.
flowchart TB P["container process
('root' inside)"] CAP["capability check
is this power in the kept set?"] SEC["seccomp filter
is this syscall on the allowlist?"] LSM["LSM policy (AppArmor/SELinux)
may this process touch this path?"] K["kernel executes the call"] P --> SEC --> CAP --> LSM --> K ENG["engine
writes all three policies at create time"] -. configures .-> SEC ENG -. configures .-> CAP ENG -. configures .-> LSM
A blocked call, step by step
The layers are boring by design — each just answers allow/deny. Here a compromised container tries to load a kernel module, the classic escape route:
sequenceDiagram
participant A as Attacker (root in container)
participant S as seccomp
participant C as Capability check
participant K as Kernel
A->>S: syscall: init_module(evil.ko)
alt syscall not in allowlist
S-->>A: EPERM — blocked before the kernel logic runs
else allowlisted (custom profile)
S->>C: pass through
C-->>A: EPERM — CAP_SYS_MODULE was dropped
end
note over A,K: two independent layers must both fail
for the call to reach kernel logic
Defense in depth is the design principle to carry forward: never rely on one wall, because Lesson 39 will show you how each individual wall has been breached historically.
Anchor these
- Root is ~40 separable capabilities; containers keep only a safe subset.
- seccomp is a syscall allowlist; the LSM guards paths and labels — independent layers.
- The engine writes policy once at create time; the kernel enforces on every call.
See what actually stuck.
Take the practice scenarios now.