Lion Cipher Lab
Sandbox untrusted code with gVisor (runsc) — a userspace kernel between the container and the host
- Cyber / Security Engineer
- Advanced
- ~75 min
- Free · no account required
Why it matters
The default way a container runs — with runc, the standard runtime — puts the container's system calls straight onto the HOST kernel. That is efficient, and for code you trust it is fine. But the host kernel is an enormous, monolithic attack surface: millions of lines of C reachable through hundreds of syscalls, and a single exploitable kernel bug reached from inside a container is a container escape onto the host. When you have to run code you genuinely do NOT trust — a customer's uploaded binary, an untrusted build step, a multi-tenant workload, a malware sample you want to observe — 'a…
What you'll learn
- explaining in plain words what a container RUNTIME is (the low-level program that actually starts a container), and the difference between runc — the default, which runs the container directly on the host Linux kernel — and runsc, gVisor's runtime, which does not
- defining a SYSTEM CALL (a syscall): the request a program makes to ask the kernel to do something privileged — open a file, send a packet, start a process — and why the syscall boundary is exactly where a container either does or does not touch the host kernel
- describing gVisor's model specifically: a userspace process called the Sentry that acts as an APPLICATION KERNEL — it intercepts the container's syscalls and services them itself in userspace, so the container's syscalls hit the Sentry, not the host kernel
- installing gVisor (the runsc binary) on Linux and registering it as a Docker runtime with `runsc install`, so you can select it per-container with `--runtime=runsc`
- proving the boundary by MEASURING: running the identical image once under runc and once under runsc and recording a concrete, checkable difference — dmesg prints gVisor's own boot banner under runsc; `uname -a` and /proc/version report a gVisor kernel rather than the host's
- reading the result correctly: under runc the workload uses the host kernel directly; under runsc the host kernel is shielded behind the Sentry, which is what shrinks the host kernel's attack surface for untrusted code
- understanding WHERE the Sentry itself is constrained (it reaches the host through a minimized, seccomp-restricted set of syscalls, with a small Gofer sidecar for filesystem access) so you can articulate why this reduces — not eliminates — host-kernel exposure
- the honest negatives: gVisor adds performance overhead (syscalls now traverse extra layers of software) and has real COMPATIBILITY limits (some syscalls/features are unimplemented and some workloads break); it is NOT a full virtual machine and not a silver bullet
- knowing what gVisor does NOT protect: it shields the host kernel, not the application inside the sandbox — an app-logic bug, an SSRF, a leaked secret, or a supply-chain backdoor is just as exploitable inside a gVisor sandbox as outside one
- the engineering judgment: when gVisor's isolation is worth its overhead (running genuinely untrusted or multi-tenant code), when a real VM (a hardware boundary) is the right call instead, and when plain seccomp/caps on runc is already enough
Steps in this lab
- First, the idea — a kernel that lives in userspace
- Set up: install gVisor and register runsc as a Docker runtime
- Smoke test: does anything even run under runsc?
- BEFORE — with default runc, the container is on the HOST kernel
- AFTER — the SAME image under runsc reports a gVisor kernel
- Why the strings differ — the Sentry serviced those syscalls
- Poke the boundary — see the emulated view, not the host's
- Honest costs — overhead and compatibility are real
- What gVisor does NOT protect — and when to reach for what