mirror of
https://github.com/openai/codex.git
synced 2026-10-19 16:17:49 +00:00
Allow exec-server to proxy permitted private IPs upstream (#48568)
## Why Private IP destinations always bypassed inherited upstream proxies, preventing their use for private networks reachable through an upstream VPN proxy. ## What changed - Add `codex exec-server --proxy-private-ips-via-upstream`, also configurable with `CODEX_EXEC_SERVER_PROXY_PRIVATE_IPS_VIA_UPSTREAM=true`. The setting defaults to disabled. - Allow permitted RFC 1918, carrier-grade NAT, and IPv6 unique-local destinations to use an applicable upstream proxy. Loopback and link-local destinations retain direct routing, and destination access policy still applies. - Keep connections direct when no valid upstream proxy applies or `allow_upstream_proxy=false`. Errors after selecting an upstream proxy do not trigger a direct retry. - Rename `ExecServerRuntimePaths` to `ExecServerRuntimeOptions` and carry the routing setting from executor startup into the managed network proxy. ## Testing Add routing coverage for private address ranges, special-use addresses, and public targets with the option enabled and disabled. Verify that HTTP and CONNECT requests still enforce destination allowlists and denylists, and update the CLI help snapshot. GitOrigin-RevId: b7c9cc7da0e0f545694a6521b74c9b36b7b92769
This commit is contained in:
@@ -52,7 +52,7 @@ use codex_config::NoopThreadConfigLoader;
|
||||
use codex_core::config::Config;
|
||||
pub use codex_core::otel_init::build_provider as build_otel_provider;
|
||||
pub use codex_exec_server::EnvironmentManager;
|
||||
pub use codex_exec_server::ExecServerRuntimePaths;
|
||||
pub use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_feedback::CodexFeedback;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
@@ -2081,7 +2081,7 @@ mod tests {
|
||||
EnvironmentManager::create_for_tests(
|
||||
Some("ws://127.0.0.1:8765".to_string()),
|
||||
Some(
|
||||
ExecServerRuntimePaths::new(
|
||||
ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current exe"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! also handles the helper argv modes because exec-server re-execs
|
||||
//! `codex_self_exe` for sandboxed filesystem and process requests.
|
||||
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
use codex_http_client::OutboundProxyPolicy;
|
||||
use std::ffi::OsStr;
|
||||
@@ -29,7 +29,7 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let current_exe = std::env::current_exe()?;
|
||||
let codex_linux_sandbox_exe =
|
||||
std::env::var_os(CODEX_LINUX_SANDBOX_EXE_ENV_VAR).map(std::path::PathBuf::from);
|
||||
let runtime_paths = ExecServerRuntimePaths::new(current_exe, codex_linux_sandbox_exe)?;
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(current_exe, codex_linux_sandbox_exe)?;
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()?
|
||||
|
||||
@@ -61,7 +61,7 @@ use codex_core::ExecPolicyError;
|
||||
use codex_core::check_execpolicy_for_warnings;
|
||||
use codex_core::config::find_codex_home;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_features::Feature;
|
||||
use codex_feedback::CodexFeedback;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
@@ -518,7 +518,7 @@ pub async fn run_main_with_transport_options(
|
||||
)
|
||||
})?;
|
||||
let codex_home = find_codex_home()?;
|
||||
let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths(
|
||||
let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
|
||||
@@ -22,7 +22,11 @@ fn exec_server_help_documents_remote_options() {
|
||||
let command = MultitoolCli::command()
|
||||
.term_width(80)
|
||||
.mut_subcommand("exec-server", |command| {
|
||||
command.mut_arg("exit_on_stdin_close", |arg| arg.hide_env_values(true))
|
||||
command
|
||||
.mut_arg("exit_on_stdin_close", |arg| arg.hide_env_values(true))
|
||||
.mut_arg("proxy_private_ips_via_upstream", |arg| {
|
||||
arg.hide_env_values(true)
|
||||
})
|
||||
});
|
||||
let help = command
|
||||
.try_get_matches_from(["codex", "exec-server", "--help"])
|
||||
|
||||
@@ -14,7 +14,7 @@ use codex_core::config::ConfigLoadOptions;
|
||||
use codex_core::config::bootstrap_auth_config;
|
||||
use codex_core::config::find_codex_home;
|
||||
use codex_core::config::load_config_toml_with_layer_stack;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
use codex_http_client::OutboundProxyPolicy;
|
||||
use codex_login::AuthManager;
|
||||
@@ -48,6 +48,16 @@ pub(super) struct ExecServerCommand {
|
||||
#[arg(long, value_name = "MODE", default_value = "isolate", global = true)]
|
||||
linux_sandbox_pid_namespace: codex_sandboxing::LinuxSandboxPidNamespace,
|
||||
|
||||
/// Allow permitted private IP destinations to use the configured upstream proxy.
|
||||
/// If no valid upstream proxy applies to the request protocol, connect directly.
|
||||
/// Loopback stays local. This flag does not require upstream routing.
|
||||
#[arg(
|
||||
long,
|
||||
env = "CODEX_EXEC_SERVER_PROXY_PRIVATE_IPS_VIA_UPSTREAM",
|
||||
global = true
|
||||
)]
|
||||
proxy_private_ips_via_upstream: bool,
|
||||
|
||||
/// Maximum number of requests to process concurrently on each connection.
|
||||
#[arg(
|
||||
long = "concurrent-requests",
|
||||
@@ -164,11 +174,12 @@ impl ExecServerCommand {
|
||||
.codex_self_exe
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow::anyhow!("Codex executable path is not configured"))?;
|
||||
let runtime_paths = ExecServerRuntimePaths::new(
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(
|
||||
codex_self_exe,
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?
|
||||
.with_linux_sandbox_pid_namespace(self.linux_sandbox_pid_namespace);
|
||||
.with_linux_sandbox_pid_namespace(self.linux_sandbox_pid_namespace)
|
||||
.with_proxy_private_ips_via_upstream(self.proxy_private_ips_via_upstream);
|
||||
if let Some(base_url) = self.remote.take() {
|
||||
let environment_id = self.environment_id.take().ok_or_else(|| {
|
||||
anyhow::anyhow!("--environment-id is required when --remote is set")
|
||||
|
||||
+8
-1
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: cli/src/exec_server_args_tests.rs
|
||||
assertion_line: 36
|
||||
expression: help_text
|
||||
---
|
||||
[EXPERIMENTAL] Run the standalone exec-server service
|
||||
@@ -40,6 +39,14 @@ Options:
|
||||
|
||||
[default: isolate]
|
||||
|
||||
--proxy-private-ips-via-upstream
|
||||
Allow permitted private IP destinations to use the configured upstream
|
||||
proxy. If no valid upstream proxy applies to the request protocol,
|
||||
connect directly. Loopback stays local. This flag does not require
|
||||
upstream routing
|
||||
|
||||
[env: CODEX_EXEC_SERVER_PROXY_PRIVATE_IPS_VIA_UPSTREAM]
|
||||
|
||||
--concurrent-requests <COUNT>
|
||||
Maximum number of requests to process concurrently on each connection
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ pub use codex_exec_server::EnvironmentRegistryHarnessKeyValidationResponse;
|
||||
pub use codex_exec_server::EnvironmentRegistryRegistrationRequest;
|
||||
pub use codex_exec_server::EnvironmentRegistryRegistrationResponse;
|
||||
pub use codex_exec_server::ExecServerError;
|
||||
pub use codex_exec_server::ExecServerRuntimePaths;
|
||||
pub use codex_exec_server::ExecServerRuntimeOptions;
|
||||
pub use codex_exec_server::NoiseChannelIdentity;
|
||||
pub use codex_exec_server::NoiseChannelPublicKey;
|
||||
pub use codex_exec_server::NoiseRendezvousConnectBundle;
|
||||
|
||||
@@ -13,7 +13,7 @@ use codex_connectors::ConnectorDirectoryCacheKey;
|
||||
use codex_connectors::apps_config_from_layer_stack;
|
||||
use codex_connectors::connector_runtime_context_key;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_tools::DiscoverableTool;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::instrument;
|
||||
@@ -165,7 +165,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_options_and_status(
|
||||
// TODO: Wire callers that already own an EnvironmentManager into
|
||||
// list_accessible_connectors_from_mcp_tools_with_environment_manager instead
|
||||
// of constructing a temporary manager here.
|
||||
let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths(
|
||||
let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths(
|
||||
config.codex_self_exe.clone(),
|
||||
config.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
|
||||
@@ -1183,7 +1183,7 @@ mod tests {
|
||||
|
||||
use crate::config::PermissionProfileSnapshot;
|
||||
use codex_exec_server::Environment;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_exec_server::LOCAL_ENVIRONMENT_ID;
|
||||
use codex_exec_server::REMOTE_ENVIRONMENT_ID;
|
||||
use codex_exec_server_test_support::environment_manager_without_environments;
|
||||
@@ -1246,8 +1246,8 @@ mod tests {
|
||||
turn_environments
|
||||
}
|
||||
|
||||
fn test_runtime_paths() -> ExecServerRuntimePaths {
|
||||
ExecServerRuntimePaths::new(
|
||||
fn test_runtime_paths() -> ExecServerRuntimeOptions {
|
||||
ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current exe"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_extension_api::ExtensionRegistry;
|
||||
use codex_extension_api::UserInstructionsProvider;
|
||||
use codex_login::AuthManager;
|
||||
@@ -37,7 +37,7 @@ pub async fn build_prompt_input(
|
||||
.await
|
||||
.map_err(|err| CodexErr::Fatal(err.to_string()))?;
|
||||
|
||||
let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths(
|
||||
let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths(
|
||||
config.codex_self_exe.clone(),
|
||||
config.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
|
||||
@@ -737,7 +737,7 @@ impl TestCodexBuilder {
|
||||
);
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let codex_linux_sandbox_exe = None;
|
||||
let local_runtime_paths = codex_exec_server::ExecServerRuntimePaths::new(
|
||||
let local_runtime_paths = codex_exec_server::ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
codex_linux_sandbox_exe,
|
||||
)?;
|
||||
|
||||
@@ -7,7 +7,7 @@ use codex_core::StartThreadOptions;
|
||||
use codex_core::TurnInputRequest;
|
||||
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_exec_server::CreateDirectoryOptions;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_features::Feature;
|
||||
use codex_protocol::capabilities::CapabilityRootLocation;
|
||||
use codex_protocol::capabilities::SelectedCapabilityRoot;
|
||||
@@ -309,7 +309,7 @@ async fn executor_stop_hook_rejects_mismatched_environment() -> Result<()> {
|
||||
let executor_address = listener.local_addr()?;
|
||||
let executor_url = format!("ws://{executor_address}");
|
||||
drop(listener);
|
||||
let runtime_paths = ExecServerRuntimePaths::new(
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?;
|
||||
|
||||
@@ -22,7 +22,7 @@ use codex_exec_server::CopyOptions;
|
||||
use codex_exec_server::CreateDirectoryOptions;
|
||||
use codex_exec_server::EnvironmentReadyInfo;
|
||||
use codex_exec_server::ExecServerError;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_exec_server::FileSystemSandboxContext;
|
||||
use codex_exec_server::LOCAL_ENVIRONMENT_ID;
|
||||
use codex_exec_server::NoiseChannelPublicKey;
|
||||
@@ -2404,7 +2404,7 @@ async fn ready_before_selection_resolves_resumed_thread_capability_root_after_wa
|
||||
.mount(®istry)
|
||||
.await;
|
||||
|
||||
let runtime_paths = ExecServerRuntimePaths::new(
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?;
|
||||
|
||||
@@ -2929,7 +2929,7 @@ async fn production_turn_keeps_rebalanced_catalogs_stable_after_compaction_and_r
|
||||
let executor_address = listener.local_addr()?;
|
||||
let executor_url = format!("ws://{executor_address}");
|
||||
drop(listener);
|
||||
let runtime_paths = codex_exec_server::ExecServerRuntimePaths::new(
|
||||
let runtime_paths = codex_exec_server::ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?;
|
||||
|
||||
@@ -478,19 +478,29 @@ The crate exports:
|
||||
- `RemoteExecServerConnectArgs`
|
||||
- protocol request/response structs for process and filesystem RPCs
|
||||
- `DEFAULT_LISTEN_URL` and `ExecServerListenUrlParseError`
|
||||
- `ExecServerRuntimePaths`
|
||||
- `ExecServerRuntimeOptions`
|
||||
- `run_main()` for embedding the websocket server
|
||||
- `RemoteEnvironmentConfig` and `run_remote_environment()` for embedding remote
|
||||
registration mode
|
||||
|
||||
Callers must pass `ExecServerRuntimePaths` and an explicitly configured
|
||||
Callers must pass `ExecServerRuntimeOptions` and an explicitly configured
|
||||
`HttpClientFactory` to `run_main()`. The top-level `codex exec-server` command
|
||||
builds these paths from the `codex` arg0 dispatch state and resolves its HTTP
|
||||
client factory from the effective Codex configuration.
|
||||
builds the runtime options from the `codex` arg0 dispatch state and startup flags,
|
||||
and resolves its HTTP client factory from the effective Codex configuration.
|
||||
`RemoteEnvironmentConfig::new(...)` also takes the auth provider and HTTP client
|
||||
factory that remote registration mode should use; the CLI builds the auth
|
||||
provider from Codex auth state before starting remote mode.
|
||||
|
||||
`--proxy-private-ips-via-upstream` (or
|
||||
`CODEX_EXEC_SERVER_PROXY_PRIVATE_IPS_VIA_UPSTREAM=true`) allows permitted private IP
|
||||
destinations to use an inherited upstream proxy. If no valid proxy configuration
|
||||
applies to the request protocol, routing falls back to a direct connection. This
|
||||
includes unset, malformed, or unsupported proxy settings. Setting
|
||||
`allow_upstream_proxy=false` also keeps routing direct; loopback always stays local.
|
||||
Destination policy still applies to every route. Enforcing mandatory upstream routing
|
||||
would require a separate fail-closed mode. A connection failure after selecting an
|
||||
upstream proxy is returned as an error, without retrying directly.
|
||||
|
||||
## Example session
|
||||
|
||||
Initialize:
|
||||
|
||||
@@ -13,7 +13,7 @@ use tokio::task::JoinSet;
|
||||
use tokio_util::task::AbortOnDropHandle;
|
||||
|
||||
use super::*;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::NoiseChannelIdentity;
|
||||
use crate::ProcessId;
|
||||
use crate::noise_relay::stream_handler::NoiseOutboundMessage;
|
||||
@@ -148,7 +148,7 @@ impl Executor {
|
||||
validator: Validator,
|
||||
identity: NoiseChannelIdentity,
|
||||
) -> Result<Self> {
|
||||
let processor = ConnectionProcessor::new(ExecServerRuntimePaths::new(
|
||||
let processor = ConnectionProcessor::new(ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?);
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::CapabilityRootsDiscoverResponse;
|
||||
use crate::EnvironmentConfigReadParams;
|
||||
use crate::EnvironmentConfigReadResponse;
|
||||
use crate::ExecServerError;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ExecutorFileSystem;
|
||||
use crate::HttpClient;
|
||||
use crate::NoiseChannelIdentity;
|
||||
@@ -90,7 +90,7 @@ pub struct EnvironmentManager {
|
||||
default_environment: Option<String>,
|
||||
pub(super) environments: RwLock<HashMap<String, Arc<Environment>>>,
|
||||
local_environment: Option<Arc<Environment>>,
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
local_runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
http_client_factory: HttpClientFactory,
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ impl EnvironmentManager {
|
||||
/// Builds a test-only manager from a raw exec-server URL value.
|
||||
pub async fn create_for_tests(
|
||||
exec_server_url: Option<String>,
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
local_runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
) -> Self {
|
||||
let provider = DefaultEnvironmentProvider::new(exec_server_url);
|
||||
match Self::from_snapshot(
|
||||
@@ -195,7 +195,7 @@ impl EnvironmentManager {
|
||||
/// Builds a manager from `CODEX_HOME` with an explicit outbound HTTP policy.
|
||||
pub async fn from_codex_home(
|
||||
codex_home: impl AsRef<std::path::Path>,
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
local_runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<Self, ExecServerError> {
|
||||
Self::prepare_from_codex_home(codex_home)
|
||||
@@ -216,7 +216,7 @@ impl EnvironmentManager {
|
||||
|
||||
/// Builds a manager from environment variables with an explicit outbound HTTP policy.
|
||||
pub async fn from_env(
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
local_runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<Self, ExecServerError> {
|
||||
Self::prepare_from_env()
|
||||
@@ -226,7 +226,7 @@ impl EnvironmentManager {
|
||||
|
||||
pub(crate) fn from_noise_environment_config(
|
||||
config: NoiseRendezvousEnvironmentConfig,
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
local_runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<Self, ExecServerError> {
|
||||
let connect_provider = config.into_connect_provider(http_client_factory.clone())?;
|
||||
@@ -254,7 +254,7 @@ impl EnvironmentManager {
|
||||
/// allowing tests to select the local environment explicitly.
|
||||
pub async fn create_for_tests_with_local(
|
||||
exec_server_url: Option<String>,
|
||||
local_runtime_paths: ExecServerRuntimePaths,
|
||||
local_runtime_paths: ExecServerRuntimeOptions,
|
||||
) -> Self {
|
||||
let mut snapshot = DefaultEnvironmentProvider::new(exec_server_url).snapshot_inner();
|
||||
snapshot.include_local = true;
|
||||
@@ -271,7 +271,7 @@ impl EnvironmentManager {
|
||||
|
||||
pub(crate) fn from_snapshot(
|
||||
snapshot: EnvironmentProviderSnapshot,
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
local_runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<Self, ExecServerError> {
|
||||
let EnvironmentProviderSnapshot {
|
||||
@@ -675,7 +675,7 @@ pub struct Environment {
|
||||
exec_backend: Arc<dyn ExecBackend>,
|
||||
filesystem: Arc<dyn ExecutorFileSystem>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
local_runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
@@ -707,7 +707,7 @@ impl Environment {
|
||||
/// Builds an environment using the caller's effective outbound HTTP policy.
|
||||
pub fn create(
|
||||
exec_server_url: Option<String>,
|
||||
local_runtime_paths: ExecServerRuntimePaths,
|
||||
local_runtime_paths: ExecServerRuntimeOptions,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<Self, ExecServerError> {
|
||||
Self::create_inner(
|
||||
@@ -730,7 +730,7 @@ impl Environment {
|
||||
/// local runtime paths used when creating local filesystem helpers.
|
||||
fn create_inner(
|
||||
exec_server_url: Option<String>,
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
local_runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<Self, ExecServerError> {
|
||||
let (exec_server_url, disabled) = normalize_exec_server_url(exec_server_url);
|
||||
@@ -757,7 +757,7 @@ impl Environment {
|
||||
}
|
||||
|
||||
pub(crate) fn local(
|
||||
local_runtime_paths: ExecServerRuntimePaths,
|
||||
local_runtime_paths: ExecServerRuntimeOptions,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -778,7 +778,7 @@ impl Environment {
|
||||
|
||||
pub(crate) fn remote_with_transport(
|
||||
remote_transport: ExecServerTransportParams,
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
local_runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Self {
|
||||
let client = LazyRemoteExecServerClient::new(remote_transport, http_client_factory);
|
||||
@@ -787,7 +787,7 @@ impl Environment {
|
||||
|
||||
pub(crate) fn remote_with_client(
|
||||
client: LazyRemoteExecServerClient,
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
local_runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
) -> Self {
|
||||
let exec_backend: Arc<dyn ExecBackend> = Arc::new(RemoteProcess::new(client.clone()));
|
||||
let filesystem: Arc<dyn ExecutorFileSystem> =
|
||||
@@ -890,7 +890,7 @@ impl Environment {
|
||||
.map(LazyRemoteExecServerClient::subscribe_connection_state)
|
||||
}
|
||||
|
||||
pub fn local_runtime_paths(&self) -> Option<&ExecServerRuntimePaths> {
|
||||
pub fn local_runtime_paths(&self) -> Option<&ExecServerRuntimeOptions> {
|
||||
self.local_runtime_paths.as_ref()
|
||||
}
|
||||
|
||||
@@ -1171,7 +1171,7 @@ mod tests {
|
||||
use super::LOCAL_ENVIRONMENT_ID;
|
||||
use super::REMOTE_ENVIRONMENT_ID;
|
||||
use super::noise_environment_config_from_values;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ProcessId;
|
||||
use crate::client_api::DEFAULT_REMOTE_EXEC_SERVER_CONNECT_TIMEOUT;
|
||||
use crate::client_api::ExecServerTransportParams;
|
||||
@@ -1196,8 +1196,8 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
fn test_runtime_paths() -> ExecServerRuntimePaths {
|
||||
ExecServerRuntimePaths::new(
|
||||
fn test_runtime_paths() -> ExecServerRuntimeOptions {
|
||||
ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current exe"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ use codex_http_client::HttpClientFactory;
|
||||
|
||||
use crate::EnvironmentManager;
|
||||
use crate::ExecServerError;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::environment_provider::EnvironmentDefault;
|
||||
use crate::environment_provider::EnvironmentProviderSnapshot;
|
||||
use crate::remote::NoiseRendezvousEnvironmentConfig;
|
||||
@@ -41,7 +41,7 @@ impl PreparedEnvironmentManager {
|
||||
/// Builds the manager and starts remote connections using the supplied policy.
|
||||
pub fn build(
|
||||
self,
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
local_runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<EnvironmentManager, ExecServerError> {
|
||||
match self.source {
|
||||
|
||||
@@ -37,7 +37,7 @@ use tokio::io::AsyncBufReadExt;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::FileSystemSandboxContext;
|
||||
use crate::fs_helper::CODEX_FS_HELPER_ARG1;
|
||||
use crate::fs_helper::FsHelperPayload;
|
||||
@@ -69,12 +69,12 @@ struct SandboxCwd {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct FileSystemSandboxRunner {
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
helper_env: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl FileSystemSandboxRunner {
|
||||
pub(crate) fn new(runtime_paths: ExecServerRuntimePaths) -> Self {
|
||||
pub(crate) fn new(runtime_paths: ExecServerRuntimeOptions) -> Self {
|
||||
Self {
|
||||
runtime_paths,
|
||||
helper_env: helper_env(),
|
||||
@@ -229,7 +229,7 @@ fn native_workspace_root(root: &PathUri) -> Result<AbsolutePathBuf, JSONRPCError
|
||||
})
|
||||
}
|
||||
|
||||
fn helper_read_roots(runtime_paths: &ExecServerRuntimePaths) -> Vec<AbsolutePathBuf> {
|
||||
fn helper_read_roots(runtime_paths: &ExecServerRuntimeOptions) -> Vec<AbsolutePathBuf> {
|
||||
let mut roots = vec![runtime_paths.codex_self_exe.clone()];
|
||||
if let Some(path) = &runtime_paths.codex_linux_sandbox_exe
|
||||
&& !roots.contains(path)
|
||||
@@ -581,7 +581,7 @@ mod tests {
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
|
||||
use super::FileSystemSandboxRunner;
|
||||
use super::SandboxCwd;
|
||||
@@ -622,7 +622,7 @@ mod tests {
|
||||
fn helper_permissions_preserve_existing_writes() {
|
||||
let codex_self_exe = std::env::current_exe().expect("current exe");
|
||||
let runtime_paths =
|
||||
ExecServerRuntimePaths::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None)
|
||||
ExecServerRuntimeOptions::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None)
|
||||
.expect("runtime paths");
|
||||
let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir().as_path())
|
||||
.expect("absolute cwd");
|
||||
@@ -743,7 +743,7 @@ mod tests {
|
||||
let path = path.to_string_lossy().into_owned();
|
||||
let codex_self_exe = std::env::current_exe().expect("current exe");
|
||||
let runtime_paths =
|
||||
ExecServerRuntimePaths::new(codex_self_exe.clone(), Some(codex_self_exe))
|
||||
ExecServerRuntimeOptions::new(codex_self_exe.clone(), Some(codex_self_exe))
|
||||
.expect("runtime paths");
|
||||
let runner = FileSystemSandboxRunner::new(runtime_paths);
|
||||
let native_cwd = AbsolutePathBuf::current_dir().expect("cwd");
|
||||
@@ -799,7 +799,7 @@ mod tests {
|
||||
fn sandbox_exec_request_uses_filesystem_root_and_preserves_policy_cwd() {
|
||||
let codex_self_exe = std::env::current_exe().expect("current exe");
|
||||
let runtime_paths =
|
||||
ExecServerRuntimePaths::new(codex_self_exe.clone(), Some(codex_self_exe))
|
||||
ExecServerRuntimeOptions::new(codex_self_exe.clone(), Some(codex_self_exe))
|
||||
.expect("runtime paths");
|
||||
let runner = FileSystemSandboxRunner::new(runtime_paths);
|
||||
let selected = tempfile::tempdir().expect("selected directory");
|
||||
@@ -889,7 +889,7 @@ mod tests {
|
||||
fn sandbox_exec_request_binds_windows_relative_globs_to_policy_cwd() {
|
||||
let codex_self_exe = std::env::current_exe().expect("current exe");
|
||||
let runtime_paths =
|
||||
ExecServerRuntimePaths::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None)
|
||||
ExecServerRuntimeOptions::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None)
|
||||
.expect("runtime paths");
|
||||
let runner = FileSystemSandboxRunner::new(runtime_paths);
|
||||
let selected = tempfile::tempdir().expect("selected directory");
|
||||
@@ -979,7 +979,7 @@ mod tests {
|
||||
fn helper_permissions_include_only_the_helper_executable() {
|
||||
let codex_self_exe = std::env::current_exe().expect("current exe");
|
||||
let runtime_paths =
|
||||
ExecServerRuntimePaths::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None)
|
||||
ExecServerRuntimeOptions::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None)
|
||||
.expect("runtime paths");
|
||||
let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir().as_path())
|
||||
.expect("absolute cwd");
|
||||
@@ -1012,7 +1012,7 @@ mod tests {
|
||||
let codex_self_exe = root.path().join("bin").join("codex");
|
||||
let codex_linux_sandbox_exe = root.path().join("aliases").join("codex-linux-sandbox");
|
||||
let runtime_paths =
|
||||
ExecServerRuntimePaths::new(codex_self_exe, Some(codex_linux_sandbox_exe))
|
||||
ExecServerRuntimeOptions::new(codex_self_exe, Some(codex_linux_sandbox_exe))
|
||||
.expect("runtime paths");
|
||||
let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir().as_path())
|
||||
.expect("absolute cwd");
|
||||
|
||||
@@ -39,7 +39,7 @@ mod resolved_capability;
|
||||
mod rpc;
|
||||
mod rpc_server_requests;
|
||||
mod rpc_timing;
|
||||
mod runtime_paths;
|
||||
mod runtime_options;
|
||||
mod sandbox_selection;
|
||||
mod sandboxed_file_open;
|
||||
mod sandboxed_file_system;
|
||||
@@ -228,7 +228,7 @@ pub use remote::run_remote_environment_forward_until_shutdown;
|
||||
pub use remote::run_remote_environment_until_shutdown;
|
||||
pub use resolved_capability::ResolvedSelectedCapabilityRoot;
|
||||
pub use resolved_capability::SelectedCapabilityRootsStatus;
|
||||
pub use runtime_paths::ExecServerRuntimePaths;
|
||||
pub use runtime_options::ExecServerRuntimeOptions;
|
||||
pub use server::ConcurrentRequestLimit;
|
||||
pub use server::DEFAULT_LISTEN_URL;
|
||||
pub use server::ExecServerListenUrlParseError;
|
||||
|
||||
@@ -20,7 +20,7 @@ use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::CopyOptions;
|
||||
use crate::CreateDirectoryOptions;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ExecutorFileSystem;
|
||||
use crate::ExecutorFileSystemFuture;
|
||||
use crate::FILE_READ_CHUNK_SIZE;
|
||||
@@ -76,7 +76,7 @@ impl LocalFileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_runtime_paths(runtime_paths: ExecServerRuntimePaths) -> Self {
|
||||
pub fn with_runtime_paths(runtime_paths: ExecServerRuntimeOptions) -> Self {
|
||||
Self {
|
||||
unsandboxed: UnsandboxedFileSystem::default(),
|
||||
sandboxed: Some(SandboxedFileSystem::new(runtime_paths)),
|
||||
|
||||
@@ -40,7 +40,7 @@ use crate::ExecProcessEvent;
|
||||
use crate::ExecProcessEventReceiver;
|
||||
use crate::ExecProcessFuture;
|
||||
use crate::ExecServerError;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ProcessId;
|
||||
use crate::StartedExecProcess;
|
||||
use crate::network_policy_decisions::network_policy_decider;
|
||||
@@ -174,7 +174,7 @@ struct Inner {
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct LocalProcess {
|
||||
inner: Arc<Inner>,
|
||||
runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
}
|
||||
|
||||
struct LocalExecProcess {
|
||||
@@ -191,11 +191,11 @@ impl Default for LocalProcess {
|
||||
}
|
||||
|
||||
impl LocalProcess {
|
||||
pub(crate) fn with_local_runtime_paths(runtime_paths: ExecServerRuntimePaths) -> Self {
|
||||
pub(crate) fn with_local_runtime_paths(runtime_paths: ExecServerRuntimeOptions) -> Self {
|
||||
Self::with_discarded_notifications(Some(runtime_paths))
|
||||
}
|
||||
|
||||
fn with_discarded_notifications(runtime_paths: Option<ExecServerRuntimePaths>) -> Self {
|
||||
fn with_discarded_notifications(runtime_paths: Option<ExecServerRuntimeOptions>) -> Self {
|
||||
let (outgoing_tx, mut outgoing_rx) =
|
||||
mpsc::channel::<RpcServerOutboundMessage>(NOTIFICATION_CHANNEL_CAPACITY);
|
||||
tokio::spawn(async move { while outgoing_rx.recv().await.is_some() {} });
|
||||
@@ -209,7 +209,7 @@ impl LocalProcess {
|
||||
pub(crate) fn new(
|
||||
notifications: RpcNotificationSender,
|
||||
telemetry: ExecServerTelemetry,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
) -> Self {
|
||||
Self::with_runtime_paths(notifications, telemetry, Some(runtime_paths))
|
||||
}
|
||||
@@ -217,7 +217,7 @@ impl LocalProcess {
|
||||
fn with_runtime_paths(
|
||||
notifications: RpcNotificationSender,
|
||||
telemetry: ExecServerTelemetry,
|
||||
runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
runtime_paths: Option<ExecServerRuntimeOptions>,
|
||||
) -> Self {
|
||||
let requests = notifications.request_sender();
|
||||
Self {
|
||||
|
||||
@@ -11,7 +11,7 @@ use tokio::time::timeout;
|
||||
|
||||
use super::ClosedNoiseVirtualStream;
|
||||
use super::spawn_noise_virtual_stream;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::connection::CHANNEL_CAPACITY;
|
||||
use crate::noise_channel::InitiatorHandshake;
|
||||
use crate::noise_channel::NoiseChannelIdentity;
|
||||
@@ -92,7 +92,7 @@ async fn processor_exit_reports_closed_virtual_stream() -> Result<()> {
|
||||
let mut stream = spawn_noise_virtual_stream(
|
||||
"stream-1".to_string(),
|
||||
/*instance_id*/ 7,
|
||||
ConnectionProcessor::new(ExecServerRuntimePaths::new(
|
||||
ConnectionProcessor::new(ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?),
|
||||
|
||||
@@ -35,7 +35,7 @@ use codex_utils_path_uri::PathUri;
|
||||
|
||||
#[cfg(unix)]
|
||||
use crate::CODEX_ARG0_EXEC_HELPER_ARG1;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::process_telemetry::trace_process_id;
|
||||
use crate::protocol::ExecParams;
|
||||
use crate::rpc::internal_error;
|
||||
@@ -86,7 +86,7 @@ impl PreparedExecRequest {
|
||||
pub(crate) async fn prepare_exec_request_with_telemetry(
|
||||
params: &ExecParams,
|
||||
env: HashMap<String, String>,
|
||||
runtime_paths: Option<&ExecServerRuntimePaths>,
|
||||
runtime_paths: Option<&ExecServerRuntimeOptions>,
|
||||
network_policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
|
||||
network_policy_audit_observer: Option<NetworkPolicyAuditObserver>,
|
||||
telemetry: &ProcessTelemetry,
|
||||
@@ -123,15 +123,9 @@ pub(crate) async fn prepare_exec_request_with_telemetry(
|
||||
|
||||
let (env, managed_network, network_proxy_handle, network_proxy_restricting_sid) =
|
||||
prepare_managed_network(
|
||||
params.managed_network.as_ref(),
|
||||
params,
|
||||
network_proxy,
|
||||
if params.sandbox.as_ref().is_some_and(|sandbox| {
|
||||
sandbox.windows_sandbox_selection == WindowsSandboxSelection::Mxc
|
||||
}) {
|
||||
ManagedProxyRouting::DedicatedListeners
|
||||
} else {
|
||||
ManagedProxyRouting::SharedIngress
|
||||
},
|
||||
runtime_paths.is_some_and(|paths| paths.proxy_private_ips_via_upstream),
|
||||
env,
|
||||
network_policy_decider,
|
||||
network_policy_audit_observer,
|
||||
@@ -332,9 +326,9 @@ pub(crate) async fn prepare_exec_request_with_telemetry(
|
||||
}
|
||||
|
||||
async fn prepare_managed_network(
|
||||
managed_network: Option<&ManagedNetworkSandboxContext>,
|
||||
params: &ExecParams,
|
||||
network_proxy: Option<&RemoteNetworkProxyLaunchConfig>,
|
||||
routing: ManagedProxyRouting,
|
||||
proxy_private_ips_via_upstream: bool,
|
||||
env: HashMap<String, String>,
|
||||
network_policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
|
||||
network_policy_audit_observer: Option<NetworkPolicyAuditObserver>,
|
||||
@@ -349,13 +343,22 @@ async fn prepare_managed_network(
|
||||
JSONRPCErrorError,
|
||||
> {
|
||||
let Some(network_proxy) = network_proxy.cloned() else {
|
||||
return Ok((env, managed_network.cloned(), None, None));
|
||||
return Ok((env, params.managed_network.clone(), None, None));
|
||||
};
|
||||
let routing =
|
||||
if params.sandbox.as_ref().is_some_and(|sandbox| {
|
||||
sandbox.windows_sandbox_selection == WindowsSandboxSelection::Mxc
|
||||
}) {
|
||||
ManagedProxyRouting::DedicatedListeners
|
||||
} else {
|
||||
ManagedProxyRouting::SharedIngress
|
||||
};
|
||||
let mut state = NetworkProxyState::from_remote_launch_config(
|
||||
network_proxy,
|
||||
codex_utils_path_uri::Platform::native(),
|
||||
)
|
||||
.map_err(|err| invalid_params(format!("invalid network proxy config: {err}")))?;
|
||||
state.set_proxy_private_ips_via_upstream(proxy_private_ips_via_upstream);
|
||||
if let Some(observer) = network_policy_audit_observer {
|
||||
state.set_policy_audit_observer(observer);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ use super::prepare_exec_request_with_telemetry;
|
||||
#[cfg(unix)]
|
||||
use crate::CODEX_ARG0_EXEC_HELPER_ARG1;
|
||||
use crate::ExecParams;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
#[cfg(any(unix, windows))]
|
||||
use crate::FileSystemSandboxContext;
|
||||
use crate::ProcessId;
|
||||
@@ -49,7 +49,7 @@ use crate::process_telemetry::ProcessTelemetry;
|
||||
async fn prepare_exec_request(
|
||||
params: &ExecParams,
|
||||
env: HashMap<String, String>,
|
||||
runtime_paths: Option<&ExecServerRuntimePaths>,
|
||||
runtime_paths: Option<&ExecServerRuntimeOptions>,
|
||||
network_policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
|
||||
network_policy_audit_observer: Option<NetworkPolicyAuditObserver>,
|
||||
) -> Result<PreparedExecRequest, JSONRPCErrorError> {
|
||||
@@ -72,7 +72,7 @@ async fn sandbox_request_wraps_native_argv_on_executor() {
|
||||
let cwd_uri = PathUri::from_abs_path(&cwd);
|
||||
let self_exe = std::env::current_exe().expect("current executable");
|
||||
let runtime_paths =
|
||||
ExecServerRuntimePaths::new(self_exe.clone(), Some(self_exe)).expect("runtime paths");
|
||||
ExecServerRuntimeOptions::new(self_exe.clone(), Some(self_exe)).expect("runtime paths");
|
||||
let sandbox = FileSystemSandboxContext::from_permission_profile(
|
||||
PermissionProfile::workspace_write(),
|
||||
cwd_uri.clone(),
|
||||
@@ -192,7 +192,7 @@ async fn sandbox_request_routes_custom_arg0_to_inner_helper() {
|
||||
let cwd_uri = PathUri::from_abs_path(&cwd);
|
||||
let self_exe = std::env::current_exe().expect("current executable");
|
||||
let runtime_paths =
|
||||
ExecServerRuntimePaths::new(self_exe.clone(), Some(self_exe)).expect("runtime paths");
|
||||
ExecServerRuntimeOptions::new(self_exe.clone(), Some(self_exe)).expect("runtime paths");
|
||||
let sandbox = FileSystemSandboxContext::from_permission_profile(
|
||||
PermissionProfile::workspace_write(),
|
||||
cwd_uri.clone(),
|
||||
@@ -246,7 +246,7 @@ async fn sandbox_request_routes_custom_arg0_to_inner_helper() {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn managed_network_sandbox_request() -> (ExecParams, ExecServerRuntimePaths) {
|
||||
fn managed_network_sandbox_request() -> (ExecParams, ExecServerRuntimeOptions) {
|
||||
let cwd: AbsolutePathBuf = std::env::current_dir()
|
||||
.expect("current directory")
|
||||
.try_into()
|
||||
@@ -254,7 +254,7 @@ fn managed_network_sandbox_request() -> (ExecParams, ExecServerRuntimePaths) {
|
||||
let cwd_uri = PathUri::from_abs_path(&cwd);
|
||||
let self_exe = std::env::current_exe().expect("current executable");
|
||||
let runtime_paths =
|
||||
ExecServerRuntimePaths::new(self_exe.clone(), Some(self_exe)).expect("runtime paths");
|
||||
ExecServerRuntimeOptions::new(self_exe.clone(), Some(self_exe)).expect("runtime paths");
|
||||
let sandbox = FileSystemSandboxContext::from_permission_profile(
|
||||
PermissionProfile::workspace_write(),
|
||||
cwd_uri.clone(),
|
||||
@@ -646,7 +646,7 @@ async fn managed_network_honors_windows_sandbox_level(windows_sandbox_level: Win
|
||||
.expect("absolute cwd");
|
||||
let cwd_uri = PathUri::from_abs_path(&cwd);
|
||||
let self_exe = std::env::current_exe().expect("current executable");
|
||||
let runtime_paths = ExecServerRuntimePaths::new(self_exe, None).expect("runtime paths");
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(self_exe, None).expect("runtime paths");
|
||||
let permissions = PermissionProfile::read_only();
|
||||
let mut sandbox =
|
||||
FileSystemSandboxContext::from_permission_profile(permissions.clone(), cwd_uri.clone());
|
||||
|
||||
@@ -1116,7 +1116,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn multiplexed_environment_sends_keepalive() -> anyhow::Result<()> {
|
||||
let (client_websocket, mut server_websocket) = websocket_pair().await?;
|
||||
let runtime_paths = crate::ExecServerRuntimePaths::new(
|
||||
let runtime_paths = crate::ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ use super::NOISE_HANDSHAKE_FAILURE_COOLDOWN;
|
||||
use super::RendezvousDisconnectReason;
|
||||
use super::run_multiplexed_environment;
|
||||
use crate::ExecServerError;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::connection::JsonRpcConnectionEvent;
|
||||
use crate::noise_channel::InitiatorHandshake;
|
||||
use crate::noise_channel::NoiseChannelIdentity;
|
||||
@@ -140,7 +140,7 @@ async fn missing_pong_disconnects_physical_relay() -> Result<()> {
|
||||
|
||||
let environment_task = tokio::spawn(run_multiplexed_environment(
|
||||
environment_websocket,
|
||||
ConnectionProcessor::new(ExecServerRuntimePaths::new(
|
||||
ConnectionProcessor::new(ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?),
|
||||
@@ -171,7 +171,7 @@ async fn pong_keeps_physical_relay_connected() -> Result<()> {
|
||||
|
||||
let environment_task = tokio::spawn(run_multiplexed_environment(
|
||||
environment_websocket,
|
||||
ConnectionProcessor::new(ExecServerRuntimePaths::new(
|
||||
ConnectionProcessor::new(ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?),
|
||||
@@ -245,7 +245,7 @@ async fn processor_exit_resets_noise_harness_stream() -> Result<()> {
|
||||
let environment_task = AbortOnDropHandle::new(tokio::spawn(run_multiplexed_environment(
|
||||
environment_websocket,
|
||||
ObservedRegistration(
|
||||
ConnectionProcessor::new(ExecServerRuntimePaths::new(
|
||||
ConnectionProcessor::new(ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?),
|
||||
@@ -315,7 +315,7 @@ async fn pending_harness_key_validation_does_not_block_new_handshakes() -> Resul
|
||||
let calls = Arc::new(AtomicUsize::new(0));
|
||||
let environment_task = tokio::spawn(run_multiplexed_environment(
|
||||
environment_websocket,
|
||||
ConnectionProcessor::new(ExecServerRuntimePaths::new(
|
||||
ConnectionProcessor::new(ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?),
|
||||
@@ -369,7 +369,7 @@ async fn duplicate_handshakes_pause_admission_without_disconnecting() -> Result<
|
||||
let release = Arc::new(Notify::new());
|
||||
let environment_task = tokio::spawn(run_multiplexed_environment(
|
||||
environment_websocket,
|
||||
ConnectionProcessor::new(ExecServerRuntimePaths::new(
|
||||
ConnectionProcessor::new(ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?),
|
||||
@@ -478,7 +478,7 @@ async fn oversized_harness_authorization_is_rejected_before_validation() -> Resu
|
||||
let calls = Arc::new(AtomicUsize::new(0));
|
||||
let environment_task = tokio::spawn(run_multiplexed_environment(
|
||||
environment_websocket,
|
||||
ConnectionProcessor::new(ExecServerRuntimePaths::new(
|
||||
ConnectionProcessor::new(ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?),
|
||||
@@ -697,7 +697,7 @@ async fn early_data_during_validation_pauses_admission_without_disconnecting() -
|
||||
let harness_identity = NoiseChannelIdentity::generate()?;
|
||||
let environment_task = tokio::spawn(run_multiplexed_environment(
|
||||
environment_websocket,
|
||||
ConnectionProcessor::new(ExecServerRuntimePaths::new(
|
||||
ConnectionProcessor::new(ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?),
|
||||
|
||||
@@ -33,7 +33,7 @@ use crate::EnvironmentRegistryHarnessKeyValidationResponse;
|
||||
use crate::EnvironmentRegistryRegistrationRequest;
|
||||
use crate::EnvironmentRegistryRegistrationResponse;
|
||||
use crate::ExecServerError;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ExecServerTelemetry;
|
||||
use crate::NoiseChannelIdentity;
|
||||
use crate::NoiseChannelPublicKey;
|
||||
@@ -603,7 +603,7 @@ impl RemoteEnvironmentConfig {
|
||||
/// conflict refreshes the registration; other permanent client errors stop the runner.
|
||||
pub async fn run_remote_environment(
|
||||
config: RemoteEnvironmentConfig,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
) -> Result<(), ExecServerError> {
|
||||
run_remote_environment_until_shutdown(config, runtime_paths, std::future::pending()).await
|
||||
}
|
||||
@@ -613,7 +613,7 @@ pub async fn run_remote_environment(
|
||||
/// Active sessions and their processes are drained before this function returns.
|
||||
pub async fn run_remote_environment_until_shutdown<F>(
|
||||
config: RemoteEnvironmentConfig,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
shutdown: F,
|
||||
) -> Result<(), ExecServerError>
|
||||
where
|
||||
|
||||
@@ -28,7 +28,7 @@ use wiremock::matchers::method;
|
||||
use wiremock::matchers::path;
|
||||
|
||||
use super::*;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::RemoteEnvironmentTransport;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -324,7 +324,7 @@ async fn direct_registration_handles_policy_outages_and_permanent_failures() ->
|
||||
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault)
|
||||
.with_network_policy(policy.clone().for_current_account()),
|
||||
)?;
|
||||
let runtime_paths = ExecServerRuntimePaths::new(
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?;
|
||||
@@ -414,7 +414,7 @@ async fn direct_websocket_reuses_registration_and_stops_on_permanent_errors() ->
|
||||
Arc::new(StaticAuthProvider),
|
||||
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
||||
)?;
|
||||
let runtime_paths = ExecServerRuntimePaths::new(
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?;
|
||||
|
||||
@@ -188,7 +188,7 @@ async fn reconnect_reuses_registration_until_url_is_rejected() -> Result<()> {
|
||||
)?;
|
||||
let environment_task = tokio::spawn(run_remote_environment(
|
||||
config,
|
||||
ExecServerRuntimePaths::new(
|
||||
ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?,
|
||||
|
||||
+13
-2
@@ -1,3 +1,5 @@
|
||||
//! Executor-local paths and trusted startup settings used when preparing execution.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codex_sandboxing::LinuxSandboxPidNamespace;
|
||||
@@ -5,7 +7,7 @@ use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
/// Paths and sandbox settings initialized when creating an executor.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ExecServerRuntimePaths {
|
||||
pub struct ExecServerRuntimeOptions {
|
||||
/// Stable path to the Codex executable used to launch hidden helper modes.
|
||||
pub codex_self_exe: AbsolutePathBuf,
|
||||
/// Path to the Linux sandbox helper alias used when the platform sandbox
|
||||
@@ -13,12 +15,14 @@ pub struct ExecServerRuntimePaths {
|
||||
pub codex_linux_sandbox_exe: Option<AbsolutePathBuf>,
|
||||
/// Trusted startup policy; requests and repository config cannot change PID isolation.
|
||||
pub linux_sandbox_pid_namespace: LinuxSandboxPidNamespace,
|
||||
/// Trusted startup routing; request policy still controls destination access.
|
||||
pub proxy_private_ips_via_upstream: bool,
|
||||
/// User-config opt-out of writable-root symlink checks beneath this host's home.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub allowed_symlinked_codex_home: Option<AbsolutePathBuf>,
|
||||
}
|
||||
|
||||
impl ExecServerRuntimePaths {
|
||||
impl ExecServerRuntimeOptions {
|
||||
pub fn from_optional_paths(
|
||||
codex_self_exe: Option<PathBuf>,
|
||||
codex_linux_sandbox_exe: Option<PathBuf>,
|
||||
@@ -38,6 +42,7 @@ impl ExecServerRuntimePaths {
|
||||
) -> std::io::Result<Self> {
|
||||
Ok(Self {
|
||||
linux_sandbox_pid_namespace: LinuxSandboxPidNamespace::default(),
|
||||
proxy_private_ips_via_upstream: false,
|
||||
codex_self_exe: absolute_path(codex_self_exe)?,
|
||||
codex_linux_sandbox_exe: codex_linux_sandbox_exe.map(absolute_path).transpose()?,
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -51,6 +56,12 @@ impl ExecServerRuntimePaths {
|
||||
self
|
||||
}
|
||||
|
||||
/// Routes permitted private IPs through the executor's configured upstream proxy.
|
||||
pub fn with_proxy_private_ips_via_upstream(mut self, enabled: bool) -> Self {
|
||||
self.proxy_private_ips_via_upstream = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Applies the symlink opt-in resolved by the execution host's config loader.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn with_allowed_symlinked_codex_home(
|
||||
@@ -10,7 +10,7 @@ use crate::CapabilityRootsDiscoverResponse;
|
||||
use crate::CopyOptions;
|
||||
use crate::CreateDirectoryOptions;
|
||||
use crate::DiscoverV2CapabilitiesResponse;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ExecutorFileSystem;
|
||||
use crate::ExecutorFileSystemFuture;
|
||||
use crate::FILE_READ_CHUNK_SIZE;
|
||||
@@ -96,7 +96,7 @@ impl SandboxedFileSystem {
|
||||
.map_err(map_sandbox_error)
|
||||
}
|
||||
|
||||
pub fn new(runtime_paths: ExecServerRuntimePaths) -> Self {
|
||||
pub fn new(runtime_paths: ExecServerRuntimeOptions) -> Self {
|
||||
Self {
|
||||
sandbox_runner: FileSystemSandboxRunner::new(runtime_paths),
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ fn sandbox_routing_uses_independent_read_write_policies_and_executor_temp_rules(
|
||||
|
||||
#[tokio::test]
|
||||
async fn sandboxed_file_system_rejects_non_native_uri_as_invalid_input() {
|
||||
let runtime_paths = ExecServerRuntimePaths::new(
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current exe"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -20,14 +20,14 @@ pub use request_dispatcher::RequestDispatchMode;
|
||||
pub use transport::DEFAULT_LISTEN_URL;
|
||||
pub use transport::ExecServerListenUrlParseError;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ExecServerTelemetry;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
use codex_websocket_auth::WebsocketAuthSettings;
|
||||
|
||||
pub async fn run_main(
|
||||
listen_url: &str,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
run_main_with_telemetry(
|
||||
@@ -48,7 +48,7 @@ pub async fn run_main(
|
||||
)]
|
||||
pub async fn run_main_with_telemetry(
|
||||
listen_url: &str,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
request_dispatch_mode: RequestDispatchMode,
|
||||
@@ -77,7 +77,7 @@ mod tests {
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
use super::run_main_with_telemetry;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ExecServerTelemetry;
|
||||
|
||||
#[tokio::test]
|
||||
@@ -93,7 +93,7 @@ mod tests {
|
||||
tracing::callsite::rebuild_interest_cache();
|
||||
run_main_with_telemetry(
|
||||
"invalid",
|
||||
ExecServerRuntimePaths::new(
|
||||
ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current executable"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::CapabilityRootsDiscoverParams;
|
||||
use crate::CapabilityRootsDiscoverResponse;
|
||||
use crate::CopyOptions;
|
||||
use crate::CreateDirectoryOptions;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ExecutorFileSystem;
|
||||
use crate::GetMetadataOptions;
|
||||
use crate::ReadFileOptions;
|
||||
@@ -59,7 +59,7 @@ pub(crate) struct FileSystemHandler {
|
||||
}
|
||||
|
||||
impl FileSystemHandler {
|
||||
pub(crate) fn new(runtime_paths: ExecServerRuntimePaths) -> Self {
|
||||
pub(crate) fn new(runtime_paths: ExecServerRuntimeOptions) -> Self {
|
||||
Self {
|
||||
file_system: LocalFileSystem::with_runtime_paths(runtime_paths),
|
||||
file_reads: FileReadHandleManager::default(),
|
||||
@@ -369,7 +369,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn no_platform_sandbox_policies_do_not_require_configured_sandbox_helper() {
|
||||
let temp_dir = tempfile::tempdir().expect("tempdir");
|
||||
let runtime_paths = ExecServerRuntimePaths::new(
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current exe"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ use tokio::sync::Mutex;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tokio_util::task::TaskTracker;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::client::http_client::PendingRouteAwareHttpBodyStream;
|
||||
use crate::client::http_client::RouteAwareHttpClient;
|
||||
use crate::client::http_client::RouteAwareHttpRequestRunner;
|
||||
@@ -83,7 +83,7 @@ pub(crate) struct ExecServerHandler {
|
||||
background_task_shutdown: CancellationToken,
|
||||
background_tasks: TaskTracker,
|
||||
file_system: FileSystemHandler,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
http_client: RouteAwareHttpClient,
|
||||
initialize_requested: AtomicBool,
|
||||
initialized: AtomicBool,
|
||||
@@ -93,7 +93,7 @@ impl ExecServerHandler {
|
||||
pub(crate) fn new(
|
||||
session_registry: Arc<SessionRegistry>,
|
||||
notifications: RpcNotificationSender,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -10,7 +10,7 @@ use tokio::sync::mpsc;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::ExecServerHandler;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ProcessId;
|
||||
use crate::protocol::ExecParams;
|
||||
use crate::protocol::InitializeParams;
|
||||
@@ -77,8 +77,8 @@ fn windows_command_processor() -> String {
|
||||
std::env::var("COMSPEC").unwrap_or_else(|_| "cmd.exe".to_string())
|
||||
}
|
||||
|
||||
fn test_runtime_paths() -> ExecServerRuntimePaths {
|
||||
ExecServerRuntimePaths::new(
|
||||
fn test_runtime_paths() -> ExecServerRuntimeOptions {
|
||||
ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current exe"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::process_telemetry::ProcessTelemetry;
|
||||
use codex_exec_server_protocol::JSONRPCErrorError;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::local_process::LocalProcess;
|
||||
use crate::protocol::ExecParams;
|
||||
use crate::protocol::ExecResponse;
|
||||
@@ -25,7 +25,7 @@ impl ProcessHandler {
|
||||
pub(crate) fn new(
|
||||
notifications: RpcNotificationSender,
|
||||
telemetry: ExecServerTelemetry,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
process: LocalProcess::new(notifications, telemetry, runtime_paths),
|
||||
|
||||
@@ -37,7 +37,7 @@ use wiremock::matchers::method;
|
||||
use super::ExecServerHandler;
|
||||
use super::registry::build_router;
|
||||
use super::session_registry::SessionRegistry;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ExecServerTelemetry;
|
||||
use crate::connection::JsonRpcConnectionEvent;
|
||||
use crate::protocol::EXEC_METHOD;
|
||||
@@ -739,7 +739,7 @@ fn new_handler(
|
||||
let mut handler = ExecServerHandler::new(
|
||||
Arc::clone(sessions),
|
||||
RpcNotificationSender::new(outgoing),
|
||||
ExecServerRuntimePaths::new(
|
||||
ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("test executable"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ use tokio_util::task::AbortOnDropHandle;
|
||||
use tracing::debug;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::LocalFileSystem;
|
||||
use crate::connection::CHANNEL_CAPACITY;
|
||||
use crate::connection::JsonRpcConnection;
|
||||
@@ -38,7 +38,7 @@ pub(crate) struct ConnectionProcessor {
|
||||
capability_manager: Arc<CapabilityManager>,
|
||||
capability_prewarm: Arc<AbortOnDropHandle<()>>,
|
||||
session_registry: Arc<SessionRegistry>,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
request_dispatch_mode: RequestDispatchMode,
|
||||
@@ -46,7 +46,7 @@ pub(crate) struct ConnectionProcessor {
|
||||
|
||||
impl ConnectionProcessor {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn new(runtime_paths: ExecServerRuntimePaths) -> Self {
|
||||
pub(crate) fn new(runtime_paths: ExecServerRuntimeOptions) -> Self {
|
||||
Self::new_with_location_request(
|
||||
runtime_paths,
|
||||
ExecServerTelemetry::default(),
|
||||
@@ -62,7 +62,7 @@ impl ConnectionProcessor {
|
||||
}
|
||||
|
||||
pub(crate) fn new_with_telemetry(
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
request_dispatch_mode: RequestDispatchMode,
|
||||
@@ -83,7 +83,7 @@ impl ConnectionProcessor {
|
||||
}
|
||||
|
||||
fn new_with_location_request(
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
request_dispatch_mode: RequestDispatchMode,
|
||||
@@ -342,7 +342,7 @@ mod tests {
|
||||
|
||||
use super::complete_queued_client_responses;
|
||||
use super::run_connection;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ProcessId;
|
||||
use crate::connection::JsonRpcConnection;
|
||||
use crate::connection::JsonRpcConnectionEvent;
|
||||
@@ -372,7 +372,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn startup_prewarms_without_a_client_connection() -> anyhow::Result<()> {
|
||||
let paths = ExecServerRuntimePaths::new(
|
||||
let paths = ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?;
|
||||
@@ -637,8 +637,8 @@ mod tests {
|
||||
(client_writer, BufReader::new(client_reader).lines(), task)
|
||||
}
|
||||
|
||||
fn test_runtime_paths() -> ExecServerRuntimePaths {
|
||||
ExecServerRuntimePaths::new(
|
||||
fn test_runtime_paths() -> ExecServerRuntimeOptions {
|
||||
ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current exe"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ use super::ConcurrentRequestLimit;
|
||||
use super::RequestDispatchMode;
|
||||
use super::RequestDispatcher;
|
||||
use super::RequestTaskResult;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::connection::JsonRpcConnectionEvent;
|
||||
use crate::rpc::RpcNotificationSender;
|
||||
use crate::rpc::RpcRouter;
|
||||
@@ -178,7 +178,7 @@ async fn request_queue_waits_for_dispatcher_admission_before_recording_telemetry
|
||||
let handler = Arc::new(ExecServerHandler::new(
|
||||
SessionRegistry::new(telemetry.clone()),
|
||||
notifications,
|
||||
ExecServerRuntimePaths::new(
|
||||
ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current executable"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
@@ -387,7 +387,7 @@ fn request_telemetry_fixture(router: RpcRouter<ExecServerHandler>) -> RequestTel
|
||||
let handler = Arc::new(ExecServerHandler::new(
|
||||
SessionRegistry::new(telemetry.clone()),
|
||||
notifications,
|
||||
ExecServerRuntimePaths::new(
|
||||
ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current executable"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ use codex_exec_server_protocol::JSONRPCErrorError;
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::rpc::RpcNotificationSender;
|
||||
use crate::rpc::invalid_request;
|
||||
use crate::rpc::session_already_attached;
|
||||
@@ -64,7 +64,7 @@ impl SessionRegistry {
|
||||
self: &Arc<Self>,
|
||||
resume_session_id: Option<String>,
|
||||
notifications: RpcNotificationSender,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
) -> Result<SessionHandle, JSONRPCErrorError> {
|
||||
enum AttachOutcome {
|
||||
Attached(Arc<SessionEntry>),
|
||||
|
||||
@@ -30,7 +30,7 @@ use tokio::net::TcpListener;
|
||||
use tracing::info;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::ExecServerTelemetry;
|
||||
use crate::connection::JsonRpcConnection;
|
||||
use crate::server::RequestDispatchMode;
|
||||
@@ -91,7 +91,7 @@ pub(crate) fn parse_listen_url(
|
||||
|
||||
pub(crate) async fn run_transport(
|
||||
listen_url: &str,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
request_dispatch_mode: RequestDispatchMode,
|
||||
@@ -129,7 +129,7 @@ pub(crate) async fn run_transport(
|
||||
}
|
||||
|
||||
async fn run_stdio_connection(
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
request_dispatch_mode: RequestDispatchMode,
|
||||
@@ -148,7 +148,7 @@ async fn run_stdio_connection(
|
||||
async fn run_stdio_connection_with_io<R, W>(
|
||||
reader: R,
|
||||
writer: W,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
request_dispatch_mode: RequestDispatchMode,
|
||||
@@ -177,7 +177,7 @@ where
|
||||
|
||||
async fn run_websocket_listener(
|
||||
bind_address: SocketAddr,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
runtime_paths: ExecServerRuntimeOptions,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
request_dispatch_mode: RequestDispatchMode,
|
||||
|
||||
@@ -17,7 +17,7 @@ use super::DEFAULT_LISTEN_URL;
|
||||
use super::ExecServerListenTransport;
|
||||
use super::parse_listen_url;
|
||||
use super::run_stdio_connection_with_io;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecServerRuntimeOptions;
|
||||
use crate::protocol::INITIALIZE_METHOD;
|
||||
use crate::protocol::INITIALIZED_METHOD;
|
||||
use crate::protocol::InitializeParams;
|
||||
@@ -163,8 +163,8 @@ async fn write_jsonrpc_line(writer: &mut tokio::io::DuplexStream, message: &JSON
|
||||
.expect("JSON-RPC newline should write");
|
||||
}
|
||||
|
||||
fn test_runtime_paths() -> ExecServerRuntimePaths {
|
||||
ExecServerRuntimePaths::new(
|
||||
fn test_runtime_paths() -> ExecServerRuntimeOptions {
|
||||
ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current exe"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! when a test only needs a WebSocket executor endpoint. It handles the arg0
|
||||
//! helper mode because sandboxed process requests re-exec this binary.
|
||||
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
use codex_http_client::OutboundProxyPolicy;
|
||||
use std::ffi::OsStr;
|
||||
@@ -28,7 +28,7 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let current_exe = std::env::current_exe()?;
|
||||
let codex_linux_sandbox_exe =
|
||||
std::env::var_os(CODEX_LINUX_SANDBOX_EXE_ENV_VAR).map(std::path::PathBuf::from);
|
||||
let runtime_paths = ExecServerRuntimePaths::new(current_exe, codex_linux_sandbox_exe)?;
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(current_exe, codex_linux_sandbox_exe)?;
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()?
|
||||
|
||||
@@ -24,7 +24,7 @@ use codex_exec_server::ExecParams;
|
||||
use codex_exec_server::ExecProcessEvent;
|
||||
use codex_exec_server::ExecResponse;
|
||||
use codex_exec_server::ExecServerClientConnectOptions;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_exec_server::InitializeParams;
|
||||
use codex_exec_server::InitializeResponse;
|
||||
use codex_exec_server::ProcessId;
|
||||
@@ -174,7 +174,7 @@ async fn accepted_websocket_interoperates_and_recovers_with_real_direct_executor
|
||||
http_client_factory.clone(),
|
||||
)?;
|
||||
let (codex_exe, codex_linux_sandbox_exe) = common::current_test_binary_helper_paths()?;
|
||||
let runtime_paths = ExecServerRuntimePaths::new(codex_exe, codex_linux_sandbox_exe)?;
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(codex_exe, codex_linux_sandbox_exe)?;
|
||||
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel();
|
||||
let executor_task = AbortOnDropHandle::new(tokio::spawn(
|
||||
codex_exec_server::run_remote_environment_until_shutdown(
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::time::Duration;
|
||||
|
||||
use codex_exec_server::CODEX_ARG0_EXEC_HELPER_ARG1;
|
||||
use codex_exec_server::CODEX_FS_HELPER_ARG1;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_exec_server::ExecServerTelemetry;
|
||||
use codex_exec_server::RequestDispatchMode;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
@@ -214,7 +214,7 @@ fn maybe_run_exec_server_from_test_binary(guard: Option<&TestBinaryDispatchGuard
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let runtime_paths = match ExecServerRuntimePaths::new(
|
||||
let runtime_paths = match ExecServerRuntimeOptions::new(
|
||||
current_exe.clone(),
|
||||
linux_sandbox_exe(guard, ¤t_exe),
|
||||
) {
|
||||
|
||||
@@ -40,7 +40,7 @@ async fn shell_snapshot_concurrent_replays_keep_independent_readers(
|
||||
let (exe, linux_sandbox) = current_test_binary_helper_paths()?;
|
||||
let environment = Environment::create(
|
||||
/*exec_server_url*/ None,
|
||||
codex_exec_server::ExecServerRuntimePaths::new(exe, linux_sandbox)?,
|
||||
codex_exec_server::ExecServerRuntimeOptions::new(exe, linux_sandbox)?,
|
||||
codex_http_client::HttpClientFactory::new(
|
||||
codex_http_client::OutboundProxyPolicy::ReqwestDefault,
|
||||
),
|
||||
|
||||
@@ -5,7 +5,7 @@ use codex_exec_server::CreateDirectoryOptions;
|
||||
use codex_exec_server::EnvironmentAccess;
|
||||
use codex_exec_server::EnvironmentAccessExt;
|
||||
#[cfg(unix)]
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_exec_server::ExecutorFileSystem;
|
||||
use codex_exec_server::FILE_READ_CHUNK_SIZE;
|
||||
use codex_exec_server::FileMetadata;
|
||||
@@ -1155,7 +1155,7 @@ async fn sandboxed_file_operations_cannot_read_helper_siblings() -> Result<()> {
|
||||
None
|
||||
};
|
||||
let file_system =
|
||||
LocalFileSystem::with_runtime_paths(ExecServerRuntimePaths::new(helper, linux_sandbox)?);
|
||||
LocalFileSystem::with_runtime_paths(ExecServerRuntimeOptions::new(helper, linux_sandbox)?);
|
||||
|
||||
let sibling = runtime_dir.join("credentials.json");
|
||||
std::fs::write(&sibling, "secret")?;
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use codex_exec_server::Environment;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_exec_server::ExecutorFileSystem;
|
||||
use codex_exec_server::FileSystemSandboxContext;
|
||||
use codex_exec_server::LocalFileSystem;
|
||||
@@ -50,7 +50,7 @@ pub(crate) async fn create_file_system_context(
|
||||
match implementation {
|
||||
FileSystemImplementation::Local => {
|
||||
let helper_paths = test_codex_helper_paths()?;
|
||||
let runtime_paths = ExecServerRuntimePaths::new(
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(
|
||||
helper_paths.codex_exe.clone(),
|
||||
helper_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
|
||||
@@ -22,7 +22,7 @@ use codex_exec_server::EnvironmentReadyInfo;
|
||||
use codex_exec_server::ExecParams;
|
||||
use codex_exec_server::ExecResponse;
|
||||
use codex_exec_server::ExecServerError;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_exec_server::FsReadFileParams;
|
||||
use codex_exec_server::NoiseChannelPublicKey;
|
||||
use codex_exec_server::NoiseRendezvousConnectBundle;
|
||||
@@ -162,7 +162,7 @@ async fn failed_noise_environment_recovers_and_reconnects_after_ready_report() -
|
||||
.await;
|
||||
|
||||
let (codex_exe, codex_linux_sandbox_exe) = common::current_test_binary_helper_paths()?;
|
||||
let runtime_paths = ExecServerRuntimePaths::new(codex_exe, codex_linux_sandbox_exe)?;
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(codex_exe, codex_linux_sandbox_exe)?;
|
||||
let http_client_factory = HttpClientFactory::new(OutboundProxyPolicy::RespectSystemProxy);
|
||||
let config = RemoteEnvironmentConfig::new(
|
||||
registry.uri(),
|
||||
@@ -361,7 +361,7 @@ async fn next_connection_state(
|
||||
async fn remote_environment_routes_encrypted_exec_server_rpc() -> Result<()> {
|
||||
let relay = RelayTest::new().await?;
|
||||
let (codex_exe, codex_linux_sandbox_exe) = common::current_test_binary_helper_paths()?;
|
||||
let runtime_paths = ExecServerRuntimePaths::new(codex_exe, codex_linux_sandbox_exe)?;
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(codex_exe, codex_linux_sandbox_exe)?;
|
||||
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel();
|
||||
let remote_environment = AbortOnDropHandle::new(tokio::spawn(
|
||||
codex_exec_server::run_remote_environment_until_shutdown(
|
||||
|
||||
@@ -76,7 +76,7 @@ impl RegistryFixture {
|
||||
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
||||
)?;
|
||||
let (codex_exe, sandbox_exe) = common::current_test_binary_helper_paths()?;
|
||||
let runtime_paths = ExecServerRuntimePaths::new(codex_exe, sandbox_exe)?;
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(codex_exe, sandbox_exe)?;
|
||||
let (shutdown_tx, shutdown_rx) = oneshot::channel();
|
||||
let task = AbortOnDropHandle::new(tokio::spawn(
|
||||
codex_exec_server::run_remote_environment_until_shutdown(
|
||||
|
||||
@@ -17,7 +17,7 @@ pub use cli::Command;
|
||||
pub use cli::ReviewArgs;
|
||||
use codex_app_server_client::DEFAULT_IN_PROCESS_CHANNEL_CAPACITY;
|
||||
use codex_app_server_client::EnvironmentManager;
|
||||
use codex_app_server_client::ExecServerRuntimePaths;
|
||||
use codex_app_server_client::ExecServerRuntimeOptions;
|
||||
use codex_app_server_client::InProcessAppServerClient;
|
||||
use codex_app_server_client::InProcessClientStartArgs;
|
||||
use codex_app_server_client::InProcessServerEvent;
|
||||
@@ -686,7 +686,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
range: None,
|
||||
})
|
||||
.collect();
|
||||
let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths(
|
||||
let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
|
||||
@@ -20,7 +20,7 @@ pub(super) async fn fork_source(
|
||||
let state_db = codex_core::init_state_db(config).await;
|
||||
let environment_manager = EnvironmentManager::from_codex_home(
|
||||
config.codex_home.clone(),
|
||||
Some(ExecServerRuntimePaths::from_optional_paths(
|
||||
Some(ExecServerRuntimeOptions::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?),
|
||||
|
||||
@@ -478,7 +478,7 @@ where
|
||||
}
|
||||
};
|
||||
let proxy = if allow_upstream_proxy {
|
||||
proxy_for_connect(&authority)
|
||||
proxy_for_connect(&authority, &app_state)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -1819,6 +1819,55 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn private_ip_upstream_routing_preserves_destination_policy() {
|
||||
for (allowlisted, denied, expected_error) in [
|
||||
(true, true, "blocked-by-denylist"),
|
||||
(false, false, "blocked-by-allowlist"),
|
||||
] {
|
||||
let mut policy = NetworkProxyConfig {
|
||||
allow_local_binding: Some(true),
|
||||
..NetworkProxyConfig::default()
|
||||
};
|
||||
if allowlisted {
|
||||
policy.set_allowed_domains(vec!["100.68.58.50".to_string()]);
|
||||
}
|
||||
if denied {
|
||||
policy.set_denied_domains(vec!["100.68.58.50".to_string()]);
|
||||
}
|
||||
let mut state = network_proxy_state_for_policy(policy);
|
||||
state.set_proxy_private_ips_via_upstream(/*enabled*/ true);
|
||||
let state = Arc::new(state);
|
||||
for method in [Method::GET, Method::CONNECT] {
|
||||
let mut req = Request::builder()
|
||||
.method(method.clone())
|
||||
.uri("http://100.68.58.50:80/api/items")
|
||||
.header("host", "100.68.58.50:80")
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
req.extensions_mut().insert(state.clone());
|
||||
let response = if method == Method::CONNECT {
|
||||
http_connect_accept(
|
||||
/*policy_decider*/ None, /*environment_id*/ None, req,
|
||||
)
|
||||
.await
|
||||
.unwrap_err()
|
||||
} else {
|
||||
http_plain_proxy(
|
||||
/*policy_decider*/ None, /*environment_id*/ None, req,
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
};
|
||||
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||
assert_eq!(
|
||||
response.headers().get("x-proxy-error").unwrap(),
|
||||
expected_error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn http_plain_proxy_rejects_absolute_uri_host_header_mismatch() {
|
||||
let state = Arc::new(network_proxy_state_for_policy(NetworkProxyConfig::default()));
|
||||
|
||||
@@ -90,6 +90,18 @@ fn ipv4_in_cidr(ip: Ipv4Addr, base: [u8; 4], prefix: u8) -> bool {
|
||||
(ip & mask) == (base & mask)
|
||||
}
|
||||
|
||||
/// Private unicast ranges that may be reachable through an upstream VPN proxy.
|
||||
/// Loopback, link-local, and other special-use addresses retain their normal routing.
|
||||
pub(crate) fn is_private_network_ip(ip: IpAddr) -> bool {
|
||||
match ip {
|
||||
IpAddr::V4(ip) => ip.is_private() || ipv4_in_cidr(ip, [100, 64, 0, 0], /*prefix*/ 10),
|
||||
IpAddr::V6(ip) => match ip.to_ipv4() {
|
||||
Some(ip) => is_private_network_ip(IpAddr::V4(ip)),
|
||||
None => ip.is_unique_local(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_non_public_ipv6(ip: Ipv6Addr) -> bool {
|
||||
if let Some(v4) = ip.to_ipv4() {
|
||||
return is_non_public_ipv4(v4) || ip.is_loopback();
|
||||
|
||||
@@ -262,6 +262,7 @@ pub struct NetworkProxyState {
|
||||
state: Arc<RwLock<ConfigState>>,
|
||||
/// Belongs to this proxy; config retains omission for launches on other executors.
|
||||
pub(crate) local_binding_policy: LocalBindingPolicy,
|
||||
pub(crate) proxy_private_ips_via_upstream: bool,
|
||||
reloader: Arc<dyn ConfigReloader>,
|
||||
blocked_request_observer: Arc<RwLock<Option<Arc<dyn BlockedRequestObserver>>>>,
|
||||
pub(crate) policy_audit_observer: Option<NetworkPolicyAuditObserver>,
|
||||
@@ -300,6 +301,7 @@ impl Clone for NetworkProxyState {
|
||||
Self {
|
||||
state: self.state.clone(),
|
||||
local_binding_policy: self.local_binding_policy,
|
||||
proxy_private_ips_via_upstream: self.proxy_private_ips_via_upstream,
|
||||
reloader: self.reloader.clone(),
|
||||
blocked_request_observer: self.blocked_request_observer.clone(),
|
||||
policy_audit_observer: self.policy_audit_observer.clone(),
|
||||
@@ -315,6 +317,12 @@ impl Clone for NetworkProxyState {
|
||||
}
|
||||
|
||||
impl NetworkProxyState {
|
||||
/// Selects routing for permitted private IPs without changing destination policy.
|
||||
/// Set by the execution host, independently of reloadable or remote policy.
|
||||
pub fn set_proxy_private_ips_via_upstream(&mut self, enabled: bool) {
|
||||
self.proxy_private_ips_via_upstream = enabled;
|
||||
}
|
||||
|
||||
/// Builds runtime state for one executor-local proxy launch.
|
||||
/// The launching executor supplies its own OS; the wire policy cannot override it.
|
||||
pub fn from_remote_launch_config(
|
||||
@@ -397,6 +405,7 @@ impl NetworkProxyState {
|
||||
credential_broker,
|
||||
state: Arc::new(RwLock::new(state)),
|
||||
local_binding_policy,
|
||||
proxy_private_ips_via_upstream: false,
|
||||
reloader,
|
||||
blocked_request_observer: Arc::new(RwLock::new(blocked_request_observer)),
|
||||
policy_audit_observer: None,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::connect_policy::TargetCheckedTcpConnector;
|
||||
use crate::connect_policy::is_non_public_target;
|
||||
use crate::policy::is_private_network_ip;
|
||||
use crate::state::NetworkProxyState;
|
||||
use codex_utils_rustls_provider::ensure_rustls_crypto_provider;
|
||||
use rama_core::Layer;
|
||||
@@ -18,6 +19,7 @@ use rama_http::layer::version_adapter::RequestVersionAdapter;
|
||||
use rama_http_backend::client::HttpClientService;
|
||||
use rama_http_backend::client::HttpConnector;
|
||||
use rama_http_backend::client::proxy::layer::HttpProxyConnectorLayer;
|
||||
use rama_net::address::Host;
|
||||
use rama_net::address::HostWithPort;
|
||||
use rama_net::address::ProxyAddress;
|
||||
use rama_net::client::EstablishedClientConnection;
|
||||
@@ -39,14 +41,20 @@ struct ProxyConfig {
|
||||
http: Option<ProxyAddress>,
|
||||
https: Option<ProxyAddress>,
|
||||
all: Option<ProxyAddress>,
|
||||
proxy_private_ips_via_upstream: bool,
|
||||
}
|
||||
|
||||
impl ProxyConfig {
|
||||
fn from_env() -> Self {
|
||||
fn from_env(proxy_private_ips_via_upstream: bool) -> Self {
|
||||
let http = read_proxy_env(&["HTTP_PROXY", "http_proxy"]);
|
||||
let https = read_proxy_env(&["HTTPS_PROXY", "https_proxy"]);
|
||||
let all = read_proxy_env(&["ALL_PROXY", "all_proxy"]);
|
||||
Self { http, https, all }
|
||||
Self {
|
||||
http,
|
||||
https,
|
||||
all,
|
||||
proxy_private_ips_via_upstream,
|
||||
}
|
||||
}
|
||||
|
||||
fn proxy_for_protocol(&self, is_secure: bool) -> Option<ProxyAddress> {
|
||||
@@ -61,7 +69,9 @@ impl ProxyConfig {
|
||||
}
|
||||
|
||||
fn proxy_for_target(&self, target: &HostWithPort, is_secure: bool) -> Option<ProxyAddress> {
|
||||
if is_non_public_target(&target.host) {
|
||||
let proxy_private_ip = self.proxy_private_ips_via_upstream
|
||||
&& matches!(&target.host, Host::Address(ip) if is_private_network_ip(*ip));
|
||||
if is_non_public_target(&target.host) && !proxy_private_ip {
|
||||
return None;
|
||||
}
|
||||
self.proxy_for_protocol(is_secure)
|
||||
@@ -97,8 +107,12 @@ fn read_proxy_env(keys: &[&str]) -> Option<ProxyAddress> {
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn proxy_for_connect(target: &HostWithPort) -> Option<ProxyAddress> {
|
||||
ProxyConfig::from_env().proxy_for_target(target, /*is_secure*/ true)
|
||||
pub(crate) fn proxy_for_connect(
|
||||
target: &HostWithPort,
|
||||
state: &NetworkProxyState,
|
||||
) -> Option<ProxyAddress> {
|
||||
ProxyConfig::from_env(state.proxy_private_ips_via_upstream)
|
||||
.proxy_for_target(target, /*is_secure*/ true)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -122,7 +136,7 @@ impl UpstreamClient {
|
||||
|
||||
pub(crate) fn from_env_proxy(state: Arc<NetworkProxyState>) -> Self {
|
||||
Self::new(
|
||||
ProxyConfig::from_env(),
|
||||
ProxyConfig::from_env(state.proxy_private_ips_via_upstream),
|
||||
TargetCheckedTcpConnector::new(state),
|
||||
client_root_certs(),
|
||||
)
|
||||
@@ -144,7 +158,7 @@ impl UpstreamClient {
|
||||
tls_root_store: Arc<rustls::RootCertStore>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
ProxyConfig::from_env(),
|
||||
ProxyConfig::from_env(state.proxy_private_ips_via_upstream),
|
||||
TargetCheckedTcpConnector::new(state),
|
||||
tls_root_store,
|
||||
)
|
||||
|
||||
@@ -119,27 +119,43 @@ async fn mitm_upstream_client_trusts_startup_custom_ca() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inherited_upstream_proxy_is_bypassed_for_non_public_targets() {
|
||||
fn private_ip_upstream_routing_is_opt_in() {
|
||||
let proxy = ProxyAddress::try_from("http://127.0.0.1:43128").unwrap();
|
||||
let config = ProxyConfig {
|
||||
http: Some(proxy.clone()),
|
||||
https: Some(proxy),
|
||||
all: None,
|
||||
};
|
||||
|
||||
for target in [
|
||||
HostWithPort::new(Host::LOCALHOST_NAME, 8080),
|
||||
HostWithPort::new(Host::LOCALHOST_IPV4, 8080),
|
||||
HostWithPort::new(Host::Address("10.0.0.1".parse().unwrap()), 8080),
|
||||
] {
|
||||
assert_eq!(config.proxy_for_target(&target, /*is_secure*/ false), None);
|
||||
assert_eq!(config.proxy_for_target(&target, /*is_secure*/ true), None);
|
||||
for enabled in [false, true] {
|
||||
let config = ProxyConfig {
|
||||
http: Some(proxy.clone()),
|
||||
https: Some(proxy.clone()),
|
||||
proxy_private_ips_via_upstream: enabled,
|
||||
..ProxyConfig::default()
|
||||
};
|
||||
for (host, use_proxy) in [
|
||||
("10.0.0.1", enabled),
|
||||
("172.16.0.1", enabled),
|
||||
("192.168.0.1", enabled),
|
||||
("100.68.58.50", enabled),
|
||||
("fd00::1", enabled),
|
||||
("::ffff:100.68.58.50", enabled),
|
||||
("localhost", false),
|
||||
("127.0.0.1", false),
|
||||
("::1", false),
|
||||
("169.254.169.254", false),
|
||||
("fe80::1", false),
|
||||
("0.0.0.0", false),
|
||||
("224.0.0.1", false),
|
||||
("192.0.2.1", false),
|
||||
("240.0.0.1", false),
|
||||
("example.com", true),
|
||||
("100.63.255.255", true),
|
||||
("100.128.0.0", true),
|
||||
] {
|
||||
let target = HostWithPort::new(host.parse::<Host>().unwrap(), /*port*/ 443);
|
||||
for is_secure in [false, true] {
|
||||
assert_eq!(
|
||||
config.proxy_for_target(&target, is_secure),
|
||||
use_proxy.then(|| proxy.clone()),
|
||||
"host={host}, enabled={enabled}, secure={is_secure}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let public = HostWithPort::new(Host::EXAMPLE_NAME, 443);
|
||||
assert!(
|
||||
config
|
||||
.proxy_for_target(&public, /*is_secure*/ true)
|
||||
.is_some()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ use codex_core_api::ConfigRequirementsToml;
|
||||
use codex_core_api::Constrained;
|
||||
use codex_core_api::EnvironmentManager;
|
||||
use codex_core_api::EventMsg;
|
||||
use codex_core_api::ExecServerRuntimePaths;
|
||||
use codex_core_api::ExecServerRuntimeOptions;
|
||||
use codex_core_api::ExtensionRegistryBuilder;
|
||||
use codex_core_api::Feature;
|
||||
use codex_core_api::Features;
|
||||
@@ -125,7 +125,7 @@ async fn run_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
|
||||
|
||||
let auth_manager =
|
||||
AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ false).await?;
|
||||
let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths(
|
||||
let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths(
|
||||
config.codex_self_exe.clone(),
|
||||
config.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
|
||||
@@ -7713,7 +7713,7 @@ async fn remote_exec_resume_current_cwd_is_rejected() -> Result<()> {
|
||||
app.environment_manager = Arc::new(
|
||||
EnvironmentManager::create_for_tests(
|
||||
Some("ws://127.0.0.1:8765".to_string()),
|
||||
Some(codex_exec_server::ExecServerRuntimePaths::new(
|
||||
Some(codex_exec_server::ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?),
|
||||
@@ -7960,7 +7960,7 @@ async fn in_app_resume_uses_configured_or_explicit_cwd() -> Result<()> {
|
||||
Arc::new(
|
||||
EnvironmentManager::create_for_tests(
|
||||
Some("ws://127.0.0.1:8765".to_string()),
|
||||
Some(codex_exec_server::ExecServerRuntimePaths::new(
|
||||
Some(codex_exec_server::ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?),
|
||||
|
||||
@@ -52,7 +52,7 @@ use codex_config::LoaderOverrides;
|
||||
use codex_config::format_config_error_with_source;
|
||||
use codex_config::types::ResumeCwdMode;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_features::Feature;
|
||||
use codex_login::AuthConfig;
|
||||
use codex_login::default_client::originator;
|
||||
@@ -2625,7 +2625,7 @@ requires_openai_auth = {requires_openai_auth}
|
||||
.cloud_config_bundle(cloud_config_bundle)
|
||||
.build()
|
||||
.await?;
|
||||
let runtime_paths = ExecServerRuntimePaths::new(
|
||||
let runtime_paths = ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe()?,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?;
|
||||
@@ -3638,7 +3638,7 @@ requires_openai_auth = {requires_openai_auth}
|
||||
let target = AppServerTarget::Embedded;
|
||||
let environment_manager = EnvironmentManager::create_for_tests(
|
||||
Some("ws://127.0.0.1:8765".to_string()),
|
||||
Some(ExecServerRuntimePaths::new(
|
||||
Some(ExecServerRuntimeOptions::new(
|
||||
std::env::current_exe().expect("current exe"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)?),
|
||||
|
||||
@@ -25,7 +25,7 @@ use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::ConfigLoadOptions;
|
||||
use codex_config::LoaderOverrides;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_exec_server::ExecServerRuntimeOptions;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_utils_cli::CliConfigOverrides;
|
||||
use codex_utils_home_dir::find_codex_home;
|
||||
@@ -274,7 +274,7 @@ pub(super) async fn start_app_server_for_session_command(
|
||||
.clone()
|
||||
.filter(|_| app_server_target.uses_remote_workspace());
|
||||
|
||||
let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths(
|
||||
let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)
|
||||
|
||||
@@ -318,7 +318,7 @@ pub(super) async fn run_main_inner(
|
||||
.clone()
|
||||
.filter(|_| app_server_target.uses_remote_workspace());
|
||||
|
||||
let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths(
|
||||
let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
|
||||
@@ -139,7 +139,7 @@ pub(super) async fn prepare(
|
||||
color_eyre::eyre::bail!("`--worktree` is only supported for local sessions");
|
||||
}
|
||||
let environment = prepared.build(
|
||||
Some(ExecServerRuntimePaths::from_optional_paths(
|
||||
Some(ExecServerRuntimeOptions::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?),
|
||||
|
||||
Reference in New Issue
Block a user