Commit Graph

7 Commits

  • fix: canonicalize symlinked Linux sandbox cwd (#14849)
    ## Problem
    On Linux, Codex can be launched from a workspace path that is a symlink
    (for example, a symlinked checkout or a symlinked parent directory).
    
    Our sandbox policy intentionally canonicalizes writable/readable roots
    to the real filesystem path before building the bubblewrap mounts. That
    part is correct and needed for safety.
    
    The remaining bug was that bubblewrap could still inherit the helper
    process's logical cwd, which might be the symlinked alias instead of the
    mounted canonical path. In that case, the sandbox starts in a cwd that
    does not exist inside the sandbox namespace even though the real
    workspace is mounted. This can cause sandboxed commands to fail in
    symlinked workspaces.
    
    ## Fix
    This PR keeps the sandbox policy behavior the same, but separates two
    concepts that were previously conflated:
    
    - the canonical cwd used to define sandbox mounts and permissions
    - the caller's logical cwd used when launching the command
    
    On the Linux bubblewrap path, we now thread the logical command cwd
    through the helper explicitly and only add `--chdir <canonical path>`
    when the logical cwd differs from the mounted canonical path.
    
    That means:
    - permissions are still computed from canonical paths
    - bubblewrap starts the command from a cwd that definitely exists inside
    the sandbox
    - we do not widen filesystem access or undo the earlier symlink
    hardening
    
    ## Why This Is Safe
    This is a narrow Linux-only launch fix, not a policy change.
    
    - Writable/readable root canonicalization stays intact.
    - Protected metadata carveouts still operate on canonical roots.
    - We only override bubblewrap's inherited cwd when the logical path
    would otherwise point at a symlink alias that is not mounted in the
    sandbox.
    
    ## Tests
    - kept the existing protocol/core regression coverage for symlink
    canonicalization
    - added regression coverage for symlinked cwd handling in the Linux
    bubblewrap builder/helper path
    
    Local validation:
    - `just fmt`
    - `cargo test -p codex-protocol`
    - `cargo test -p codex-core
    normalize_additional_permissions_canonicalizes_symlinked_write_paths`
    - `cargo clippy -p codex-linux-sandbox -p codex-protocol -p codex-core
    --tests -- -D warnings`
    - `cargo build --bin codex`
    
    ## Context
    This is related to #14694. The earlier writable-root symlink fix
    addressed the mount/permission side; this PR fixes the remaining
    symlinked-cwd launch mismatch in the Linux sandbox path.
  • 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.
  • refactor: make bubblewrap the default Linux sandbox (#13996)
    ## Summary
    - make bubblewrap the default Linux sandbox and keep
    `use_legacy_landlock` as the only override
    - remove `use_linux_sandbox_bwrap` from feature, config, schema, and
    docs surfaces
    - update Linux sandbox selection, CLI/config plumbing, and related
    tests/docs to match the new default
    - fold in the follow-up CI fixes for request-permissions responses and
    Linux read-only sandbox error text
  • linux-sandbox: honor split filesystem policies in bwrap (#13453)
    ## Why
    
    After `#13449`, the Linux helper could receive split filesystem and
    network policies, but the bubblewrap mount builder still reconstructed
    filesystem access from the legacy `SandboxPolicy`.
    
    That loses explicit unreadable carveouts under writable roots, and it
    also mishandles `Root` read access paired with explicit deny carveouts.
    In those cases bubblewrap could still expose paths that the split
    filesystem policy intentionally blocked.
    
    ## What changed
    
    - switched bubblewrap mount generation to consume
    `FileSystemSandboxPolicy` directly at the implementation boundary;
    legacy `SandboxPolicy` configs still flow through the existing
    `FileSystemSandboxPolicy::from(&sandbox_policy)` bridge before reaching
    bwrap
    - kept the Linux helper and preflight path on the split filesystem
    policy all the way into bwrap
    - re-applied explicit unreadable carveouts after readable and writable
    mounts so blocked subpaths still win under bubblewrap
    - masked denied directories with `--tmpfs` plus `--remount-ro` and
    denied files with `--ro-bind-data`, preserving the backing fd until exec
    - added comments in the unreadable-root masking block to explain why the
    mount order and directory/file split are intentional
    - updated Linux helper call sites and tests for the split-policy bwrap
    path
    
    ## Verification
    
    - added protocol coverage for root carveouts staying scoped
    - added core coverage that root-write plus deny carveouts still requires
    a platform sandbox
    - added bwrap unit coverage for reapplying blocked carveouts after
    writable binds
    - added Linux integration coverage for explicit split-policy carveouts
    under bubblewrap
    - validated the final branch state with `cargo test -p
    codex-linux-sandbox`, `cargo clippy -p codex-linux-sandbox --all-targets
    -- -D warnings`, and the PR CI reruns
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/13453).
    * __->__ #13453
    * #13452
    * #13451
    * #13449
    * #13448
    * #13445
    * #13440
    * #13439
    
    ---------
    
    Co-authored-by: viyatb-oai <viyatb@openai.com>
  • linux-sandbox: plumb split sandbox policies through helper (#13449)
    ## Why
    
    The Linux sandbox helper still only accepted the legacy `SandboxPolicy`
    payload.
    
    That meant the runtime could compute split filesystem and network
    policies, but the helper would immediately collapse them back to the
    compatibility projection before applying seccomp or staging the
    bubblewrap inner command.
    
    ## What changed
    
    - added hidden `--file-system-sandbox-policy` and
    `--network-sandbox-policy` flags alongside the legacy `--sandbox-policy`
    flag so the helper can migrate incrementally
    - updated the core-side Landlock wrapper to pass the split policies
    explicitly when launching `codex-linux-sandbox`
    - added helper-side resolution logic that accepts either the legacy
    policy alone or a complete split-policy pair and normalizes that into
    one effective configuration
    - switched Linux helper network decisions to use `NetworkSandboxPolicy`
    directly
    - added `FromStr` support for the split policy types so the helper can
    parse them from CLI JSON
    
    ## Verification
    
    - added helper coverage in `linux-sandbox/src/linux_run_main_tests.rs`
    for split-policy flags and policy resolution
    - added CLI argument coverage in `core/src/landlock.rs`
    - verified the current PR state with `just clippy`
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/13449).
    * #13453
    * #13452
    * #13451
    * __->__ #13449
    * #13448
    * #13445
    * #13440
    * #13439
    
    ---------
    
    Co-authored-by: viyatb-oai <viyatb@openai.com>
  • fix(linux-sandbox): always unshare bwrap userns (#13624)
    ## Summary
    - always pass `--unshare-user` in the Linux bubblewrap argv builders
    - stop relying on bubblewrap's auto-userns behavior, which is skipped
    for `uid 0`
    - update argv expectations in tests and document the explicit user
    namespace behavior
    
    The installed Codex binary reproduced the same issue with:
    - `codex -c features.use_linux_sandbox_bwrap=true sandbox linux -- true`
    - `bwrap: Creating new namespace failed: Operation not permitted`
    
    This happens because Codex asked bubblewrap for mount/pid/network
    namespaces without explicitly asking for a user namespace. In a
    root-inside-container environment without ambient `CAP_SYS_ADMIN`, that
    fails. Adding `--unshare-user` makes bubblewrap create the user
    namespace first and then the remaining namespaces succeed.
  • feat(linux-sandbox): implement proxy-only egress via TCP-UDS-TCP bridge (#11293)
    ## Summary
    - Implement Linux proxy-only routing in `codex-rs/linux-sandbox` with a
    two-stage bridge: host namespace `loopback TCP proxy endpoint -> UDS`,
    then bwrap netns `loopback TCP listener -> host UDS`.
    - Add hidden `--proxy-route-spec` plumbing for outer-to-inner stage
    handoff.
    - Fail closed in proxy mode when no valid loopback proxy endpoints can
    be routed.
    - Introduce explicit network seccomp modes: `Restricted` (legacy
    restricted networking) and `ProxyRouted` (allow INET/INET6 for routed
    proxy access, deny `AF_UNIX` and `socketpair`).
    - Enforce that proxy bridge/routing is bwrap-only by validating
    `--apply-seccomp-then-exec` requires `--use-bwrap-sandbox`.
    - Keep landlock-only flows unchanged (no proxy bridge behavior outside
    bwrap).
    
    ---------
    
    Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>