core-agent-ide/codex-rs/linux-sandbox
viyatb-oai 774965f1e8
fix: preserve split filesystem semantics in linux sandbox (#14173)
## Stack

   fix: fail closed for unsupported split windows sandboxing #14172
-> fix: preserve split filesystem semantics in linux sandbox #14173
   fix: align core approvals with split sandbox policies #14171
   refactor: centralize filesystem permissions precedence #14174

## Summary
## Summary
- Preserve Linux split filesystem carveouts in bubblewrap by applying
mount masks in the right order, so narrower rules still win under
broader writable roots.
- Preserve unreadable ancestors of writable roots by masking them first
and then rebinding the narrower writable descendants.
- Stop rejecting legacy-plus-split Linux configs that are
sandbox-equivalent after `cwd` resolution by comparing semantics instead
of raw legacy structs.
- Fail closed when callers provide partial split policies, mismatched
legacy-plus-split policies, or force `--use-legacy-landlock` for
split-only shapes that legacy Landlock cannot enforce.
- Add Linux regressions for overlapping writable, read-only, and denied
paths, and document the supported split-policy enforcement path.

## Example
Given a split filesystem policy like:

```toml
[permissions.dev.filesystem]
":root" = "read"
"/code" = "write"
"/code/.git" = "read"
"/code/secrets" = "none"
"/code/secrets/tmp" = "write"
```

this PR makes Linux enforce the intended result under bubblewrap:

- `/code` stays writable
- `/code/.git` stays read-only
- `/code/secrets` stays denied
- `/code/secrets/tmp` can still be reopened as writable if explicitly
allowed

Before this, Linux could lose one of those carveouts depending on mount
order or legacy-policy fallback. This PR keeps the split-policy
semantics intact and rejects configurations that legacy Landlock cannot
represent safely.
2026-03-12 10:56:32 -07:00
..
src fix: preserve split filesystem semantics in linux sandbox (#14173) 2026-03-12 10:56:32 -07:00
tests refactor: make bubblewrap the default Linux sandbox (#13996) 2026-03-11 23:31:18 -07:00
BUILD.bazel build(linux-sandbox): always compile vendored bubblewrap on Linux; remove CODEX_BWRAP_ENABLE_FFI (#11498) 2026-02-11 21:30:41 -08:00
build.rs build(linux-sandbox): always compile vendored bubblewrap on Linux; remove CODEX_BWRAP_ENABLE_FFI (#11498) 2026-02-11 21:30:41 -08:00
Cargo.toml feat(linux-sandbox): implement proxy-only egress via TCP-UDS-TCP bridge (#11293) 2026-02-21 18:16:34 +00:00
config.h build(linux-sandbox): always compile vendored bubblewrap on Linux; remove CODEX_BWRAP_ENABLE_FFI (#11498) 2026-02-11 21:30:41 -08:00
README.md fix: preserve split filesystem semantics in linux sandbox (#14173) 2026-03-12 10:56:32 -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 uses the vendored bubblewrap path compiled into this binary.

Current Behavior

  • Bubblewrap is the default filesystem sandbox pipeline and is standardized on the vendored path.
  • 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.
  • 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 parents while narrower denied subpaths still win.
  • 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.