Sealed Sandbox Case Study: A Script That Ignores Proxy Settings Tries Three Ways Out and Finds None
A script that ignores proxy settings tries three ways out with a stolen deploy token: HTTPS by name with the proxy switched off, a direct connection to the attacker’s address, and a local service on a Unix socket that connects for whoever asks. Inside vpnw’s sealed sandbox none of them works.
The case was recorded with VPN Works 0.2.0’s demo kit; the screenshot is from that recording. The sandbox is the same code in 0.4.0, and its tests now try 16 ways out, not three.
A program under vpnw normally goes out through the proxy settings vpnw gives it. Most tools honor them. A script told to leak something has every reason not to. Without vpnw, the first way out worked. Under vpnw each failed at a different wall: the name couldn’t be looked up, the address was unreachable, and the socket wasn’t permitted. vpnw itself never saw a connection.

How It’s Sealed
On Linux, vpnw starts the program in a network namespace of its own. The only interface in it is loopback, and the only things listening there are vpnw’s two proxy ports. There’s no default route, so a direct connection fails with “network is unreachable” at once, and a DNS lookup has nowhere to go.
A seccomp filter closes what a namespace leaves open: Unix sockets, so the program can’t ask a local service like Docker to connect for it; io_uring, which can make sockets without calling socket(); and system calls through a second system call table.
The third way is the one people forget. A Unix socket lives in the file system, outside any network namespace, and some local services act on whatever their socket tells them. The Docker socket is the classic case: a program that can reach it can ask Docker to start a container on the host’s own network. The filter makes the script’s socket call fail before it gets that far.
Child processes inherit all of it. A script that starts another program to do its connecting gets nowhere either.
Tested Way by Way
The tests try each way out from inside the sandbox, and from outside as a control where that makes sense, so “blocked” means the sandbox blocked it:
| Way out | Result |
|---|---|
| TCP to loopback, this machine’s address, a public address, a private address, cloud metadata | blocked |
| UDP to this machine and to a public DNS resolver | blocked |
| A DNS lookup through the system resolver | blocked |
| A raw IP socket | blocked |
| A child process connecting on its own | blocked |
| An abstract Unix socket, and one in the file system like Docker’s | blocked |
| io_uring | blocked |
| A system call through the second table | blocked |
| TCP over IPv6, to loopback and to a public address | blocked |
| ping | blocked |
17 of 17 were blocked, in VPN Works 0.4.1, on a GitHub Actions Linux runner. The full matrix
When a Program Needs a Socket
Some programs legitimately need a local socket: ssh-agent, a D-Bus session. --allow-unix-sockets lifts the socket filter for that run. The network stays sealed.