Skip to content

ProcessContainer: let an identity-less proxy run under PSEC 1.0 - #1373

Closed
Carlos Alexandro Becker (caarlos0) wants to merge 4 commits into
mainfrom
caarlos0/identity-less-proxy-psec-1.0
Closed

Carlos Alexandro Becker (caarlos0) wants to merge 4 commits into
mainfrom
caarlos0/identity-less-proxy-psec-1.0

Conversation

@caarlos0

@caarlos0 Carlos Alexandro Becker (caarlos0) commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

📖 Description

An unpackaged, non-AppContainer proxy previously required
network.ingress.hostLoopback: "allow". That setting requires PSEC 1.1 with
ingress support, so an identity-less proxy could not run on a PSEC 1.0-only host.

This change accepts hostLoopback: "deny" for an identity-less proxy and grants
networkLoopback and the reserved MXC-Loopback peer based on proxy identity,
not the host-loopback setting. The configured proxy endpoint remains unchanged.

hostLoopback Requirement
"allow" Requests bidirectional access and retains the PSEC 1.1 ingress prerequisite
"deny" Removes that prerequisite; PSEC 1.0 can be used unless another requested feature requires a newer contract

Both settings serialize the same proxy capability and peer and omit native
ingress. "deny" does not remove a separately encoded bidirectional grant.
Other features, such as enumeration-only filesystem grants, can still require
PSEC 1.1.

Unchanged: identity-scoped proxies require a non-blank allowedProxyPeer
and hostLoopback: "deny". Every ProcessContainer proxy requires
ingress.default: "allow". Identity-less proxies must omit allowedProxyPeer.
WFP endpoint scoping is unchanged; no AppContainer fallback is added.

Limitations: the backend guide records that proxy-mode host-to-container
connections time out on Windows build 26691.1002. Requesting "allow" does not
make that direction work. The denied-loopback path still needs native
confinement evidence from an identified PSEC 1.0-only host.

Merged main through 298bb39 in 573e0f7 without discarding upstream's
blank-peer rejection. Follow-up commits a893cc8 and 8701818 add denied-loopback
IPv4/IPv6 confinement coverage and correct the remaining setup guidance.

🔗 References

  • Reported downstream: Copilot CLI cannot use an identity-less Windows sandbox
    proxy on a PSEC 1.0-only host.
  • Expose ProcessContainer probe capabilities #1151 added BackendCapability::IngressHostLoopbackAllow, which distinguishes
    hosts that support the ingress prerequisite.
  • Updated docs/process-container/networking.md,
    docs/process-container/os-version-support.md, docs/schema.md, and
    docs/sandbox-policy/0.8.0/networking/networking.md.

🔍 Validation

Original validation reported before the merge, on a Windows host with PSEC 1.1
ingress support:

Command Reported result
cargo test -p wxc_common --lib 959 passed, 0 failed
cargo test -p process_container_common --lib 316 passed, 1 failed
cargo fmt --all --check Passed

The original report identifies
base_container_helpers::tests::runtime_proxy_preserves_allowed_host_to_container_loopback
as a pre-existing failure (os error 10060), reproduced on the parent commit.
That native comparison has not been repeated from the current macOS environment.

Follow-up validation on macOS:

Command Result
cd src && cargo fmt --all -- --check Passed after the merge and review fixes
git diff --check Passed for the review fixes
git diff --cached origin/main --check Passed before the merge commit
cargo test -p process_container_common --lib runtime_proxy_with_denied_host_loopback_confines_ipv4_and_ipv6 -- --nocapture (from src/) Not run: requires a Windows PSEC host

Local builds, Rust tests, and Clippy were deferred because system load exceeded
the local validation limit. The PowerShell backend harness was not run on macOS.

New coverage:

  • Parser acceptance of both identity-less host-loopback settings.
  • Rejection of identity-scoped host-loopback allow and denied ingress defaults.
  • PSEC 1.0 serialization of the identity-less proxy capability and reserved peer.
  • No loopback capability grant without a proxy.
  • runtime_proxy_with_denied_host_loopback_confines_ipv4_and_ipv6: reuses the
    existing live-listener helper with hostLoopback: "deny" and PSEC 1.0 gating.
    It requires a successful proxy connection and WSAEACCES (10013) from another
    live IPv4/IPv6 listener. Launch failure, timeout, refusal, and unexpected
    success do not count as policy denial. Existing PSEC 1.1 cases remain intact.

The P8e harness case checks that the denied-loopback request reaches the proxy.
The new Rust native test adds the missing endpoint-rejection coverage.
Pending: run that test on an identified PSEC 1.0-only Windows host and record
its build, PSEC support, and result. A skipped test is not confinement evidence;
the corresponding review thread remains unresolved.

✅ Checklist

