fix(networking): Windows container egress is unenforceable on WinNAT+Hyper-V — evidence + VLAN recommendation - #142
fix(networking): Windows container egress is unenforceable on WinNAT+Hyper-V — evidence + VLAN recommendation#142luthermonson wants to merge 1 commit into
Conversation
…yper-V, point to VLAN Metal verification on Windows Server 2025 (build 26100) proved the RFC1918 block ACLs are stored on the HNS endpoint but not enforced, and that no per-container software egress filter is available on this host class. Replace the false 'refusing to start unfirewalled' assurance with an honest, evidenced WARN and a windowsEgressNotEnforced pointer, so the daemon never implies a containment it does not provide. Real containment must be an upstream VLAN.
|
Closing — the central conclusion here has been disproven and superseded. This PR concluded that no per-container software egress filter enforces on this host class, and recommended an isolated VLAN as the only remedy. That was an honest read of the evidence at the time: WFP, the Hyper-V firewall, netsh, and VFP-on-NAT were each tested on metal and each genuinely failed. What the investigation missed is that the failures were all specific to the NAT stack. Switching the container network type to L2Bridge engages VFP on the container's vSwitch port, and per-endpoint Switch ACLs then enforce for real. That shipped in v0.2.0 (#144) and is validated on hardware: an adversarial probe running inside a real Windows CI job is blocked from Grafana, Incus, both Proxmox hosts, the LAN router, and cloud metadata, while the internet, DNS, and dind keep working. So the accurate scoping of this PR's finding is "the default NAT network cannot be egress-filtered host-side" — which is true, and is now documented as such ( The VLAN recommendation still has value as defense-in-depth — see #153, where an isolated VLAN is the clean mitigation for the LAN-peer/multicast exposure L2Bridge inherently accepts — but it is not the only option, and it is no longer the recommendation. The negative results documented here were genuinely useful in getting to L2Bridge, and they're preserved in |
Summary — honest negative result
I did NOT achieve a green Windows containment run. After genuine on-metal testing of every software mechanism available on the node, the evidence shows there is no per-container software egress filter that enforces on this host class (Windows Server 2025 build 26100, HNS NAT network, Hyper-V-isolated job containers). Real containment must come from outside the box — an upstream/switch VLAN whose router denies RFC1918.
This PR does not claim to fix egress. It replaces the code's false assurance ("refusing to start unfirewalled") with an honest, evidenced WARN + a
windowsEgressNotEnforcedpointer, so the daemon never implies a containment it does not provide, and so the next attempt does not repeat #1–#4.Phase 1 diagnosis — the ACLs are applied but NOT enforced
Caught a live job-container endpoint during a held containment run. The RFC1918 block ACLs
buildEgressBlockPoliciesproduces are present on the live endpoint (10.88.82.163):[{"Action":"Block","Direction":"Out","Priority":100,"Protocols":"6,17","RemoteAddresses":"10.0.0.0/8","RuleType":"Switch","Type":"ACL"}, {"...":"172.16.0.0/12"},{"...":"192.168.0.0/16"},{"...":"169.254.0.0/16"}]…yet the same run still reaches every plane (baseline, deployed v0.1.7):
So the endpoint
RuleType:"Switch"(VFP) ACLs are stored but inert.Root cause — VFP is not enforcing on the WinNAT switch
vfpctrl.exe /list-vmswitch-portenumerates the switch ports (host vNICAEAC667CPortId 1; container synthetic ports), but every port- or NAT-scoped VFP operation fails:Only passive enumeration works. On a WinNAT
natnetwork the VFP switch extension is not enforcing on the ports, soRuleType:"Switch"ACLs (and anyvfpctrl-added rule, and the nativevfpctrl"1:Block RFC1918" NAT flag) have no datapath to bite. Container→LAN egress is instead forwarded + SNATed by the host, a path the host Windows Firewall does not filter (it governs host-terminated traffic, not routing/forwarding).Why every mechanism fails here (the enforcement matrix)
vfpctrl)New-NetFirewallHyperVRule, #140)Get-NetFirewallHyperVVMCreatorandGet-NetFirewallHyperVPortare empty even with a live container → rules never attach.Add-VMNetworkAdapterExtendedAcl)Get-VMSwitch/Get-VMNetworkAdapteralso absent).route add … 127.0.0.1)The route addition failed: The parameter is incorrect.Get-NetFirewallProfile→ Windows System Error 1753 (BFE not running);Start-Service BFE/mpssvc→ "Cannot start service". Base Filtering Engine absent inside the container.Every one of these was run on the live node and observed via the containment suite; the four planes stayed reachable in each case.
The intended allow-list (mirrors
firewall_linux.go) was respected in every test: block only10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16minus the container's own10.88.0.0/16(so gateway 10.88.0.1 / DNS / default route / container-to-container survive), leaving the public internet open. The gap is enforcement, not scope.Recommendation
FWPM_LAYER_IPFORWARD_V4(the Windows analogue of the Linux iptables FORWARD chain), added via the WFP API. It is unverified (no CLI to test it; WinNAT's WFP-callout ordering is undocumented) and risky on the production node — a mis-scoped forward filter can sever the host's own management LAN, recoverable only through the fragile guest-agent. It should be prototyped in a lab, not shipped blind, rather than add a 4th untested attempt.What this PR changes
network_windows.go: keep the fail-closed ACL apply, drop the false "refusing to start unfirewalled" claim, emit a per-container WARN with the metal-verified analysis.firewall_windows.go: startup WARN that Windows egress filtering is best-effort and NOT enforced on this host class.windowsEgressNotEnforced: single-line log pointer to the analysis.test/containment-suite, separate branch) added a Windows "internet must still be reachable" probe so a future working fix is checked for over-blocking too.The ACLs are left in place — harmless, fail-closed, and correct on any future host where VFP or the Hyper-V firewall does enforce.