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.
This commit is contained in:
viyatb-oai 2026-03-05 13:57:40 -08:00 committed by GitHub
parent aa3fe8abf8
commit 9950b5e265
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 10 additions and 1 deletions

View file

@ -25,7 +25,8 @@ into this binary.
- When enabled, 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 enabled, the helper isolates the PID namespace via `--unshare-pid`.
- When enabled, the helper explicitly isolates the user namespace via
`--unshare-user` and the PID namespace via `--unshare-pid`.
- When enabled 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

View file

@ -107,6 +107,9 @@ fn create_bwrap_flags_full_filesystem(command: Vec<String>, options: BwrapOption
"--bind".to_string(),
"/".to_string(),
"/".to_string(),
// Always enter a fresh user namespace so root inside a container does
// not need ambient CAP_SYS_ADMIN to create the remaining namespaces.
"--unshare-user".to_string(),
"--unshare-pid".to_string(),
];
if options.network_mode.should_unshare_network() {
@ -132,6 +135,9 @@ fn create_bwrap_flags(
args.push("--new-session".to_string());
args.push("--die-with-parent".to_string());
args.extend(create_filesystem_args(sandbox_policy, cwd)?);
// Request a user namespace explicitly rather than relying on bubblewrap's
// auto-enable behavior, which is skipped when the caller runs as uid 0.
args.push("--unshare-user".to_string());
// Isolate the PID namespace.
args.push("--unshare-pid".to_string());
if options.network_mode.should_unshare_network() {
@ -425,6 +431,7 @@ mod tests {
"--bind".to_string(),
"/".to_string(),
"/".to_string(),
"--unshare-user".to_string(),
"--unshare-pid".to_string(),
"--unshare-net".to_string(),
"--proc".to_string(),

View file

@ -49,6 +49,7 @@ fn inserts_bwrap_argv0_before_command_separator() {
"/".to_string(),
"--dev".to_string(),
"/dev".to_string(),
"--unshare-user".to_string(),
"--unshare-pid".to_string(),
"--proc".to_string(),
"/proc".to_string(),