mirror of
https://github.com/openai/codex.git
synced 2026-09-05 15:18:41 +00:00
## What changed - Add client-side handling for exec-server network policy requests, including request validation, per-process decision routing, and allow, deny, or ask responses. - Bound concurrent callbacks and fail closed when requests are invalid, exceed capacity, time out, or outlive their process session. - Replace the callback opt-in flag with a launch-level `policyDecisionTimeoutMs` value and include transport overhead in the executor timeout. - Preserve callback admission limits across connection recovery and clean up policy state when process startup is abandoned or a session ends. ## Testing - Cover decision forwarding, invalid and excess requests, cancellation during process cleanup, abandoned starts, timeout propagation, and launch-config serialization. GitOrigin-RevId: 6ee2a0139e0f8170cf1a5cdd646bd811b7d3c1f6
98 lines
4.1 KiB
Rust
98 lines
4.1 KiB
Rust
use std::sync::Arc;
|
|
use std::sync::RwLock;
|
|
use std::time::Duration;
|
|
|
|
use codex_network_proxy::NetworkDecision;
|
|
use codex_network_proxy::NetworkPolicyDecider;
|
|
use codex_network_proxy::NetworkPolicyRequest;
|
|
use codex_network_proxy::NetworkProtocol;
|
|
use tokio_util::sync::CancellationToken;
|
|
|
|
use crate::ProcessId;
|
|
use crate::protocol::ExecServerNetworkPolicyDecision;
|
|
use crate::protocol::ExecServerNetworkPolicyRequest;
|
|
use crate::protocol::ExecServerNetworkProtocol;
|
|
use crate::protocol::MAX_NETWORK_POLICY_HOST_BYTES;
|
|
use crate::protocol::MAX_NETWORK_POLICY_REASON_BYTES;
|
|
use crate::protocol::NETWORK_POLICY_REQUEST_METHOD;
|
|
use crate::protocol::NetworkPolicyRequestParams;
|
|
use crate::protocol::NetworkPolicyRequestResponse;
|
|
use crate::rpc_server_requests::RpcServerRequestSender;
|
|
|
|
const NETWORK_POLICY_TRANSPORT_TIMEOUT_MARGIN: Duration = Duration::from_secs(5);
|
|
|
|
pub(crate) fn network_policy_decider(
|
|
process_id: ProcessId,
|
|
requests: Arc<RwLock<Option<RpcServerRequestSender>>>,
|
|
controller_timeout: Duration,
|
|
process_shutdown: CancellationToken,
|
|
) -> Arc<dyn NetworkPolicyDecider> {
|
|
let request_timeout =
|
|
controller_timeout.saturating_add(NETWORK_POLICY_TRANSPORT_TIMEOUT_MARGIN);
|
|
Arc::new(move |request: NetworkPolicyRequest| {
|
|
let process_id = process_id.clone();
|
|
let requests = Arc::clone(&requests);
|
|
let process_shutdown = process_shutdown.clone();
|
|
async move {
|
|
let host = request.host.as_str();
|
|
if host.is_empty()
|
|
|| host.len() > MAX_NETWORK_POLICY_HOST_BYTES
|
|
|| host.chars().any(char::is_control)
|
|
|| host.chars().any(char::is_whitespace)
|
|
{
|
|
return NetworkDecision::deny("not_allowed");
|
|
}
|
|
let requests = requests
|
|
.read()
|
|
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
|
.clone();
|
|
let Some(requests) = requests else {
|
|
return NetworkDecision::deny("not_allowed");
|
|
};
|
|
let params = NetworkPolicyRequestParams {
|
|
process_id,
|
|
request: ExecServerNetworkPolicyRequest {
|
|
protocol: match request.protocol {
|
|
NetworkProtocol::Http => ExecServerNetworkProtocol::Http,
|
|
NetworkProtocol::HttpsConnect => ExecServerNetworkProtocol::HttpsConnect,
|
|
NetworkProtocol::Socks5Tcp => ExecServerNetworkProtocol::Socks5Tcp,
|
|
NetworkProtocol::Socks5Udp => ExecServerNetworkProtocol::Socks5Udp,
|
|
},
|
|
host: request.host,
|
|
port: request.port,
|
|
},
|
|
};
|
|
tokio::select! {
|
|
biased;
|
|
_ = process_shutdown.cancelled() => NetworkDecision::deny("not_allowed"),
|
|
response = requests.call_with_timeout::<_, NetworkPolicyRequestResponse>(
|
|
NETWORK_POLICY_REQUEST_METHOD,
|
|
¶ms,
|
|
request_timeout,
|
|
) => response
|
|
.map(|response| match response.decision {
|
|
ExecServerNetworkPolicyDecision::Allow => NetworkDecision::Allow,
|
|
ExecServerNetworkPolicyDecision::Deny { reason }
|
|
| ExecServerNetworkPolicyDecision::Ask { reason }
|
|
if reason.len() > MAX_NETWORK_POLICY_REASON_BYTES
|
|
|| reason.chars().any(char::is_control) =>
|
|
{
|
|
NetworkDecision::deny("not_allowed")
|
|
}
|
|
ExecServerNetworkPolicyDecision::Deny { reason } => {
|
|
NetworkDecision::deny(reason)
|
|
}
|
|
ExecServerNetworkPolicyDecision::Ask { reason } => {
|
|
NetworkDecision::ask(reason)
|
|
}
|
|
})
|
|
.unwrap_or_else(|_| NetworkDecision::deny("not_allowed")),
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "network_policy_decisions_tests.rs"]
|
|
mod tests;
|