mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
## Summary This PR installs a first wave of WFP (Windows Filtering Platform) filters that reduce the surface area of network egress vulnerabilities for the Windows Sandbox. - Add persistent Windows Filtering Platform provider, sublayer, and filters for the Windows sandbox offline account. - Install WFP filters during elevated full setup, log failures non-fatally, and emit setup metrics when analytics are enabled. - Bump the Windows sandbox setup version so existing users rerun full setup and receive the new filters. ## What WFP is Windows Filtering Platform (WFP) is the low-level Windows networking policy engine underneath things like Windows Firewall. It lets privileged code install persistent filtering rules at specific network stack layers, with conditions like "only traffic from this Windows account" or "only this remote port," and an action like block. In this change, we create a Codex-owned persistent WFP provider and sublayer, then install block filters scoped to the Windows sandbox's offline user account via `ALE_USER_ID`. That means the filters are targeted at sandboxed processes running as that account, rather than globally affecting the host. ## Initial filter set We are starting with 12 concrete WFP filters across a few high-value bypass surfaces. The table below describes the filter families rather than one filter per row: | Area | Concrete filters | Purpose | | --- | --- | --- | | ICMP | 4 filters: ICMP v4/v6 on `ALE_AUTH_CONNECT` and `ALE_RESOURCE_ASSIGNMENT` | Block direct ping-style network reachability checks from the offline account. | | DNS | 2 filters: remote port `53` on `ALE_AUTH_CONNECT_V4/V6` | Block direct DNS queries that bypass our intended proxy/offline path. | | DNS-over-TLS | 2 filters: remote port `853` on `ALE_AUTH_CONNECT_V4/V6` | Block encrypted DNS attempts that could bypass ordinary DNS interception. | | SMB / NetBIOS | 4 filters: remote ports `445` and `139` on `ALE_AUTH_CONNECT_V4/V6` | Block Windows file-sharing/network share traffic from sandboxed processes. | For IPv4/IPv6 coverage, the port-based filters are installed on both `ALE_AUTH_CONNECT_V4` and `ALE_AUTH_CONNECT_V6`. ICMP also gets both connect-layer and resource-assignment-layer coverage because ICMP traffic is shaped differently from ordinary TCP/UDP port traffic. ## Validation - `cargo fmt -p codex-windows-sandbox` (completed with existing stable-rustfmt warnings about `imports_granularity = Item`) - `cargo test -p codex-windows-sandbox wfp::tests` - `cargo test -p codex-windows-sandbox` (fails in existing legacy PowerShell sandbox tests because `Microsoft.PowerShell.Utility` could not be loaded; WFP tests passed before that failure)
179 lines
6.0 KiB
Rust
179 lines
6.0 KiB
Rust
use crate::install_wfp_filters_for_account;
|
|
use crate::setup_error::sanitize_setup_metric_tag_value;
|
|
use anyhow::Result;
|
|
use codex_otel::OtelExporter;
|
|
use codex_otel::OtelProvider;
|
|
use codex_otel::OtelSettings;
|
|
use codex_otel::StatsigMetricsSettings;
|
|
use std::path::Path;
|
|
|
|
const WFP_SETUP_SERVICE_NAME: &str = "codex-windows-sandbox-setup";
|
|
const WFP_SETUP_SUCCESS_METRIC: &str = "codex.windows_sandbox.wfp_setup_success";
|
|
const WFP_SETUP_FAILURE_METRIC: &str = "codex.windows_sandbox.wfp_setup_failure";
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
enum WfpSetupMetricOutcome {
|
|
Success,
|
|
Failure,
|
|
}
|
|
|
|
struct WfpSetupMetric {
|
|
outcome: WfpSetupMetricOutcome,
|
|
target_account: String,
|
|
installed_filter_count: usize,
|
|
error: Option<String>,
|
|
}
|
|
|
|
fn panic_payload_to_string(panic_payload: Box<dyn std::any::Any + Send>) -> String {
|
|
match panic_payload.downcast::<String>() {
|
|
Ok(message) => *message,
|
|
Err(panic_payload) => match panic_payload.downcast::<&'static str>() {
|
|
Ok(message) => (*message).to_string(),
|
|
Err(_) => "unknown panic payload".to_string(),
|
|
},
|
|
}
|
|
}
|
|
|
|
fn build_wfp_metrics_provider(
|
|
codex_home: &Path,
|
|
otel: Option<&StatsigMetricsSettings>,
|
|
) -> Result<Option<OtelProvider>> {
|
|
let Some(otel) = otel else {
|
|
return Ok(None);
|
|
};
|
|
// The setup helper cannot call codex-core's OTEL builder because core
|
|
// depends on this crate, so the parent process passes only the resolved
|
|
// Statsig environment in the elevation payload. Other exporters are
|
|
// intentionally omitted from this helper path.
|
|
OtelProvider::from(&OtelSettings {
|
|
environment: otel.environment.clone(),
|
|
service_name: WFP_SETUP_SERVICE_NAME.to_string(),
|
|
service_version: env!("CARGO_PKG_VERSION").to_string(),
|
|
codex_home: codex_home.to_path_buf(),
|
|
exporter: OtelExporter::None,
|
|
trace_exporter: OtelExporter::None,
|
|
metrics_exporter: OtelExporter::Statsig,
|
|
runtime_metrics: false,
|
|
})
|
|
.map_err(|err| anyhow::anyhow!("failed to initialize WFP setup metrics provider: {err}"))
|
|
}
|
|
|
|
fn emit_wfp_setup_metric(
|
|
codex_home: &Path,
|
|
otel: Option<&StatsigMetricsSettings>,
|
|
metric: &WfpSetupMetric,
|
|
) -> Result<()> {
|
|
let Some(provider) = build_wfp_metrics_provider(codex_home, otel)? else {
|
|
return Ok(());
|
|
};
|
|
if let Some(metrics) = provider.metrics() {
|
|
let target_account = sanitize_setup_metric_tag_value(&metric.target_account);
|
|
match metric.outcome {
|
|
WfpSetupMetricOutcome::Success => {
|
|
let installed_filter_count = metric.installed_filter_count.to_string();
|
|
metrics.counter(
|
|
WFP_SETUP_SUCCESS_METRIC,
|
|
/*inc*/ 1,
|
|
&[
|
|
("target_account", target_account.as_str()),
|
|
("installed_filter_count", installed_filter_count.as_str()),
|
|
],
|
|
)?;
|
|
}
|
|
WfpSetupMetricOutcome::Failure => {
|
|
let mut tags = vec![("target_account", target_account.as_str())];
|
|
let error_tag = metric.error.as_deref().map(sanitize_setup_metric_tag_value);
|
|
if let Some(error) = error_tag.as_deref() {
|
|
tags.push(("message", error));
|
|
}
|
|
metrics.counter(WFP_SETUP_FAILURE_METRIC, /*inc*/ 1, &tags)?;
|
|
}
|
|
}
|
|
}
|
|
provider.shutdown();
|
|
Ok(())
|
|
}
|
|
|
|
fn emit_wfp_setup_metric_safely<F>(
|
|
codex_home: &Path,
|
|
otel: Option<&StatsigMetricsSettings>,
|
|
offline_username: &str,
|
|
metric: &WfpSetupMetric,
|
|
log: &mut F,
|
|
) where
|
|
F: FnMut(&str),
|
|
{
|
|
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
emit_wfp_setup_metric(codex_home, otel, metric)
|
|
}));
|
|
match result {
|
|
Ok(Ok(())) => {}
|
|
Ok(Err(err)) => log(&format!(
|
|
"failed to emit WFP setup metric for {offline_username}: {err}"
|
|
)),
|
|
Err(panic_payload) => {
|
|
let error = panic_payload_to_string(panic_payload);
|
|
log(&format!(
|
|
"WFP setup metric emission panicked for {offline_username}: {error}"
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn install_wfp_filters<F>(
|
|
codex_home: &Path,
|
|
offline_username: &str,
|
|
otel: Option<&StatsigMetricsSettings>,
|
|
mut log: F,
|
|
) where
|
|
F: FnMut(&str),
|
|
{
|
|
let metric = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
install_wfp_filters_for_account(offline_username)
|
|
})) {
|
|
Ok(Ok(installed_filter_count)) => {
|
|
log(&format!(
|
|
"WFP setup succeeded for {offline_username} with {installed_filter_count} installed filters"
|
|
));
|
|
WfpSetupMetric {
|
|
outcome: WfpSetupMetricOutcome::Success,
|
|
target_account: offline_username.to_string(),
|
|
installed_filter_count,
|
|
error: None,
|
|
}
|
|
}
|
|
Ok(Err(err)) => {
|
|
let error = err.to_string();
|
|
log(&format!(
|
|
"WFP setup failed for {offline_username}: {error}; continuing elevated setup"
|
|
));
|
|
WfpSetupMetric {
|
|
outcome: WfpSetupMetricOutcome::Failure,
|
|
target_account: offline_username.to_string(),
|
|
installed_filter_count: 0,
|
|
error: Some(error),
|
|
}
|
|
}
|
|
Err(panic_payload) => {
|
|
let error = panic_payload_to_string(panic_payload);
|
|
log(&format!(
|
|
"WFP setup panicked for {offline_username}: {error}; continuing elevated setup"
|
|
));
|
|
WfpSetupMetric {
|
|
outcome: WfpSetupMetricOutcome::Failure,
|
|
target_account: offline_username.to_string(),
|
|
installed_filter_count: 0,
|
|
error: Some(format!("panic: {error}")),
|
|
}
|
|
}
|
|
};
|
|
|
|
emit_wfp_setup_metric_safely(
|
|
codex_home,
|
|
otel,
|
|
offline_username,
|
|
&metric,
|
|
&mut log,
|
|
);
|
|
}
|