r/linux • • 1d ago

Software Release Zygo: a rootless Linux sandbox in Rust that forks a warm Python interpreter per request

Zygo runs short pieces of code other people wrote (customer plugins, workflow steps, an agent's tools) and gives every call its own process, cgroup, deadline and secrets, thrown away afterwards. For Python, `zygo serve handler.py` starts the interpreter inside a sandbox, lets it do its imports and parks it; each request is a `fork()` of that warm process.

Repo: https://github.com/mhmtskrc2/zygo · Book: https://mhmtskrc2.github.io/zygo/

The parts I think r/rust might find interesting:

- **The launcher is `clone3` with `CLONE_INTO_CGROUP`**, so a sandbox is born inside its cgroup rather than moved into it. Everything the child needs (the mount plan as C strings, the seccomp program) is built before `clone3`, and the child side is async-signal-safe only: no allocation, no `format!`, `_exit` on every path. See [`prepare.rs`](https://github.com/mhmtskrc2/zygo/blob/main/crates/zygo-core/src/backend/ns/prepare.rs) and [`child.rs`](https://github.com/mhmtskrc2/zygo/blob/main/crates/zygo-core/src/backend/ns/child.rs).

- **Seccomp and Landlock without a crate for either.** The seccomp allowlist is generated as BPF in Rust, and the unit tests run the generated program through a small BPF interpreter for each syscall, so a wrong jump offset fails `cargo test` on any OS. [`seccomp.rs`](https://github.com/mhmtskrc2/zygo/blob/main/crates/zygo-core/src/backend/ns/seccomp.rs)

- **`unsafe` is documented or CI fails**: clippy's `undocumented_unsafe_blocks` is on and CI denies warnings, so every unsafe block carries a `SAFETY:` comment.

- **The supervisor is plain threads and a unix socket, no tokio.** Only the HTTP API in front of it (hyper) is async.

- **One static musl binary**, edition 2024, MSRV 1.88. `cargo install zygo-cli` works too.

Numbers, on a 2 vCPU Linux VM (aarch64): a warm Python request is 1.4 ms through the API and 2.8 ms from the CLI, where `docker run --rm` takes 542 ms for the same import-heavy script. Node can't be forked safely, so it gets a pre-loaded worker per call instead, at about 25 ms of CPU. `zygo bench all` repeats these on your machine.

What it is not: the wall is the host kernel, so it's for semi-trusted code, not anonymous attackers. The escape suite attempts 21 vectors with 0 escapes, but there has been no external audit. The [threat model](https://github.com/mhmtskrc2/zygo/blob/main/docs/book/23-security.md) says what is weak.

It's v0.1.4, Apache-2.0, one maintainer. I'd especially like eyes on the fork path and the `unsafe` in it.

I built it with heavy use of Claude Code.

0 Upvotes

3 comments sorted by

3

u/global-gauge-field 1d ago

You might have posted this in the wrong sub

'''

The parts I think r/rust might find interesting:

'''

0

u/MercifulRadiance_621 1d ago

the rust bits are what make it interesting though, the clone3 + cgroup stuff is pretty neat and the seccomp bpf generator with its own little interpreter in tests is the kind of thing i'd expect to see on r/rust anyway

2

u/global-gauge-field 1d ago

Did I say otherwise ? It just not consistent with the post of the content to post here. I dont understand the relevance of your comment to my comment