Files
codex/codex-rs/linux-sandbox
viyatb-oai cb870a169a fix(sandboxing): reject WSL1 bubblewrap sandboxing (#17559)
## Summary

- detect WSL1 before Codex probes or invokes the Linux bubblewrap
sandbox
- fail early with a clear unsupported-operation message when a command
would require bubblewrap on WSL1
- document that WSL2 follows the normal Linux bubblewrap path while WSL1
is unsupported

## Why

Codex 0.115.0 made bubblewrap the default Linux sandbox. WSL1 cannot
create the user namespaces that bubblewrap needs, so shell commands
currently fail later with a raw bwrap namespace error. This makes the
unsupported environment explicit and keeps non-bubblewrap paths
unchanged.

The WSL detection reads /proc/version, lets an explicit WSL<version>
marker decide WSL1 vs WSL2+, and only treats a bare Microsoft marker as
WSL1 when no explicit WSL version is present.

addresses https://github.com/openai/codex/issues/16076

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-12 14:08:14 -07:00
..

codex-linux-sandbox

This crate is responsible for producing:

  • a codex-linux-sandbox standalone executable for Linux that is bundled with the Node.js version of the Codex CLI
  • a lib crate that exposes the business logic of the executable as run_main() so that
    • the codex-exec CLI can check if its arg0 is codex-linux-sandbox and, if so, execute as if it were codex-linux-sandbox
    • this should also be true of the codex multitool CLI

On Linux, the bubblewrap pipeline prefers the first bwrap found on PATH outside the current working directory whenever it is available. If bwrap is present but too old to support --argv0, the helper keeps using system bubblewrap and switches to a no---argv0 compatibility path for the inner re-exec. If bwrap is missing, the helper falls back to the vendored bubblewrap path compiled into this binary. Codex also surfaces a startup warning when bwrap is missing so users know it is falling back to the vendored helper. Codex surfaces the same startup warning path when bubblewrap cannot create user namespaces. WSL2 follows the normal Linux bubblewrap path. WSL1 is not supported for bubblewrap sandboxing because it cannot create the required user namespaces, so Codex rejects sandboxed shell commands that would enter the bubblewrap path.

Current Behavior

  • Legacy SandboxPolicy / sandbox_mode configs remain supported.
  • Bubblewrap is the default filesystem sandbox pipeline.
  • If bwrap is present on PATH outside the current working directory, the helper uses it.
  • If bwrap is present but too old to support --argv0, the helper uses a no---argv0 compatibility path for the inner re-exec.
  • If bwrap is missing, the helper falls back to the vendored bubblewrap path.
  • If bwrap is missing, Codex also surfaces a startup warning instead of printing directly from the sandbox helper.
  • If bubblewrap cannot create user namespaces, Codex surfaces a startup warning instead of waiting for a runtime sandbox failure.
  • WSL2 uses the normal Linux bubblewrap path.
  • WSL1 is not supported for bubblewrap sandboxing; Codex rejects sandboxed shell commands that would require the bubblewrap path before invoking bwrap.
  • Legacy Landlock + mount protections remain available as an explicit legacy fallback path.
  • Set features.use_legacy_landlock = true (or CLI -c use_legacy_landlock=true) to force the legacy Landlock fallback.
  • The legacy Landlock fallback is used only when the split filesystem policy is sandbox-equivalent to the legacy model after cwd resolution.
  • Split-only filesystem policies that do not round-trip through the legacy SandboxPolicy model stay on bubblewrap so nested read-only or denied carveouts are preserved.
  • When the default bubblewrap pipeline is active, the helper applies PR_SET_NO_NEW_PRIVS and a seccomp network filter in-process.
  • When the default bubblewrap pipeline is active, the filesystem is read-only by default via --ro-bind / /.
  • When the default bubblewrap pipeline is active, writable roots are layered with --bind <root> <root>.
  • When the default bubblewrap pipeline is active, protected subpaths under writable roots (for example .git, resolved gitdir:, and .codex) are re-applied as read-only via --ro-bind.
  • When the default bubblewrap pipeline is active, overlapping split-policy entries are applied in path-specificity order so narrower writable children can reopen broader read-only or denied parents while narrower denied subpaths still win. For example, /repo = write, /repo/a = none, /repo/a/b = write keeps /repo writable, denies /repo/a, and reopens /repo/a/b as writable again.
  • When the default bubblewrap pipeline is active, symlink-in-path and non-existent protected paths inside writable roots are blocked by mounting /dev/null on the symlink or first missing component.
  • When the default bubblewrap pipeline is active, the helper explicitly isolates the user namespace via --unshare-user and the PID namespace via --unshare-pid.
  • When the default bubblewrap pipeline is active and network is restricted without proxy routing, the helper also isolates the network namespace via --unshare-net.
  • In managed proxy mode, the helper uses --unshare-net plus an internal TCP->UDS->TCP routing bridge so tool traffic reaches only configured proxy endpoints.
  • In managed proxy mode, after the bridge is live, seccomp blocks new AF_UNIX/socketpair creation for the user command.
  • When the default bubblewrap pipeline is active, it mounts a fresh /proc via --proc /proc by default, but you can skip this in restrictive container environments with --no-proc.

Notes

  • The CLI surface still uses legacy names like codex debug landlock.