📋 Issue Type

  • Bug fix
  • Feature
  • Task

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 GitHub
Actions build; it runs on merge to main, and Microsoft reviewers with write access can trigger it
on a PR with /azp run. See docs/pull-requests.md.

If the dependency-feed-check check fails on a new dependency, the crate must be added to
the feed before the PR can pass. See docs/pull-requests.md
for the steps.

Microsoft Reviewers: Open in CodeFlow

An unpackaged, non-AppContainer proxy had to send
`network.ingress.hostLoopback: "allow"`. That value raises the request to PSEC
contract 1.1 with ingress support, so a host whose BaseContainer reports only
PSEC 1.0 could not use `runtimeConfig.networkProxy` at all. The parser refused
the `"deny"` spelling, and the capability that authorizes the loopback
connection was granted only beside `"allow"`, so no caller could reach its
proxy on such a host.

The grant belongs to the proxy identity, not to the host-loopback setting.
`networkLoopback` and the reserved `MXC-Loopback` peer now follow
`identity_less_proxy`: a proxy with no `allowedProxyPeer`. `hostLoopback` keeps
its own meaning -- `"allow"` additionally requests the bidirectional contract
and its PSEC 1.1 host requirement, while `"deny"` keeps the configured endpoint
alone under the PSEC 1.0 capability mapping. WFP endpoint scoping is unchanged
in both, so the client still reaches only the proxy.

The posture an identity-scoped proxy must keep is untouched: `hostLoopback:
"allow"` beside `allowedProxyPeer` is still refused, and every proxy still
requires `ingress.default: "allow"`.

The bidirectional half does not work today in any case: on Windows build
26691.1002, host-to-container listener connections time out in proxy mode, as
docs/process-container/networking.md records.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
@caarlos0
Carlos Alexandro Becker (caarlos0) requested review from a team and a balanced review from Copilot October 2, 2026 19:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

Review effort: Lite
Findings: 1 Medium severity

Open (1)
What changed in this PR

Enables an identity-less ProcessContainer proxy to run on hosts limited to PSEC 1.0 by decoupling loopback authorization from network.ingress.hostLoopback, while preserving the stricter identity-scoped behavior and updating docs/tests accordingly.

Changes:

  • Allow identity-less proxies (runtimeConfig.networkProxy without allowedProxyPeer) to use either hostLoopback: "allow" or "deny"; keep identity-scoped proxy requiring hostLoopback: "deny".
  • Grant networkLoopback capability + reserved MXC-Loopback peer based on “identity-less proxy” detection (proxy enabled + no peer), not on hostLoopback.
  • Update documentation and the PowerShell harness assertion to validate that the PSEC 1.0-shaped config actually reaches the proxy endpoint.
File Description
tests/​scripts/​run_processcontainer_network_proxy_test.ps1 Updates the P8e harness to assert reachability for identity-less proxy with hostLoopback=deny.
src/​core/​wxc_common/​src/​network_parser.rs Loosens proxy policy validation for identity-less proxies; adds targeted unit tests.
src/​core/​wxc_common/​src/​config_parser.rs Updates schema parsing tests to accept identity-less proxy with denied host loopback.
src/​backends/​process_container/​common/​src/​base_container_helpers/​tests.rs Adds tests asserting PSEC 1.0 mapping behavior for identity-less proxies and no-loopback leakage without proxy.
src/​backends/​process_container/​common/​src/​base_container_helpers.rs Introduces identity_less_proxy() and uses it to add loopback capability/peer for identity-less proxies.
docs/​schema.md Updates schema prose to reflect hostLoopback meaning for identity-less proxies.
docs/​sandbox-policy/​0.8.0/​networking/​networking.md Updates sandbox policy docs for PSEC 1.0 vs 1.1 behavior with identity-less proxies.
docs/​process-container/​os-version-support.md Documents PSEC 1.0 host behavior for identity-less proxy posture.
docs/​process-container/​networking.md Updates ProcessContainer networking docs to reflect the new semantics and PSEC version constraints.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Preserve main's non-blank allowedProxyPeer requirement and the PR's identity-less PSEC 1.0 proxy support.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d20acabb-2367-4261-b346-2d7ed8995896
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 2, 2026 19:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

Review effort: Lite
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by Copilot of 573e0f7. No confirmed production-code defect found; two follow-ups concern coverage and conflicting documentation. The independent premise check confirms a real parser/version-selection dead end for identity-less proxies on PSEC 1.0 hosts.

No local builds or tests were run, as requested; build status is assumed green, not independently verified. Actual PSEC 1.0 endpoint confinement remains unverified. The capability-order complaint has no current nondeterministic mechanism and does not need a change.

This is a comment-only review because the authenticated reviewer is also the PR author.

