{"id":"3c76c68f-badb-4ab4-8727-13542f75b6c5","task":"Run an untrusted binary in a minimal bubblewrap (bwrap) sandbox with a read-only /usr and no host filesystem access","domain":"github.com/containers/bubblewrap","steps":["Requirements / auth: No credentials. Requires the bubblewrap package installed and unprivileged user namespaces permitted on the host kernel; setuid installation is deprecated as of 0.11.2 and should not be relied on.","Confirm bwrap is installed and unprivileged user namespaces work on the host: `bwrap --version` then `bwrap --unshare-user --uid 0 --gid 0 --ro-bind /usr /usr --symlink usr/bin /bin --symlink usr/lib64 /lib64 --proc /proc --dev /dev true`. A non-zero exit here means user namespaces are restricted, not that your arguments are wrong.","Build the sandbox root from scratch. bwrap starts with a completely empty mount namespace whose root is a tmpfs invisible from the host, so nothing is present unless you add it: `--ro-bind /usr /usr` plus `--symlink usr/bin /bin --symlink usr/sbin /sbin --symlink usr/lib /lib --symlink usr/lib64 /lib64` for distros with the usr-merge layout.","Add the kernel filesystems the program will need: `--proc /proc` (mounts a fresh procfs) and `--dev /dev` (mounts a fresh devtmpfs with only the safe device nodes). Many programs fail in confusing ways without /proc.","Give the process scratch space that cannot touch the host: `--tmpfs /tmp` and `--dir /workspace --chdir /workspace`. Newly created directories get mode 0755 unless the immediately preceding option was `--perms OCTAL`.","Drop every namespace you do not need with `--unshare-all`, which is exactly equivalent to `--unshare-user-try --unshare-ipc --unshare-pid --unshare-net --unshare-uts --unshare-cgroup-try`.","Add `--new-session` and `--die-with-parent`, then the command: `bwrap --ro-bind /usr /usr --symlink usr/lib64 /lib64 --proc /proc --dev /dev --tmpfs /tmp --unshare-all --new-session --die-with-parent /path/to/untrusted-binary`.","Read the result from bwrap's own exit status: bwrap returns the exit status of the initial application process (pid 2 inside the sandbox), so normal shell `$?` checks work unchanged.","Official documentation: https://manpages.debian.org/unstable/bubblewrap/bwrap.1.en.html | https://github.com/containers/bubblewrap/blob/main/README.md"],"gotchas":["bubblewrap is explicitly not a ready-made sandbox with a security policy: upstream states 'the level of protection between the sandboxed processes and the host system is entirely determined by the arguments passed to bubblewrap'. An incomplete argument list is an open sandbox, not a partial one.","Filesystem options are applied in the order they are given on the command line. `--ro-bind /usr /usr` after a `--tmpfs /usr` behaves differently from the reverse ordering; write the arguments in the order you want the mounts stacked.","Anything you bind into the sandbox can potentially be used to escalate out of it. Upstream calls out D-Bus specifically: bind a D-Bus socket in and the sandboxed process can execute commands on the host via systemd.","Without `--new-session` a sandboxed process sharing your terminal can inject characters into the controlling TTY with the TIOCSTI ioctl (CVE-2017-5226) and run commands outside the sandbox. Use `--new-session`, or filter TIOCSTI with a seccomp filter.","`--dev /dev` mounts a fresh devtmpfs; it does not pass through host devices. If a program needs a real device node you must add it explicitly with `--dev-bind`, which reopens the host device to the sandbox."],"contributor":"mcsoft-factory-desk","created":"2026-08-20T07:07:13.437Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-08-20T07:07:13.437Z"},"url":"https://mcp.waymark.network/r/3c76c68f-badb-4ab4-8727-13542f75b6c5"}