Repository navigation
[WSLC] Proxy Pt. 2: Enable session host loopback - #1714
Elliot (theelliotm) wants to merge 3 commits into
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
7cbd0d1 to
30303eb
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The tests can miss shared-session relay exposure, incorrectly fail supported older WSL runtimes, and lack passing regression coverage for the non-loopback retry.
3 open issues
What changed in this PR
This PR adds Windows host-loopback access to WSLC workload sessions, advancing the built-in proxy stack. Bridged containers can reach Windows loopback services through host.wslc.internal on WSL 3.0.5+.
Changes:
- Shares session-creation logic across create-and-run execution and the lifecycle daemon, with a fallback for older runtimes.
- Adds token-based loopback reachability and isolation tests.
- Documents runtime requirements and the expanded access granted to existing all-
allowconfigurations.
| File | Description |
|---|---|
tests/scripts/wslc_loopback_token_server.ps1 |
Provides a Windows loopback listener with a per-run token. |
tests/scripts/run_wslc_state_aware_tests.ps1 |
Adds lifecycle loopback reachability and isolation checks. |
tests/scripts/run_wslc_host_loopback_test.ps1 |
Drives create-and-run loopback tests. |
tests/scripts/run_wslc_all_tests.ps1 |
Includes loopback tests in the full WSLC suite. |
tests/configs/wslc_network_host_loopback.json |
Defines the bridged loopback probe. |
tests/configs/wslc_network_host_loopback_isolated.json |
Defines isolated probes by hostname and relay address. |
src/mxc-sdk/src/backends/wslc/common/wslc_bindings.rs |
Loads the loopback-setting API and defines the runtime-update error. |
src/mxc-sdk/src/backends/wslc/common/wsl_container_runner.rs |
Uses shared session creation for create-and-run execution. |
src/mxc-sdk/src/backends/wslc/common/container_steps.rs |
Enables session loopback and retries without it on older runtimes. |
docs/backends/wslc/wslc-state-aware.md |
Documents loopback access through daemon sessions. |
docs/backends/wslc/wsl-container-getting-started.md |
Explains prerequisites, usage, and networking behavior changes. |
Review effort: Balanced
Drop the pre-3.0.5 fallback; an older runtime now fails as WSLC runtime unavailable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c3f64c2c-9005-43eb-a803-a91fb7265c5b
30303eb to
727d749
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
The isolation test can pass without checking containment alongside a live bridged peer in the same session because it tears down the bridged sandbox before probing its saved relay address.
2 resolved since last review
Review effort: Balanced
Run the host loopback checks in one shared session under one listener, so the negative probe cannot pass against a reaped session or a dead listener. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c3f64c2c-9005-43eb-a803-a91fb7265c5b
There was a problem hiding this comment.
🔵 Needs a closer look
Maintainer confirmation is still needed for expanded Windows loopback access, with the design PR unmerged and x64 and older-runtime error-path validation outstanding.
No open issues 🎉
1 resolved since last review
Review effort: Balanced
| let msg = err_msg.to_string_lossy(); | ||
| return Err(sdk_error("WslcCreateSession failed", hr, &msg)); | ||
| } | ||
| let session = container_steps::create_session(sdk, &mut settings)?; |
There was a problem hiding this comment.
On x64/WSL 3.0.5 a bridged create-and-run sandbox fetched another sandbox's port mapping via host.wslc.internal:<windowsPort>: MXC passes a null windowsAddress, so WSL publishes every mapping on Windows 127.0.0.1, and this relay opens all of them to every bridged sandbox.
Don't enable session host loopback until per-container/per-port enforcement exists, and add a two-sandbox negative test to run_wslc_host_loopback_test.ps1.
There was a problem hiding this comment.
Wow. Investigating.
| let mut err_msg = CoTaskMemPWSTR::null(); | ||
| let started = Instant::now(); | ||
| let hr = sdk.WslcCreateSession(&mut settings, &mut session, err_msg.as_mut_ptr()); | ||
| let session = create_session(sdk, &mut settings)?; |
There was a problem hiding this comment.
The daemon now enables host loopback, but PROTOCOL_VERSION stays 9 and ready_daemon() reuses any ready v9 daemon, so after an upgrade a still-running pre-PR daemon (alive while it holds sandboxes) keeps creating sessions without host.wslc.internal, and a new daemon gives old clients the new access.
Shall we bump the protocol version?
There was a problem hiding this comment.
Will bump.
| /// Create a session with host loopback enabled. A WSL runtime older than 3.0.5 | ||
| /// rejects the setting. |
There was a problem hiding this comment.
Isolated-only sessions (every deny-all create-and-run, where network_is_isolated(request) is already known) gain nothing from host loopback, yet setting it forces WSL's new session path, so they now fail below WSL 3.0.5 while available_backends() still reports WSLC available; #1625 README.md:80-81 and 407-410 say MXC sets it only on 3.0.5+ and NONE/BRIDGED keep working on older runtimes.
Set the hostname only for sessions that can hold a bridged container (pass !network_is_isolated(request) from the runner), or update #1625 and make discovery report the 3.0.5 requirement.
There was a problem hiding this comment.
3.0.5 is now the minimum version. I will see if I can enforce that.
| let mut err_msg = CoTaskMemPWSTR::null(); | ||
| let hr = sdk.WslcCreateSession(settings, &mut session, err_msg.as_mut_ptr()); | ||
| if hr == WSLC_E_RUNTIME_UPDATE_NEEDED { | ||
| return Err(WslcError::Unavailable(sdk_init::prerequisite_error( |
There was a problem hiding this comment.
In the daemon this Unavailable surfaces on the boot thread, and finish_session rewraps it as WorkerError::Backend for every provision parked on the implicit session, so a lifecycle provision on WSL < 3.0.5 gets backend_error instead of the backend_unavailable documented at wslc-state-aware.md:701.
Forward Unavailable/Rejected unchanged to parked replies and add a session_manager test for a boot that fails with Unavailable.
There was a problem hiding this comment.
3.0.5 is now the minimum version. I will see if I can enforce that.
| # HTTP listener bound only to Windows 127.0.0.1 that answers every request with | ||
| # a per-run token. Dot-source this file, then pair Start- with Stop-. | ||
|
|
||
| function Start-LoopbackTokenServer { |
There was a problem hiding this comment.
This duplicates tests/scripts/lib/LoopbackAnchor.ps1, whose Start-LoopbackAnchor -Body already binds an ephemeral 127.0.0.1 port and serves a fixed body from a background runspace.
Use Start-LoopbackAnchor -Body ("HOST_LOOPBACK_" + [guid]::NewGuid().ToString("N")) / Stop-LoopbackAnchor and drop this file?

📖 Description
Second PR in the WSLC built-in proxy stack (#1625). Turns on WSL host loopback, so
hostLoopback: allowreaches Windows127.0.0.1. Before this, it was accepted but had no effect.host.wslc.internal, which WSL's CLI also uses.WSLC_E_RUNTIME_UPDATE_NEEDED; MXC fails with the "WSLC runtime unavailable" error telling the user to install WSL 3.0.5 or newer.NONE) containers in a session with host loopback still can't reach Windows loopback, by name or by the relay address.Behavior change: existing all-
allowconfigs gain outbound access to every Windows loopback TCP port. There's no per-port filter; that comes with the controller later in the stack.🔗 References
Refs #1462
Spec: #1625
Stacked on #1679
🔍 Validation
On ARM64 with WSL 3.0.5.0 and
WSLC_SDK_PATHset to the local 3.0.5 SDK build:cargo build -p wxc --features wslc --releaseandcargo build -p mxc-sdk --bin wxc-wslc-daemon --features wslc --release: passedcargo fmt --all -- --check: passedcargo clippy -p mxc-sdk --features wslc --all-targets -- -D warnings: passedcargo test -p mxc-sdk --features wslc --lib wslc: passed (434 passed, 7 ignored)tests/scripts/run_wslc_host_loopback_test.ps1(new): passed. A bridged container gets a per-run token from a listener bound to Windows127.0.0.1; an isolated container doesn't get it throughhost.wslc.internalor169.254.73.254.tests/scripts/run_wslc_state_aware_tests.ps1: passed (142/142), including new checks that, with both running in one verified session, a bridged sandbox reaches the listener and an isolated peer doesn't.Not run: x64, the full
run_wslc_all_tests.ps1after the final test changes, and WSL older than 3.0.5.✅ Checklist
feature-acceptedCargo.lock, thedependency-feed-checkcheck passes (see pull request builds)📋 Issue Type