[WSLC] Add cooperative HTTP/HTTPS proxy support - #652
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Adds WSLC “cooperative” HTTP/HTTPS proxy support by translating network.proxy into standard proxy environment variables (with hygiene scrubbing), aligning WSLC behavior with the existing Bubblewrap cooperative proxy model and documenting/testing it end-to-end.
Changes:
- Added shared
wxc_common::proxy_envhelpers for scrubbing/setting proxy env vars and refactored Bubblewrap to use them. - Extended config parsing to allow
network.proxyforwslcand added WSLC-specific validation (url-form only). - Added WSLC proxy functional test config + scripts and documented WSLC proxy behavior/caveats.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/scripts/run_wslc_proxy_test.ps1 | New WSLC proxy functional test runner script |
| tests/scripts/run_wslc_all_tests.ps1 | Adds WSLC proxy config to the all-tests suite |
| tests/configs/wslc_network_proxy.json | New WSLC proxy test config exercising env injection + scrubbing |
| src/core/wxc_common/src/proxy_env.rs | New shared proxy env scrubbing/injection helpers + unit tests |
| src/core/wxc_common/src/lib.rs | Exposes the new proxy_env module |
| src/core/wxc_common/src/config_parser.rs | Allows WSLC proxy + validates WSLC proxy must be { url } form |
| src/backends/wslc/common/src/wsl_container_runner.rs | Injects cooperative proxy env vars into WSLC process settings |
| src/backends/bubblewrap/common/src/bwrap_command.rs | Refactors Bubblewrap proxy env handling to shared helpers |
| sdk/node/src/types.ts | Updates SDK docs/comments to mention WSLC proxy constraints |
| docs/wsl/wsl-container-getting-started.md | Documents WSLC cooperative proxy model and limitations |
| docs/schema.md | Notes WSLC proxy support and url-only constraint in schema docs |
| $WxcExec = $Candidates | Where-Object { Test-Path $_ } | Select-Object -First 1 | ||
| } | ||
| if (-not $WxcExec -or -not (Test-Path $WxcExec)) { | ||
| Write-Host "ERROR: wxc-exec.exe not found. Build with: cargo build --features wslc --release --target $Target" -ForegroundColor Red |
| // MXC-run host-loopback proxy is unreachable. Accept only the | ||
| // caller-supplied `url` form (which carries `original_url`); reject | ||
| // the `localhost` / `builtinTestServer` forms. | ||
| if containment == ContainmentBackend::Wslc && proxy_config.is_enabled() { |
There was a problem hiding this comment.
The WSLc url-form guard only checks original_url.is_some(), so a loopback URL like {"url":"http://127.0.0.1:8080"} or http://[::1]:8080 is accepted here. That is the same host-loopback target the localhost form is rejected for, since a WSLc container runs in its own network namespace and cannot reach the host loopback. As written this contradicts its own rationale: the localhost form is blocked but the identical address slips through via url. Because WSLc requires defaultPolicy=allow, the sandbox then gets connection-refused to a proxy that can never work while traffic is otherwise unrestricted. Please also reject loopback and unspecified hosts (127.0.0.0/8, ::1, 0.0.0.0, ::) for the WSLc url form, and add a test covering http://127.0.0.1:8080.
There was a problem hiding this comment.
A WSLC container runs in its own network namespace (separate VM), so the 127.0.0.1 in wslc_network_proxy.json is the container's own loopback, not the host's- the host loopback isn't reachable from a WSLC container at all. Rejecting it would break that documented, tested pattern
|
On config_parser.rs around line 400 (the shared network.proxy url parsing, not part of this diff): the url form validates host and port but not the scheme, so ftp://proxy:21 or socks5://proxy:1080 are accepted and then injected as HTTP_PROXY/HTTPS_PROXY values. Some HTTP clients silently ignore a non-HTTP proxy scheme, which fails open under the WSLc defaultPolicy=allow requirement. Consider restricting accepted schemes to http and https (or explicitly documenting socks support if intended). Shared across backends, so likely a follow-up rather than blocking this PR. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
docs/wsl/wsl-container-getting-started.md:247
- Node's built-in
http/httpsclients do not honor these variables by default. Current supported releases require explicitNODE_USE_ENV_PROXY=1or--use-env-proxyopt-in, while older supported Node versions need a proxy agent. Listing Node here without that qualification can cause traffic to bypass the proxy unexpectedly.
2. Cooperative tools (curl, wget, Python `requests`, Node `https`, etc.) honor
the env vars and their traffic flows through the proxy.
| /// Only the HTTP/HTTPS keys (upper- and lower-case) are set. `NO_PROXY` is | ||
| /// deliberately omitted (see module docs). | ||
| pub const PROXY_SET_KEYS: &[&str] = &["HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy"]; |
| $pass = $false | ||
| $reason = "runner did not inject/scrub HTTP_PROXY (expected http://127.0.0.1:8888)" | ||
| } | ||
| if ($pass -and ($output -match "attacker\.invalid")) { |
| Any caller-supplied values for these keys — including `NO_PROXY` / | ||
| `no_proxy` — are **stripped** first, so a workload cannot override or | ||
| disable the configured proxy. The runner deliberately does **not** set | ||
| `NO_PROXY`. |
| * WSLC accepts only the `{ url }` form (its containers run in their own network | ||
| * namespace, so the `localhost` / `builtinTestServer` loopback forms are | ||
| * unreachable and rejected); enforcement is cooperative (no in-kernel iptables). */ |
…e-http-proxy Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f3ae1af2-7b79-4340-a5ce-a5402e7ede3d # Conflicts: # sdk/node/src/types.ts # src/backends/bubblewrap/common/src/bwrap_command.rs # src/core/wxc_common/src/config_parser.rs
|
|
||
| Write-Host "`n--- Network Tests ---" -ForegroundColor Cyan | ||
| $null = $results.Add((Run-WslcTest "wslc_network_isolated.json")) | ||
| $null = $results.Add((Run-WslcTest "wslc_network_proxy.json" -OutputContains "WSLC_PROXY_FUNCTIONAL_OK")) |
|
@copilot resolve the merge conflicts in this pull request |
…as/wslc-enable-http-proxy # Conflicts: # src/backends/wslc/common/src/wsl_container_runner.rs
Resolved. The only conflict was in |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
src/core/wxc_common/src/config_parser.rs:427
- This validation changes every backend, not just WSLC. Before this hunk, Bubblewrap and Seatbelt accepted URLs such as
socks5://proxy:1080and passed the preserved URL to clients that support SOCKS proxy schemes; those existing configs will now fail parsing even though the fail-open rationale here is WSLC-specific. Please scope the scheme restriction to the WSLC branch (and make this test usecontainment: "wslc"), or explicitly treat and document this as a breaking cross-backend API change.
// Only http/https are meaningful for the HTTP(S)_PROXY env vars we
// inject. A non-HTTP scheme (ftp://, socks5://, …) is silently ignored
// by many clients, which fails open under WSLc's defaultPolicy=allow.
let scheme = parsed.scheme();
if scheme != "http" && scheme != "https" {
src/core/wxc_common/src/proxy_env.rs:37
FTP_PROXY/ftp_proxyare missing from the managed set. A cooperating client such as curl gives a scheme-specific FTP proxy precedence overALL_PROXY, so caller-supplied values can survive an active proxy policy. This also conflicts with the repository's proxy hygiene requirement indocs/linux-wsl-roadmap-june-2026.md:104,379anddocs/sandbox-policy/v2/networking.md:217, which explicitly lists both variants. Add them to the scrubbed keys (not the set keys).
"NO_PROXY",
"no_proxy",
sdk/node/src/types.ts:205
- The primary
SandboxPolicy.network.proxydocumentation in this same file (types.ts:352-360) still says the proxy is supported only on ProcessContainer, Bubblewrap, and Seatbelt. SinceSandboxPolicyis whatspawnSandboxconsumes, WSLC users will still see contradictory API documentation despite this update to the lower-levelContainerConfigtype. Please update that public policy comment too, including WSLC's URL-only/allowOutboundcombination rules.
/** Proxy configuration (supported on Windows ProcessContainer, Linux Bubblewrap,
* macOS Seatbelt, and WSLC). On Bubblewrap/Seatbelt/WSLC it is a cooperative
* env-var proxy (HTTP_PROXY/HTTPS_PROXY): well-behaved HTTP clients honor it,
📖 Description
WSLc containers run in their own network namespace (a separate WSL system VM) and the WSLc kernel has no in-kernel iptables, so per-host network enforcement is not possible. Mirror the Bubblewrap backend's cooperative model: translate
network.proxyinto HTTP(S)_PROXY env vars that cooperating clients (curl, wget, requests, ...) honor.🔗 References
🔍 Validation
✅ Checklist
Cargo.lock, thedependency-feed-checkcheck passes (see docs/pull-requests.md)📋 Issue Type
GitHub Actions runs the PR validation build automatically. The ADO pipeline
(
MXC-PR-Build) is the Azure version of the PR pipeline, kept in parity with the GitHubActions build; it runs on merge to
main, and Microsoft reviewers with write access can trigger iton a PR with
/azp run. See docs/pull-requests.md.If the
dependency-feed-checkcheck fails on a new dependency, the crate must be added tothe feed before the PR can pass. See docs/pull-requests.md
for the steps.
Microsoft Reviewers: Open in CodeFlow