Sandboxes & runtime detection

gVisor, Kata, and catching what still gets through.

Advanced12 min · lesson 25 of 25

When a workload is untrusted enough that a shared kernel is an unacceptable risk — multi-tenant platforms, customer-supplied code, anything you did not write — a sandboxed runtime gives that container its own kernel boundary. The two mainstream options trade a little performance for a lot of isolation, and both slot in through the standard runtime interface, so the workload spec barely changes.

How much kernel is shared
standard runtime
runc
syscalls hit the host kernel directly
sandboxed
gVisor (runsc)
user-space kernel intercepts syscalls
Kata Containers
lightweight VM, its own real kernel
A kernel-level escape from a sandboxed container lands in the sandbox (the user-space kernel or the VM), not on the host.

gVisor runs a user-space kernel (runsc) that intercepts a container’s syscalls and services most of them itself, so very few ever reach the host kernel — dramatically shrinking the attack surface a kernel exploit could hit. Kata Containers goes further and wraps each container in a lightweight VM with its own real kernel, approaching VM-grade isolation with container-like ergonomics. You select either via a runtime, exactly like choosing runc.

terminal
# run a workload under gVisor instead of runc (runtime installed on the host)
$ docker run --rm --runtime=runsc alpine dmesg | head -1
... Starting gVisor... # a user-space kernel, not the host’s
# Kubernetes: a RuntimeClass named "gvisor"/"kata", then runtimeClassName on the pod

Catch what still gets through: runtime detection

Prevention can fail, so pair it with detection. Falco taps the kernel and alerts in real time on the exact behaviors an escape produces — a shell spawned in a container, a write under a binary directory, a mount attempt, an unexpected connection, access to /var/run/docker.sock. On a hardened, distroless, non-root container these are near-zero-false-positive signals precisely because the container should never do them, which turns the whole hardening effort into a sharp tripwire.

falco-rule.yaml
- rule: Docker socket touched from a container
desc: A process in a container accessed the Docker socket (escape attempt)
condition: >
open_read and container and fd.name = /var/run/docker.sock
output: >
Container touched docker.sock (container=%container.name
image=%container.image.repository cmd=%proc.cmdline)
priority: CRITICAL
Sandboxes are for untrusted code, not everything
gVisor and Kata cost some performance and syscall compatibility, so apply them where the threat model justifies it — untrusted, multi-tenant, or high-blast-radius workloads — not as a blanket default. For your own trusted services, the layered controls from this course (minimal image, non-root, cap-drop, seccomp, read-only, rootless) are the right floor; reach for a sandbox when you genuinely cannot trust what runs inside.