Comment on lines 215 to +219
$noLoopback = Invoke-NetRun -Name 'net-proxy-identityless-no-loopback' -ConfigPath $cfgNoLoopback
Record-Result -Phase 'P8e' -Name 'identity-less proxy without hostLoopback=allow is rejected' `
-Pass (Test-WasRejected $noLoopback) `
-Detail "exit=$($noLoopback.Result.ExitCode); timedOut=$($noLoopback.Result.TimedOut); doc requires hostLoopback=allow when allowedProxyPeer is omitted"
Record-Result -Phase 'P8e' -Name 'identity-less proxy with hostLoopback=deny reaches the proxy endpoint' `
-Pass ($noLoopback.Verdict -eq 'REACHED') `
-Detail ("verdict=$($noLoopback.Verdict); endpoint=127.0.0.1:$port; " +
'this shape carries the proxy on a host without PSEC 1.1 ingress support')

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Check endpoint rejection with the new denied-loopback configuration. On a PSEC 1.0-only host, the earlier hostLoopback=allow workload cannot start, so lines 142-154 skip the direct-egress assertion. This new case then checks only REACHED. The Rust IPv4/IPv6 rejection tests also require PSEC 1.1 and use allow. Thus the new configuration has no test that would detect access to another endpoint, even if this assertion passes.

Reuse assert_proxy_endpoint_only with hostLoopback=deny and PSEC 1.0-compatible gating. Require success at the configured proxy and access denial at another live IPv4/IPv6 listener; keep launch failure distinct from denial. Run that case on an identified PSEC 1.0 host. This is a coverage gap, not a confirmed bypass.

Comment thread docs/sandbox-policy/0.8.0/networking/networking.md Outdated
Address endpoint-rejection feedback by reusing the live IPv4/IPv6 listener checks with hostLoopback=deny and PSEC 1.0 gating. Preserve explicit access-denied checks and launch failure diagnostics.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d20acabb-2367-4261-b346-2d7ed8995896
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
Address setup-guidance feedback across the shared policy, schema, backend and OS-version guides. Distinguish the PSEC 1.1 ingress prerequisite from the identical proxy capability/peer mapping, and retain the documented inbound limitation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d20acabb-2367-4261-b346-2d7ed8995896
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 2, 2026 20:43

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated incremental review by Copilot of 573e0f7..8701818: no new findings.

The documentation finding is addressed and its thread is resolved. The P2 code-coverage gap is also addressed: the new test uses the real backend with hostLoopback=deny, gates on PSEC 1.0 rather than 1.1 ingress, and requires proxy success plus WSAEACCES at other live IPv4 and IPv6 endpoints. Launch failure cannot count as denial.

I am leaving P2 open for the remaining target-host evidence, consistent with its latest reply: a non-skipped run on an identified PSEC 1.0-only Windows host has not been supplied. No local builds or tests were run, as requested. This is a comment-only review because the authenticated reviewer is also the PR author.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

Review effort: Lite
Findings: 2 Medium severity · 1 Low severity

Open (3)
Resolved since last review (1)

Comment thread src/core/wxc_common/src/network_parser.rs
Comment thread tests/scripts/run_processcontainer_network_proxy_test.ps1

@bbonaby Branden Bonaby (bbonaby) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is what we want. Machines that have PSEC 1.0 will be updated with 1.1 this month and will get the changes regardless. Making them "deny" is actually still not accurate because they can talk to anyone on the localhost at that port. So, saying it's denied isn't really accurate.

In the allowedProxyPeer case when they give us an appcontainer profile name or package family name the OS will actually do a check that the thing the container is attempting to communicate with on the loopback is the actual allowedProxyPeer they gave us. We can't do that in the uncontained proxy case, so that's why they need to opt into this "no security guarantee other than only loopback network traffic allowed at this port".

This new case that you're adding, gives consumers a false sense of security that deny in this case means anything. It would be better to update how allow works in this case to account for machines that may be on the older PSEC 1.0. I would prefer to keep deny to mean the consumer has a stronger security guarantee, and allow host loopback to mean they've opted into this less security networking mode.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the Needs-Author-Feedback Waiting for additional information or action from the issue or pull-request author. label Oct 2, 2026
@microsoft-github-policy-service microsoft-github-policy-service Bot added Needs-Attention Requires attention or a decision from the MXC maintainers. and removed Needs-Author-Feedback Waiting for additional information or action from the issue or pull-request author. labels Oct 2, 2026
@caarlos0

Copy link
Copy Markdown
Collaborator Author

superseded by #1406

@caarlos0
Carlos Alexandro Becker (caarlos0) deleted the caarlos0/identity-less-proxy-psec-1.0 branch October 7, 2026 14:27
@microsoft-github-policy-service microsoft-github-policy-service Bot removed the Needs-Attention Requires attention or a decision from the MXC maintainers. label Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants