Honor explicit Unix socket grants in the Linux managed sandbox (#45534)

## Why

Linux proxy-routed sandboxing denied standalone Unix sockets even when the effective network policy enabled `dangerously_allow_all_unix_sockets`.

## What changed

- Carry Unix socket permissions in `ManagedNetworkSandboxContext` and pass the prepared context through Linux sandbox launches with `--managed-network`.
- Allow `AF_UNIX` socket creation in proxy-routed mode when `dangerously_allow_all_unix_sockets` is enabled, while preserving network namespace isolation and restrictions on other socket families.
- Keep standalone Unix sockets denied by default and for path-only grants. Default missing fields in older serialized contexts to restrictive values.

## Testing

Add coverage for policy preparation and transport, legacy deserialization, and malformed policy rejection. Add a Linux integration test covering default denial, path-only denial, and explicit allow-all access, while checking that direct TCP access and `AF_NETLINK`/`AF_VSOCK` sockets remain blocked.

GitOrigin-RevId: 2695b945ad3e59fcb3faf7662d852a26650af16c
This commit is contained in:
viyatb-oai
2026-09-14 22:40:20 +00:00
committed by copyberry
parent d39cfa8a2d
commit 99914f4950
18 changed files with 599 additions and 68 deletions
+64
View File
@@ -483,6 +483,12 @@ pub struct ManagedNetworkSandboxContext {
/// Whether the command may bind local sockets and exchange loopback traffic.
#[serde(default)]
pub allow_local_binding: bool,
/// Unix-domain socket paths allowed by the effective managed-network policy.
#[serde(default)]
pub allow_unix_sockets: Vec<String>,
/// Whether the effective policy permits connections to all Unix-domain sockets.
#[serde(default)]
pub dangerously_allow_all_unix_sockets: bool,
}
/// Environment-specific managed-network settings prepared for one command launch.
@@ -1133,6 +1139,9 @@ impl NetworkProxy {
sandbox_context: ManagedNetworkSandboxContext {
loopback_ports,
allow_local_binding: runtime_settings.allow_local_binding,
allow_unix_sockets: runtime_settings.allow_unix_sockets.to_vec(),
dangerously_allow_all_unix_sockets: runtime_settings
.dangerously_allow_all_unix_sockets,
},
}
}
@@ -2109,6 +2118,60 @@ mod tests {
);
}
#[tokio::test]
async fn prepare_for_optional_environment_preserves_effective_unix_socket_permissions()
-> Result<()> {
let mut config = NetworkProxyConfig {
enabled: true,
proxy_url: "http://127.0.0.1:43128".to_string(),
socks_url: "http://127.0.0.1:48081".to_string(),
allow_local_binding: true,
unix_sockets: Some(crate::config::NetworkUnixSocketPermissions {
entries: [
(
"/tmp/allowed.sock".to_string(),
crate::config::NetworkUnixSocketPermission::Allow,
),
(
"/tmp/denied.sock".to_string(),
crate::config::NetworkUnixSocketPermission::Deny,
),
]
.into_iter()
.collect(),
}),
..NetworkProxyConfig::default()
};
let proxy = NetworkProxy::builder()
.state(Arc::new(network_proxy_state_for_policy(config.clone())))
.managed_by_codex(/*managed_by_codex*/ false)
.build()
.await?;
for allow_all in [false, true] {
config.dangerously_allow_all_unix_sockets = allow_all;
let replacement = crate::state::build_config_state(config.clone(), Default::default())?;
proxy.replace_config_state(replacement).await?;
let prepared = proxy
.prepare_for_optional_environment(HashMap::new(), /*environment_id*/ None)?;
assert_eq!(
prepared.sandbox_context,
ManagedNetworkSandboxContext {
loopback_ports: vec![43128, 48081],
allow_local_binding: true,
allow_unix_sockets: if cfg!(target_os = "windows") {
Vec::new()
} else {
vec!["/tmp/allowed.sock".to_string()]
},
dangerously_allow_all_unix_sockets: !cfg!(target_os = "windows") && allow_all,
}
);
}
Ok(())
}
#[tokio::test]
async fn prepare_for_environment_keeps_env_and_sandbox_ports_in_sync() -> Result<()> {
#[cfg(target_os = "windows")]
@@ -2173,6 +2236,7 @@ mod tests {
ManagedNetworkSandboxContext {
loopback_ports: expected_ports,
allow_local_binding: false,
..ManagedNetworkSandboxContext::default()
}
);
}