Skip to content

feat(tool-host): route shell through an opt-in bubblewrap sandbox - #50

Open
arcuru-bot wants to merge 1 commit into
arcuru:mainfrom
arcuru-bot:feat/bwrap-toolhost
Open

feat(tool-host): route shell through an opt-in bubblewrap sandbox#50
arcuru-bot wants to merge 1 commit into
arcuru:mainfrom
arcuru-bot:feat/bwrap-toolhost

Conversation

@arcuru-bot

Copy link
Copy Markdown
Contributor

What

The BubblewrapToolHost has been in-tree since it landed but no config could select it, so every shell command still ran in-process. This wires it up behind tool_host: bubblewrap (default stays native) and makes the sandbox actually refuse escapes.

Changes

  • Config selection: new tool_host: native | bubblewrap top-level option, wired in build to construct either NativeToolHost or BubblewrapToolHost.
  • /proc leak closed: the profile mounted /proc without --unshare-pid, exposing the host's PID namespace (systemd's cmdline, the host process list, and a /proc/<pid>/root route back to the host filesystem). /proc is no longer mounted — the shell tool doesn't need it and the host process list must not be visible.
  • Denial tests: integration tests run a real bwrap sandbox and assert the negative case — network connects refused with a stderr diagnostic, writes outside the working dir (and to read-only system dirs) fail, writes inside the working dir land on the host, and /proc is absent. They skip cleanly where bwrap or unprivileged user namespaces are unavailable.
  • Packaging: bwrap added to the Linux test/dev/wrapper inputs (guarded with stdenv.isLinux so Darwin still evaluates).

Verification

  • just nix full passes (lint, treefmt, test, doc, build), with the bwrap integration tests running inside the Nix sandbox (not skipped).
  • Shell sandbox overhead measured at ~5 ms per invocation over native sh -c true.

Notes

  • Only shell is sandboxed; web_fetch, file read/write, and HTTP capabilities still fall through to native execution (unchanged from the existing design).
  • Merge is yours (rebase-and-merge).

The BubblewrapToolHost has sat in-tree since it landed but no config
could select it, so every shell command still ran in-process. Wire it
up behind `tool_host: bubblewrap` (default stays native) and make the
sandbox actually refuse escapes instead of just passing happy-path
tests.

The profile already isolated the filesystem (empty root + read-only
system dirs + a read-write working dir) and network (`--unshare-net`),
but it mounted `/proc` without `--unshare-pid`, which exposed the
host's PID namespace — including systemd's cmdline and a
`/proc/<pid>/root` route back to the host filesystem. Drop the `/proc`
mount entirely; the shell tool doesn't need it and the host process
list must not be visible.

Add integration tests that run a real bwrap sandbox and assert the
negative case: network connects are refused with a diagnostic on
stderr, writes outside the working dir (and to read-only system dirs)
fail, writes inside the working dir land on the host, and `/proc` is
absent. They skip cleanly where bwrap or unprivileged user namespaces
are unavailable. bwrap is added to the Linux test/dev/wrapper inputs
(guarded so Darwin still evaluates).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant