本文用于归档 Windows 11 + WSL2 networkingMode=mirrored 场景下,
“WSL 内服务正常,但从局域网或其他外部主机访问 Windows 主机 IP 的某个端口失败”时的排障方法,
并解释数据包从外部主机进入 WSL 服务时的实际网络路径。
本文假设:
%UserProfile%\.wslconfig 中启用了 mirrored networking;[wsl2]
networkingMode=mirrored
firewall=true
修改 .wslconfig 后需要让 WSL2 VM 完全重启,例如:
wsl --shutdown
假设目标为:
Windows LAN IP : 192.168.1.50
WSL 服务端口 : TCP 22
外部客户端 : 192.168.1.100
客户端执行:
ssh user@192.168.1.50
如果连接失败,按以下顺序排查。
在 WSL 内:
ss -lntp
对于 SSH,希望看到类似:
LISTEN ... 0.0.0.0:22
或者 IPv6 listener:
LISTEN ... [::]:22
如果只有:
127.0.0.1:22
则服务只接受 WSL 本机 loopback 连接,外部主机本来就无法访问。
同时在 WSL 内进行本机验证:
curl http://127.0.0.1:PORT
或者 SSH:
ssh user@127.0.0.1
在 Windows PowerShell 中:
Get-NetTCPConnection -State Listen |
Where-Object LocalPort -eq 22
也可以使用:
netstat -ano | findstr :22
mirrored networking 与 NAT + portproxy 不同。 外部访问通常使用 Windows 主机地址和相同端口号, 因此 Windows 侧已有服务占用该端口时应首先视为潜在冲突。
例如:
Windows OpenSSH Server :22 WSL sshd :22 这种设计容易产生冲突。 更清晰的设计: Windows OpenSSH Server :22 WSL sshd :2222
外部访问:
ssh windows-user@192.168.1.50 -p 22
ssh linux-user@192.168.1.50 -p 2222
ignoredPorts 不是通用的“Windows 与 WSL 对外共享同一个端口”的方案。
它允许 Linux 在某些 Windows 已使用的端口上绑定,但微软文档明确说明,
这种能力主要用于 Linux 内部流量等特殊场景。
WSL 的 VMCreatorId:
{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}
查看实际生效设置:
$WslId = '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}'
Get-NetFirewallHyperVVMSetting `
-PolicyStore ActiveStore `
-Name $WslId |
Format-List
重点关注:
EnabledDefaultInboundActionDefaultOutboundActionAllowHostPolicyMerge查看针对 WSL 的 Hyper-V Firewall 规则:
Get-NetFirewallHyperVRule -VMCreatorId $WslId
例如明确允许 SSH:
New-NetFirewallHyperVRule `
-Name 'WSL-SSH' `
-DisplayName 'WSL SSH' `
-Direction Inbound `
-VMCreatorId $WslId `
-Protocol TCP `
-LocalPorts 22 `
-Action Allow
如果只希望局域网访问,可进一步限制来源:
New-NetFirewallHyperVRule `
-Name 'WSL-SSH-LAN' `
-DisplayName 'WSL SSH from LAN' `
-Direction Inbound `
-VMCreatorId $WslId `
-Protocol TCP `
-LocalPorts 22 `
-RemoteAddresses '192.168.1.0/24' `
-Action Allow
如果 WSL 的 Hyper-V Firewall 设置中:
AllowHostPolicyMerge = True
则符合条件的 Windows Host Firewall 策略可以参与 WSL 的 Hyper-V effective policy。 这不应理解成“数据包一定先经过一次 Windows Firewall,再经过一次 Hyper-V Firewall”, 而应理解成: Windows Host Firewall 的部分策略可以被合并进 Hyper-V Firewall 的最终判定策略。
因此应检查 Windows Firewall 中是否存在与目标端口、协议、网络 Profile、来源地址相关的显式阻断规则。
可以先查看当前 Windows 网络 Profile:
Get-NetConnectionProfile
特别留意当前网络是 Public、Private 还是 DomainAuthenticated。
某些规则只在指定 Profile 下生效。
查询 Windows 防火墙中与本地 TCP 22 端口相关的规则,可以使用:
Get-NetFirewallRule -Enabled True |
Get-NetFirewallPortFilter |
Where-Object {
$_.Protocol -eq 'TCP' -and
($_.LocalPort -eq '22' -or $_.LocalPort -eq 'Any')
}
Ubuntu 使用 UFW 时:
sudo ufw status verbose
如果需要允许 SSH:
sudo ufw allow 22/tcp
查看 nftables:
sudo nft list ruleset
仍使用 iptables 的系统:
sudo iptables -L -n -v
即使 Hyper-V Firewall 已经允许数据包进入 WSL, Linux 自己的 nftables / UFW / iptables 仍然可以再次丢弃该连接。
| 顺序 | 检查对象 | 典型问题 | 主要命令 |
|---|---|---|---|
| 1 | WSL 服务监听 | 服务未启动;只监听 127.0.0.1;端口错误 | ss -lntp |
| 2 | Windows 端口占用 | Windows 自己的程序已监听同一端口 | Get-NetTCPConnection |
| 3 | Hyper-V Firewall | 默认入站 Block;没有 Allow rule;存在 Block rule | Get-NetFirewallHyperVVMSettingGet-NetFirewallHyperVRule |
| 4 | Host Policy Merge | Windows Firewall 中可合并的规则阻断流量 | AllowHostPolicyMergeGet-NetFirewallRule |
| 5 | Linux Firewall | UFW / nftables / iptables 阻断 | ufw statusnft list ruleset |
| 6 | 外部网络 | 路由、VLAN、AP isolation、上游防火墙等 | Test-NetConnection / nc / ssh -v |
传统 WSL2 NAT 模式可抽象为:
LAN │ ▼ Windows 192.168.1.50 │ │ NAT ▼ WSL 私有虚拟网络 172.x.x.x │ ▼ Linux 服务
WSL 位于 Windows 后方的私有 NAT 网络中。 LAN 主机通常不能直接访问 WSL 私有地址,因此经常需要端口转发。
mirrored networking 则更接近:
Windows 上的 Wi-Fi / Ethernet / VPN / IPv6 网络环境
│
│ mirror / traffic steering
▼
WSL2 networking
│
▼
Linux application
Windows 网络接口环境被镜像到 Linux, 并由 Windows / WSL / Hyper-V 网络基础设施负责将适当的流量引导给 WSL。 它不是传统意义上的桥接虚拟机,也不是简单 NAT。
假设:
Windows IP = 192.168.1.50
WSL sshd = TCP 22
外部客户端发送:
dst = 192.168.1.50:22
因此从外部网络的视角, 访问的始终是Windows 主机对外可达的地址。
但该流量到达 Windows 后,如果属于可以被 mirrored networking 导向 WSL 的流量,则:
外部客户端
192.168.1.100
│
│ TCP 192.168.1.50:22
▼
Windows 物理/无线网卡
│
▼
WSL mirrored traffic steering
│
▼
Hyper-V Firewall effective policy
│
▼
WSL Linux 网络栈
│
▼
Linux firewall
│
▼
sshd :22
因此“外部访问 Windows_IP:22”与“最终由 WSL sshd 接收该连接”并不矛盾。
因为外部客户端只有一个目标:
Windows_IP:Port
如果 Windows 主机自身已经有一个服务占用了相同端口, 则这个端口已经存在明确的 Windows endpoint。 mirrored networking 并不是设计用来让两个互相独立的对外服务无条件共享同一个 IP:Port。
因此最清晰的设计是:
Windows 服务 → 使用自己的端口 WSL 服务 → 使用不同的端口
Hyper-V Firewall 用于过滤 Windows 托管虚拟化环境或容器的网络流量, WSL2 属于它的适用对象。
对于进入 WSL 的连接,可概念化为:
mirrored traffic
│
▼
Hyper-V Firewall
│
├─ Hyper-V-specific rules
├─ DefaultInboundAction
└─ 可合并的 Host Firewall policy
│
├─ Block → DROP
└─ Allow
│
▼
WSL Linux
这就是为什么 WSL 内服务正常、Linux firewall 也允许, 但 Hyper-V Firewall 没放行时,外部仍然连接不上。
不应简单画成:
packet ↓ Windows Firewall ↓ Hyper-V Firewall ↓ WSL
更准确的是:
Hyper-V 专用策略 ──────────┐
│
可适用的 Windows Host FW ──┼──→ Hyper-V effective policy
│
GPO / MDM 等策略 ──────────┘
│
▼
WSL traffic
当 AllowHostPolicyMerge=True 时,
Windows Host Firewall 中符合条件的策略可以被合并进 Hyper-V Firewall 的 effective policy。
因此排障时必须同时考虑两类规则来源。
数据包通过 Windows / Hyper-V 层后,进入的是正常 Linux 网络栈:
Hyper-V Firewall
│
▼
Linux IP/TCP stack
│
▼
nftables / UFW / iptables
│
▼
socket
│
▼
application
因此 Linux 防火墙与 Hyper-V Firewall 是两个真正不同的过滤域。 前者属于 WSL 内 Linux,后者属于 Windows/Hyper-V 虚拟化网络基础设施。
外部主机
│
│ Windows_IP:Port
▼
Windows 网卡
│
├─ Windows 本机 endpoint?
│
└─ mirrored WSL traffic?
│
▼
Hyper-V Firewall
│
├─ Hyper-V rules
├─ Default policy
└─ merged Host Firewall policy
│
▼
WSL Linux 网络栈
│
▼
nftables / UFW
│
▼
Linux application
推荐配置:
# %UserProfile%\.wslconfig
[wsl2]
networkingMode=mirrored
firewall=true
WSL:
sudo systemctl enable ssh
sudo systemctl start ssh
ss -lntp | grep ':22'
Windows 管理员 PowerShell:
$WslId = '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}'
New-NetFirewallHyperVRule `
-Name 'WSL-SSH-LAN' `
-DisplayName 'WSL SSH from LAN' `
-Direction Inbound `
-VMCreatorId $WslId `
-Protocol TCP `
-LocalPorts 22 `
-RemoteAddresses '192.168.1.0/24' `
-Action Allow
外部客户端:
ssh user@192.168.1.50
0.0.0.0 / 合适的 IPv6 地址本文针对 mirrored networking 场景。WSL、Windows Firewall 与 Hyper-V Firewall 的实现和默认行为可能随 Windows / WSL 版本更新, 归档长期使用时应优先以 Microsoft Learn 当前文档为准。