mirror of
https://github.com/openai/codex.git
synced 2026-05-02 20:32:04 +03:00
fix(network-proxy): add unix socket allow-all and update seatbelt rules (#11368)
## Summary Adds support for a Unix socket escape hatch so we can bypass socket allowlisting when explicitly enabled. ## Description * added a new flag, `network.dangerously_allow_all_unix_sockets` as an explicit escape hatch * In codex-network-proxy, enabling that flag now allows any absolute Unix socket path from x-unix-socket instead of requiring each path to be explicitly allowlisted. Relative paths are still rejected. * updated the macOS seatbelt path in core so it enforces the same Unix socket behavior: * allowlisted sockets generate explicit network* subpath rules * allow-all generates a broad network* (subpath "/") rule --------- Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use crate::config::NetworkMode;
|
||||
use crate::config::NetworkProxyConfig;
|
||||
use crate::config::ValidatedUnixSocketPath;
|
||||
use crate::policy::Host;
|
||||
use crate::policy::is_loopback_host;
|
||||
use crate::policy::is_non_public_ip;
|
||||
@@ -418,6 +419,10 @@ impl NetworkProxyState {
|
||||
}
|
||||
|
||||
let guard = self.state.read().await;
|
||||
if guard.config.network.dangerously_allow_all_unix_sockets {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Normalize the path while keeping the absolute-path requirement explicit.
|
||||
let requested_abs = match AbsolutePathBuf::from_absolute_path(requested_path) {
|
||||
Ok(path) => path,
|
||||
@@ -425,7 +430,16 @@ impl NetworkProxyState {
|
||||
};
|
||||
let requested_canonical = std::fs::canonicalize(requested_abs.as_path()).ok();
|
||||
for allowed in &guard.config.network.allow_unix_sockets {
|
||||
if allowed == path {
|
||||
let allowed_path = match ValidatedUnixSocketPath::parse(allowed) {
|
||||
Ok(ValidatedUnixSocketPath::Native(path)) => path,
|
||||
Ok(ValidatedUnixSocketPath::UnixStyleAbsolute(_)) => continue,
|
||||
Err(err) => {
|
||||
warn!("ignoring invalid network.allow_unix_sockets entry at runtime: {err:#}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if allowed_path.as_path() == requested_abs.as_path() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
@@ -434,7 +448,7 @@ impl NetworkProxyState {
|
||||
let Some(requested_canonical) = &requested_canonical else {
|
||||
continue;
|
||||
};
|
||||
if let Ok(allowed_canonical) = std::fs::canonicalize(allowed)
|
||||
if let Ok(allowed_canonical) = std::fs::canonicalize(allowed_path.as_path())
|
||||
&& &allowed_canonical == requested_canonical
|
||||
{
|
||||
return Ok(true);
|
||||
@@ -1075,6 +1089,77 @@ mod tests {
|
||||
assert!(validate_policy_against_constraints(&config, &constraints).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_policy_against_constraints_disallows_allow_all_unix_sockets_without_managed_opt_in()
|
||||
{
|
||||
let constraints = NetworkProxyConstraints {
|
||||
dangerously_allow_all_unix_sockets: Some(false),
|
||||
..NetworkProxyConstraints::default()
|
||||
};
|
||||
|
||||
let config = NetworkProxyConfig {
|
||||
network: NetworkProxySettings {
|
||||
enabled: true,
|
||||
dangerously_allow_all_unix_sockets: true,
|
||||
..NetworkProxySettings::default()
|
||||
},
|
||||
};
|
||||
|
||||
assert!(validate_policy_against_constraints(&config, &constraints).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_policy_against_constraints_disallows_allow_all_unix_sockets_when_allowlist_is_managed()
|
||||
{
|
||||
let constraints = NetworkProxyConstraints {
|
||||
allow_unix_sockets: Some(vec!["/tmp/allowed.sock".to_string()]),
|
||||
..NetworkProxyConstraints::default()
|
||||
};
|
||||
|
||||
let config = NetworkProxyConfig {
|
||||
network: NetworkProxySettings {
|
||||
enabled: true,
|
||||
dangerously_allow_all_unix_sockets: true,
|
||||
..NetworkProxySettings::default()
|
||||
},
|
||||
};
|
||||
|
||||
assert!(validate_policy_against_constraints(&config, &constraints).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_policy_against_constraints_allows_allow_all_unix_sockets_with_managed_opt_in() {
|
||||
let constraints = NetworkProxyConstraints {
|
||||
dangerously_allow_all_unix_sockets: Some(true),
|
||||
..NetworkProxyConstraints::default()
|
||||
};
|
||||
|
||||
let config = NetworkProxyConfig {
|
||||
network: NetworkProxySettings {
|
||||
enabled: true,
|
||||
dangerously_allow_all_unix_sockets: true,
|
||||
..NetworkProxySettings::default()
|
||||
},
|
||||
};
|
||||
|
||||
assert!(validate_policy_against_constraints(&config, &constraints).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_policy_against_constraints_allows_allow_all_unix_sockets_when_unmanaged() {
|
||||
let constraints = NetworkProxyConstraints::default();
|
||||
|
||||
let config = NetworkProxyConfig {
|
||||
network: NetworkProxySettings {
|
||||
enabled: true,
|
||||
dangerously_allow_all_unix_sockets: true,
|
||||
..NetworkProxySettings::default()
|
||||
},
|
||||
};
|
||||
|
||||
assert!(validate_policy_against_constraints(&config, &constraints).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_globset_is_case_insensitive() {
|
||||
let patterns = vec!["ExAmPle.CoM".to_string()];
|
||||
@@ -1172,6 +1257,19 @@ mod tests {
|
||||
assert!(state.is_unix_socket_allowed(&link_s).await.unwrap());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[tokio::test]
|
||||
async fn unix_socket_allow_all_flag_bypasses_allowlist() {
|
||||
let state = network_proxy_state_for_policy(NetworkProxySettings {
|
||||
allowed_domains: vec!["example.com".to_string()],
|
||||
dangerously_allow_all_unix_sockets: true,
|
||||
..NetworkProxySettings::default()
|
||||
});
|
||||
|
||||
assert!(state.is_unix_socket_allowed("/tmp/any.sock").await.unwrap());
|
||||
assert!(!state.is_unix_socket_allowed("relative.sock").await.unwrap());
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[tokio::test]
|
||||
async fn unix_socket_allowlist_is_rejected_on_non_macos() {
|
||||
@@ -1179,6 +1277,7 @@ mod tests {
|
||||
let state = network_proxy_state_for_policy(NetworkProxySettings {
|
||||
allowed_domains: vec!["example.com".to_string()],
|
||||
allow_unix_sockets: vec![socket_path.clone()],
|
||||
dangerously_allow_all_unix_sockets: true,
|
||||
..NetworkProxySettings::default()
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user