mirror of
https://github.com/openai/codex.git
synced 2026-04-25 15:15:15 +00:00
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>
This commit is contained in:
159
codex-rs/linux-sandbox/src/linux_run_main_tests.rs
Normal file
159
codex-rs/linux-sandbox/src/linux_run_main_tests.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
#[cfg(test)]
|
||||
use super::*;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
|
||||
#[test]
|
||||
fn detects_proc_mount_invalid_argument_failure() {
|
||||
let stderr = "bwrap: Can't mount proc on /newroot/proc: Invalid argument";
|
||||
assert!(is_proc_mount_failure(stderr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_proc_mount_operation_not_permitted_failure() {
|
||||
let stderr = "bwrap: Can't mount proc on /newroot/proc: Operation not permitted";
|
||||
assert!(is_proc_mount_failure(stderr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_proc_mount_permission_denied_failure() {
|
||||
let stderr = "bwrap: Can't mount proc on /newroot/proc: Permission denied";
|
||||
assert!(is_proc_mount_failure(stderr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_non_proc_mount_errors() {
|
||||
let stderr = "bwrap: Can't bind mount /dev/null: Operation not permitted";
|
||||
assert!(!is_proc_mount_failure(stderr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inserts_bwrap_argv0_before_command_separator() {
|
||||
let argv = build_bwrap_argv(
|
||||
vec!["/bin/true".to_string()],
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
Path::new("/"),
|
||||
BwrapOptions {
|
||||
mount_proc: true,
|
||||
network_mode: BwrapNetworkMode::FullAccess,
|
||||
},
|
||||
);
|
||||
assert_eq!(
|
||||
argv,
|
||||
vec![
|
||||
"bwrap".to_string(),
|
||||
"--new-session".to_string(),
|
||||
"--die-with-parent".to_string(),
|
||||
"--ro-bind".to_string(),
|
||||
"/".to_string(),
|
||||
"/".to_string(),
|
||||
"--dev".to_string(),
|
||||
"/dev".to_string(),
|
||||
"--unshare-pid".to_string(),
|
||||
"--proc".to_string(),
|
||||
"/proc".to_string(),
|
||||
"--argv0".to_string(),
|
||||
"codex-linux-sandbox".to_string(),
|
||||
"--".to_string(),
|
||||
"/bin/true".to_string(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inserts_unshare_net_when_network_isolation_requested() {
|
||||
let argv = build_bwrap_argv(
|
||||
vec!["/bin/true".to_string()],
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
Path::new("/"),
|
||||
BwrapOptions {
|
||||
mount_proc: true,
|
||||
network_mode: BwrapNetworkMode::Isolated,
|
||||
},
|
||||
);
|
||||
assert!(argv.contains(&"--unshare-net".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inserts_unshare_net_when_proxy_only_network_mode_requested() {
|
||||
let argv = build_bwrap_argv(
|
||||
vec!["/bin/true".to_string()],
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
Path::new("/"),
|
||||
BwrapOptions {
|
||||
mount_proc: true,
|
||||
network_mode: BwrapNetworkMode::ProxyOnly,
|
||||
},
|
||||
);
|
||||
assert!(argv.contains(&"--unshare-net".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proxy_only_mode_takes_precedence_over_full_network_policy() {
|
||||
let mode = bwrap_network_mode(&SandboxPolicy::DangerFullAccess, true);
|
||||
assert_eq!(mode, BwrapNetworkMode::ProxyOnly);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn managed_proxy_preflight_argv_is_wrapped_for_full_access_policy() {
|
||||
let mode = bwrap_network_mode(&SandboxPolicy::DangerFullAccess, true);
|
||||
let argv = build_preflight_bwrap_argv(Path::new("/"), &SandboxPolicy::DangerFullAccess, mode);
|
||||
assert!(argv.iter().any(|arg| arg == "--"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn managed_proxy_inner_command_includes_route_spec() {
|
||||
let args = build_inner_seccomp_command(
|
||||
Path::new("/tmp"),
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
true,
|
||||
true,
|
||||
Some("{\"routes\":[]}".to_string()),
|
||||
vec!["/bin/true".to_string()],
|
||||
);
|
||||
|
||||
assert!(args.iter().any(|arg| arg == "--proxy-route-spec"));
|
||||
assert!(args.iter().any(|arg| arg == "{\"routes\":[]}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_managed_inner_command_omits_route_spec() {
|
||||
let args = build_inner_seccomp_command(
|
||||
Path::new("/tmp"),
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
true,
|
||||
false,
|
||||
None,
|
||||
vec!["/bin/true".to_string()],
|
||||
);
|
||||
|
||||
assert!(!args.iter().any(|arg| arg == "--proxy-route-spec"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn managed_proxy_inner_command_requires_route_spec() {
|
||||
let result = std::panic::catch_unwind(|| {
|
||||
build_inner_seccomp_command(
|
||||
Path::new("/tmp"),
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
true,
|
||||
true,
|
||||
None,
|
||||
vec!["/bin/true".to_string()],
|
||||
)
|
||||
});
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_seccomp_then_exec_without_bwrap_panics() {
|
||||
let result = std::panic::catch_unwind(|| ensure_inner_stage_mode_is_valid(true, false));
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_inner_stage_modes_do_not_panic() {
|
||||
ensure_inner_stage_mode_is_valid(false, false);
|
||||
ensure_inner_stage_mode_is_valid(false, true);
|
||||
ensure_inner_stage_mode_is_valid(true, true);
|
||||
}
|
||||
Reference in New Issue
Block a user