Skip to content

processcontainer: egress deny + hostLoopback deny hangs asyncio servers — intra-container loopback (socketpair) is blocked #1261

Description

Relevant area(s)

Linux, Windows

Brief description of your issue

With network.egress.default: "deny" and ingress.hostLoopback: "deny", any Python asyncio program hangs forever at startup, with no error and no output.

On Windows, asyncio creates its internal self-pipe with socket.socketpair(), which CPython implements as a TCP connection to 127.0.0.1 within the same process. Under MXC that connection never completes, so accept() blocks forever. This silently breaks every asyncio-based server under the most restrictive network policy, including MCP SDK / FastMCP servers, which never answer their first request.

A plain AppContainer with no network capabilities, created without MXC, allows this same-process loopback while still blocking the host and the internet. So the block appears to come from MXC's WFP filters, not from AppContainer itself.

Related: #1260 (found in the same investigation).

Steps to reproduce

Environment: MXC v0.8.0 release binaries (x64), schema 0.8.0-alpha, Windows 11 Pro 10.0.26200, unelevated user, wxc-exec --probe → base-container, Python 3.12.

  1. Save as cfg.json:
{
  "version": "0.8.0-alpha",
  "containment": "processcontainer",
  "process": {
    "env": ["SystemRoot=C:\\Windows", "LOCALAPPDATA=C:\\Users\\<user>\\AppData\\Local"]
  },
  "filesystem": { "readonlyPaths": ["C:\\Program Files\\Python312"] },
  "network": {
    "egress": { "default": "deny" },
    "ingress": { "default": "deny", "hostLoopback": "deny" }
  },
  "ui": { "disable": true }
}

(LOCALAPPDATA is included to avoid error 203, see #1130.)

  1. Run:
wxc-exec.exe cfg.json -- "C:\Program Files\Python312\python.exe" -c "import socket; a,b=socket.socketpair(); print('ok')"
  1. Observe: ok is never printed and the process never exits. A faulthandler dump shows it blocked in socket._fallback_socketpair → accept.

  2. Change only the network block and rerun. Results:

network config socketpair host 127.0.0.1 listener internet
egress deny, hostLoopback deny (step 1) hangs — —
egress deny, hostLoopback allow (± ingress allow) hangs — —
egress allow ok blocked open
egress deny + "allow": [{"to": [{"cidr": "127.0.0.1/32"}]}], hostLoopback deny ok blocked blocked
(no MXC) raw AppContainer via CreateProcessW + SECURITY_CAPABILITIES, no network capabilities ok blocked blocked

Expected behavior

socket.socketpair() completes (intra-container loopback, same AppContainer SID to itself), while the host 127.0.0.1 listener and the internet stay blocked. A raw AppContainer with no network capabilities, created without MXC, behaves this way (last row of the table).

Actual behavior

socket.socketpair() hangs indefinitely in accept() with no error and no output, so any asyncio program never starts. This includes MCP SDK / FastMCP servers, which never answer initialize.

Workaround: an explicit egress allow rule for {"cidr":"127.0.0.1/32"}. It also adds internetClient to the token, so internet blocking then relies entirely on WFP.

Request

Allow intra-container loopback (same AppContainer SID to itself) by default under deny policies, or document the rule above as the supported recipe.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Backend-ProcessContainerWindows ProcessContainer behavior, including BaseContainer and AppContainer isolation tiers.Issue-BugSomething is not working as intended.OS-WindowsApplies specifically to Windows hosts or Windows behavior.Priority2High-impact issue affecting key functionality; prioritize for the next appropriate release.

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions