mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
fix(exec-server): separate proxy launch inputs
Co-authored-by: Codex noreply@openai.com
This commit is contained in:
co-authored by
Codex noreply@openai.com
parent
2af252a7b8
commit
6d48c777bc
@@ -69,6 +69,7 @@ pub use proxy::PreparedManagedNetwork;
|
||||
pub use proxy::has_proxy_url_env_vars;
|
||||
pub use proxy::proxy_url_env_value;
|
||||
pub use remote_config::RemoteNetworkProxyConfig;
|
||||
pub use remote_config::RemoteNetworkProxyLaunchConfig;
|
||||
pub use runtime::BlockedRequest;
|
||||
pub use runtime::BlockedRequestArgs;
|
||||
pub use runtime::BlockedRequestObserver;
|
||||
|
||||
@@ -348,9 +348,6 @@ pub struct ManagedNetworkSandboxContext {
|
||||
/// Whether the command may bind local sockets and exchange loopback traffic.
|
||||
#[serde(default)]
|
||||
pub allow_local_binding: bool,
|
||||
/// Effective proxy settings for an exec-server that must start its own local listeners.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub proxy_config: Option<crate::RemoteNetworkProxyConfig>,
|
||||
}
|
||||
|
||||
/// Environment-specific managed-network settings prepared for one command launch.
|
||||
@@ -660,6 +657,29 @@ impl NetworkProxy {
|
||||
self.state.current_cfg().await
|
||||
}
|
||||
|
||||
/// Captures the static inputs needed to launch a matching executor-local proxy.
|
||||
pub async fn remote_launch_config(&self) -> Result<crate::RemoteNetworkProxyLaunchConfig> {
|
||||
let proxy = crate::RemoteNetworkProxyConfig::from_effective_config(
|
||||
&self.state.current_cfg().await?,
|
||||
)?;
|
||||
let (environment_id, execution_id) = self
|
||||
.execution_scope
|
||||
.as_ref()
|
||||
.map(|scope| {
|
||||
(
|
||||
Some(scope.environment_id.clone()),
|
||||
Some(scope.execution_id.clone()),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
Ok(crate::RemoteNetworkProxyLaunchConfig {
|
||||
proxy,
|
||||
audit_metadata: self.state.audit_metadata().clone(),
|
||||
environment_id,
|
||||
execution_id,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn add_allowed_domain(&self, host: &str) -> Result<()> {
|
||||
self.state.add_allowed_domain(host).await
|
||||
}
|
||||
@@ -730,7 +750,6 @@ impl NetworkProxy {
|
||||
sandbox_context: ManagedNetworkSandboxContext {
|
||||
loopback_ports,
|
||||
allow_local_binding: runtime_settings.allow_local_binding,
|
||||
proxy_config: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1220,7 +1239,6 @@ mod tests {
|
||||
ManagedNetworkSandboxContext {
|
||||
loopback_ports: expected_ports,
|
||||
allow_local_binding: false,
|
||||
proxy_config: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::*;
|
||||
|
||||
pub(super) struct ExecutionScope {
|
||||
pub(super) environment_id: String,
|
||||
pub(super) execution_id: String,
|
||||
pub(super) attribution_token: String,
|
||||
state: Arc<NetworkProxyState>,
|
||||
}
|
||||
@@ -30,6 +31,7 @@ impl NetworkProxy {
|
||||
let mut proxy = self.clone();
|
||||
proxy.execution_scope = Some(Arc::new(ExecutionScope {
|
||||
environment_id: environment_id.to_string(),
|
||||
execution_id: execution_id.to_string(),
|
||||
attribution_token,
|
||||
state: Arc::clone(&self.state),
|
||||
}));
|
||||
|
||||
@@ -5,9 +5,49 @@ use serde::Serialize;
|
||||
|
||||
use crate::NetworkDomainPermissions;
|
||||
use crate::NetworkMode;
|
||||
use crate::NetworkProxyAuditMetadata;
|
||||
use crate::NetworkProxyConfig;
|
||||
use crate::NetworkUnixSocketPermissions;
|
||||
|
||||
/// Executor-local proxy launch inputs transported with one process start.
|
||||
///
|
||||
/// Unlike [`crate::ManagedNetworkSandboxContext`], this describes how the executor should create
|
||||
/// proxy listeners. The sandbox context is materialized only after those listeners are running.
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[non_exhaustive]
|
||||
pub struct RemoteNetworkProxyLaunchConfig {
|
||||
pub proxy: RemoteNetworkProxyConfig,
|
||||
#[serde(default)]
|
||||
pub audit_metadata: NetworkProxyAuditMetadata,
|
||||
#[serde(default)]
|
||||
pub environment_id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub execution_id: Option<String>,
|
||||
}
|
||||
|
||||
impl RemoteNetworkProxyLaunchConfig {
|
||||
pub fn new(proxy: RemoteNetworkProxyConfig) -> Self {
|
||||
Self {
|
||||
proxy,
|
||||
audit_metadata: NetworkProxyAuditMetadata::default(),
|
||||
environment_id: None,
|
||||
execution_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_audit_metadata(mut self, audit_metadata: NetworkProxyAuditMetadata) -> Self {
|
||||
self.audit_metadata = audit_metadata;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn for_execution(mut self, environment_id: String, execution_id: String) -> Self {
|
||||
self.environment_id = Some(environment_id);
|
||||
self.execution_id = Some(execution_id);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Effective network proxy settings that are safe to send to a remote executor.
|
||||
///
|
||||
/// Listener addresses are deliberately omitted because the executor chooses its own loopback
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::RemoteNetworkProxyConfig;
|
||||
use super::RemoteNetworkProxyLaunchConfig;
|
||||
use crate::NetworkMode;
|
||||
use crate::NetworkProxyAuditMetadata;
|
||||
use crate::NetworkProxyConfig;
|
||||
use crate::NetworkProxyState;
|
||||
|
||||
#[test]
|
||||
fn round_trip_preserves_supported_effective_settings() {
|
||||
@@ -44,3 +47,27 @@ fn rejects_mitm_configuration() {
|
||||
"remote exec-server network proxy does not support MITM, credential injection, or MITM hooks"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn launch_config_materializes_audit_and_execution_attribution() {
|
||||
let proxy = RemoteNetworkProxyConfig::from_effective_config(&NetworkProxyConfig::default())
|
||||
.expect("supported remote config");
|
||||
let audit_metadata = NetworkProxyAuditMetadata {
|
||||
conversation_id: Some("conversation-1".to_string()),
|
||||
user_account_id: Some("account-1".to_string()),
|
||||
originator: Some("codex_cli_rs".to_string()),
|
||||
model: Some("model-1".to_string()),
|
||||
..NetworkProxyAuditMetadata::default()
|
||||
};
|
||||
let state = NetworkProxyState::from_remote_launch_config(RemoteNetworkProxyLaunchConfig {
|
||||
proxy,
|
||||
audit_metadata: audit_metadata.clone(),
|
||||
environment_id: Some("remote".to_string()),
|
||||
execution_id: Some("execution-1".to_string()),
|
||||
})
|
||||
.expect("remote launch state");
|
||||
|
||||
assert_eq!(state.audit_metadata(), &audit_metadata);
|
||||
assert_eq!(state.environment_id(), Some("remote"));
|
||||
assert_eq!(state.execution_id().as_deref(), Some("execution-1"));
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use globset::GlobSet;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
@@ -47,7 +48,8 @@ const MAX_BLOCKED_EVENTS: usize = 200;
|
||||
const DNS_LOOKUP_TIMEOUT: Duration = Duration::from_secs(2);
|
||||
const NETWORK_POLICY_VIOLATION_PREFIX: &str = "CODEX_NETWORK_POLICY_VIOLATION";
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NetworkProxyAuditMetadata {
|
||||
pub conversation_id: Option<String>,
|
||||
pub app_version: Option<String>,
|
||||
@@ -286,6 +288,29 @@ impl NetworkProxyState {
|
||||
))
|
||||
}
|
||||
|
||||
/// Builds immutable runtime state for one executor-local proxy launch.
|
||||
pub fn from_remote_launch_config(
|
||||
launch: crate::RemoteNetworkProxyLaunchConfig,
|
||||
) -> Result<Self> {
|
||||
let crate::RemoteNetworkProxyLaunchConfig {
|
||||
proxy,
|
||||
audit_metadata,
|
||||
environment_id,
|
||||
execution_id,
|
||||
} = launch;
|
||||
let config = proxy.into_network_proxy_config();
|
||||
let state = build_config_state(config, NetworkProxyConstraints::default())?;
|
||||
Ok(Self {
|
||||
environment_id: environment_id.map(Into::into),
|
||||
execution_id: execution_id.map(Into::into),
|
||||
..Self::with_reloader_and_audit_metadata(
|
||||
state.clone(),
|
||||
Arc::new(StaticConfigReloader { state }),
|
||||
audit_metadata,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_reloader(state: ConfigState, reloader: Arc<dyn ConfigReloader>) -> Self {
|
||||
Self::with_reloader_and_audit_metadata(
|
||||
state,
|
||||
|
||||
Reference in New Issue
Block a user