Enable MXC TTY launches and managed networking in the exec server (#45524)

## What changed

- Report `windows_mxc` from native MXC availability on Windows.
- Allow MXC TTY launches and managed networking, using dedicated proxy listeners without requiring a shared-ingress restricting SID.
- Reject MXC custom `argv0` and private-desktop launches, and continue failing closed when native MXC is unavailable.

## Testing

Extend the Windows remote sandbox process-write test to cover MXC with both pipes and ConPTY. Retain coverage for rejecting MXC requests when native support is unavailable.

GitOrigin-RevId: 80c5f319b9066d06b26f0a7eb7119a109e6ebef7
This commit is contained in:
iceweasel-oai
2026-09-14 21:45:09 +00:00
committed by copyberry
parent e84a594636
commit 60e35765c3
7 changed files with 72 additions and 53 deletions
+1
View File
@@ -3298,6 +3298,7 @@ version = "0.0.0"
dependencies = [
"base64 0.22.1",
"codex-file-system",
"codex-mxc-sandbox",
"codex-network-proxy",
"codex-protocol",
"codex-shell-command",
+3
View File
@@ -22,5 +22,8 @@ codex-utils-path-uri = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, features = ["arbitrary_precision", "raw_value"] }
[target.'cfg(windows)'.dependencies]
codex-mxc-sandbox = { workspace = true }
[dev-dependencies]
pretty_assertions = { workspace = true }
@@ -217,6 +217,10 @@ impl EnvironmentInfo {
/// Returns information about the current local exec-server process.
pub fn local() -> Self {
#[cfg(windows)]
let windows_mxc = codex_mxc_sandbox::is_available();
#[cfg(not(windows))]
let windows_mxc = false;
let cwd = std::env::current_dir().ok();
let temporary_directories = Self::local_temporary_directories_with_cwd(cwd.as_deref());
let normalize_temp_path = |path: std::ffi::OsString| {
@@ -246,7 +250,7 @@ impl EnvironmentInfo {
http_header_env_vars: true,
sandboxed_file_streaming: true,
shell_snapshot_v2: cfg!(unix),
windows_mxc: false,
windows_mxc,
},
}
}
+30 -20
View File
@@ -5,6 +5,7 @@ use crate::process_telemetry::ProcessTelemetry;
use codex_exec_server_protocol::JSONRPCErrorError;
use codex_network_proxy::CUSTOM_CA_ENV_KEYS;
use codex_network_proxy::ManagedNetworkSandboxContext;
use codex_network_proxy::ManagedProxyRouting;
use codex_network_proxy::NetworkPolicyAuditObserver;
use codex_network_proxy::NetworkPolicyDecider;
use codex_network_proxy::NetworkProxy;
@@ -14,6 +15,7 @@ use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
use codex_network_proxy::is_managed_mitm_ca_trust_bundle_path;
#[cfg(target_os = "windows")]
use codex_network_proxy::strip_managed_proxy_env;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
use codex_sandboxing::SandboxCommand;
use codex_sandboxing::SandboxDirectSpawnTransformRequest;
@@ -87,17 +89,9 @@ pub(crate) async fn prepare_exec_request_with_telemetry(
if let Some(sandbox) = params.sandbox.as_ref()
&& sandbox.windows_sandbox_level == codex_protocol::config_types::WindowsSandboxLevel::Mxc
{
if params.tty || params.arg0.is_some() {
if params.arg0.is_some() || sandbox.windows_sandbox_private_desktop {
return Err(invalid_params(
"MXC currently supports ordinary pipe launches only".to_owned(),
));
}
if params.enforce_managed_network
|| params.managed_network.is_some()
|| params.network_proxy.is_some()
{
return Err(invalid_params(
"MXC managed networking is not supported yet".to_owned(),
"MXC custom argv0 and private-desktop launches are not supported".to_owned(),
));
}
if !codex_sandboxing::windows_mxc_available() {
@@ -126,6 +120,15 @@ pub(crate) async fn prepare_exec_request_with_telemetry(
prepare_managed_network(
params.managed_network.as_ref(),
network_proxy,
if params
.sandbox
.as_ref()
.is_some_and(|sandbox| sandbox.windows_sandbox_level == WindowsSandboxLevel::Mxc)
{
ManagedProxyRouting::DedicatedListeners
} else {
ManagedProxyRouting::SharedIngress
},
env,
network_policy_decider,
network_policy_audit_observer,
@@ -328,6 +331,7 @@ pub(crate) async fn prepare_exec_request_with_telemetry(
async fn prepare_managed_network(
managed_network: Option<&ManagedNetworkSandboxContext>,
network_proxy: Option<&RemoteNetworkProxyLaunchConfig>,
routing: ManagedProxyRouting,
env: HashMap<String, String>,
network_policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
network_policy_audit_observer: Option<NetworkPolicyAuditObserver>,
@@ -363,7 +367,9 @@ async fn prepare_managed_network(
registration_id: registration.executor_registration_id.clone(),
}),
});
let mut builder = NetworkProxy::builder().state(Arc::new(state));
let mut builder = NetworkProxy::builder()
.state(Arc::new(state))
.managed_proxy_routing(routing);
if let Some(network_policy_decider) = network_policy_decider {
builder = builder.policy_decider_arc(network_policy_decider);
}
@@ -376,15 +382,19 @@ async fn prepare_managed_network(
.await
.map_err(|err| internal_error(format!("failed to start executor network proxy: {err}")))?;
#[cfg(target_os = "windows")]
let network_proxy_restricting_sid = Some(
proxy
.network_proxy_restricting_sid(/*environment_id*/ None)
.ok_or_else(|| {
internal_error(
"managed Windows proxy route is missing its restricting SID".to_string(),
)
})?,
);
let network_proxy_restricting_sid = if routing == ManagedProxyRouting::SharedIngress {
Some(
proxy
.network_proxy_restricting_sid(/*environment_id*/ None)
.ok_or_else(|| {
internal_error(
"managed Windows proxy route is missing its restricting SID".to_string(),
)
})?,
)
} else {
None
};
#[cfg(not(target_os = "windows"))]
let network_proxy_restricting_sid = None;
let prepared = proxy
@@ -133,29 +133,17 @@ async fn sandbox_request_wraps_native_argv_on_executor() {
let mut params = params;
params.sandbox.as_mut().unwrap().windows_sandbox_level =
codex_protocol::config_types::WindowsSandboxLevel::Mxc;
for (tty, managed, message) in [
(false, false, "native MXC is unavailable on this executor"),
(
true,
false,
"MXC currently supports ordinary pipe launches only",
),
(false, true, "MXC managed networking is not supported yet"),
] {
params.tty = tty;
params.enforce_managed_network = managed;
let error = prepare_exec_request(
&params,
HashMap::new(),
Some(&runtime_paths),
/*network_policy_decider*/ None,
/*network_policy_audit_observer*/ None,
)
.await
.err()
.expect("unsupported MXC must fail closed");
assert_eq!(error.message, message);
}
let error = prepare_exec_request(
&params,
HashMap::new(),
Some(&runtime_paths),
/*network_policy_decider*/ None,
/*network_policy_audit_observer*/ None,
)
.await
.err()
.expect("unsupported MXC must fail closed");
assert_eq!(error.message, "native MXC is unavailable on this executor");
}
#[cfg(unix)]
@@ -187,9 +187,10 @@ async fn accepted_websocket_interoperates_and_recovers_with_real_direct_executor
.default_environment()
.context("direct executor environment should be installed")?;
let expected_info = EnvironmentInfo::local();
assert_eq!(
timeout(TEST_TIMEOUT, environment.force_info()).await??,
EnvironmentInfo::local()
expected_info
);
let files = tempfile::tempdir()?;
let large_file_path = files.path().join("large-response.bin");
@@ -340,7 +341,7 @@ async fn accepted_websocket_interoperates_and_recovers_with_real_direct_executor
));
assert_eq!(
timeout(TEST_TIMEOUT, environment.force_info()).await??,
EnvironmentInfo::local()
expected_info
);
let recovered_read = timeout(Duration::from_secs(5), pending_read)
+19 -7
View File
@@ -1297,6 +1297,7 @@ async fn assert_exec_process_write_then_read_without_tty(use_remote: bool) -> Re
async fn assert_remote_windows_sandbox_process_write(
windows_sandbox_level: WindowsSandboxLevel,
expected_sandbox_type: codex_sandboxing::SandboxType,
tty: bool,
) -> Result<()> {
if !selected_windows_sandbox_available(windows_sandbox_level) {
eprintln!("skipping MXC enforcement test: native MXC is unavailable on this host");
@@ -1332,8 +1333,8 @@ async fn assert_remote_windows_sandbox_process_write(
shell_snapshot: None,
env_policy: /*env_policy*/ None,
env: Default::default(),
tty: false,
pipe_stdin: true,
tty,
pipe_stdin: !tty,
arg0: None,
sandbox: Some(sandbox),
enforce_managed_network: false,
@@ -1347,7 +1348,8 @@ async fn assert_remote_windows_sandbox_process_write(
};
assert_eq!(session.sandbox_type, Some(expected_sandbox_type));
let write_response = session.process.write(b"hello\n".to_vec()).await?;
let input = if tty { b"hello\r" } else { b"hello\n" };
let write_response = session.process.write(input.to_vec()).await?;
assert_eq!(write_response.status, WriteStatus::Accepted);
let StartedExecProcess { process, .. } = session;
let wake_rx = process.subscribe_wake();
@@ -1766,13 +1768,21 @@ async fn exec_process_write_then_read_without_tty(use_remote: bool) -> Result<()
#[test_case(
WindowsSandboxLevel::RestrictedToken,
codex_sandboxing::SandboxType::WindowsRestrictedToken;
codex_sandboxing::SandboxType::WindowsRestrictedToken,
false;
"restricted_token"
)]
#[test_case(
WindowsSandboxLevel::Mxc,
codex_sandboxing::SandboxType::WindowsMxc;
"mxc"
codex_sandboxing::SandboxType::WindowsMxc,
false;
"mxc_pipe"
)]
#[test_case(
WindowsSandboxLevel::Mxc,
codex_sandboxing::SandboxType::WindowsMxc,
true;
"mxc_conpty"
)]
#[cfg_attr(not(windows), ignore = "Windows-only exec-server sandbox process test")]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
@@ -1780,8 +1790,10 @@ async fn exec_process_write_then_read_without_tty(use_remote: bool) -> Result<()
async fn remote_windows_sandbox_process_accepts_process_write(
windows_sandbox_level: WindowsSandboxLevel,
expected_sandbox_type: codex_sandboxing::SandboxType,
tty: bool,
) -> Result<()> {
assert_remote_windows_sandbox_process_write(windows_sandbox_level, expected_sandbox_type).await
assert_remote_windows_sandbox_process_write(windows_sandbox_level, expected_sandbox_type, tty)
.await
}
#[test_case(false ; "local")]