Hey folks, been tinkering with gVisor for a few months now to sandbox some of our internal monitoring agents. A question that always comes up is: if gVisor is just a user-space kernel (the "sentry"), how does it actually stop a container escape? Isn't userland just... software?
Great question! The key is that gVisor isn't just another library in your container. It's a *distinct, isolated process* that sits between your app and the real host kernel.
Think of it like this:
* In a normal container, your app's system calls (like `read`, `write`, `open`) go directly to the **host kernel**. A kernel exploit in the container can compromise the host.
* With gVisor, your app's system calls go to the **gVisor sentry** (the user-space kernel). The sentry then makes its own, much more limited, set of calls to the real host kernel.
So the security boundary shifts. An attacker has to:
1. Break out of the application into the gVisor sentry's runtime (which is written in Go and designed to be safe).
2. Then break out of *that* sentry process to attack the host kernel.
Here's a trivial `runsc` (gVisor's runtime) config snippet for a sandboxed container:
```json
{
"runtimeArgs": [
"--network=none",
"--platform=ptrace"
]
}
```
The "real security delta" is that gVisor drastically reduces the host kernel's attack surface exposed to the container. It's not a perfect VM-level isolation (like Firecracker), but it's a massive step up from plain containers for many workloads, especially for running less-trusted code like external agents. The tradeoff? There's a performance hit, as you're adding a layer of indirection for every syscall.
What's everyone else's experience? Found any gotchas with specific syscalls or networking setups?