目录
Please enable Javascript to view the contents

WSL网络配置-镜像网络

 ·  ☕ 5 分钟

    在此之前使用网桥的方案进行网络配置,实现局域网同网段访问WSL内服务的访问。

    但最近一年左右,Windows11 出了一个更加优雅的新解决方案:“镜像网络”,利用了 Hyper-V 虚拟交换机的镜像端口技术。
    随着此次更新,WSL2 2.0版本有以下功能更新:

    • 自动回收空闲内存
    • 自动回收虚拟硬盘空间
    • 镜像网络
    • DNS 代理隧道
    • Windows 系统防火墙集成
    • Windows 代理设置集成
      这些功能极大的方便了对子系统服务发现和暴露管理,深度优化了在子系统进行网络开发方向的体验。

    镜像网络和桥接网络,两个方案都具有无需关注端口转发,仅需关注更宏观的路由规则及防火墙设置的优势。

    版本说明

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    > wsl -v
    WSL 版本: 2.1.0.0
    内核版本: 5.15.137.3-1
    WSLg 版本: 1.0.59
    MSRDC 版本: 1.2.4677
    Direct3D 版本: 1.611.1-81528511
    DXCore 版本: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp
    Windows 版本: 10.0.22631.3296
    
    > wsl -l -v
      NAME            STATE           VERSION
    * Ubuntu-22.04    Running         2

    wsl更新

    Windows 11 23H2 ++

    1
    
    wsl --update --pre-release

    配置网卡路由规则、防火墙规则

    1. 允许外部路由设备进站到 WSLg/WSL2 子系统

    Hyper-V默认防火墙规则会阻挡同一局域网的访问请求,参考此链接解决

    管理员权限在PowerShell窗口执行下面命令,修改Hyper-V防火墙规则,允许入站请求:

    1
    
    Set-NetFirewallHyperVVMSetting -NXXame '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' -DefaultInboundAction Allow
    1. 允许 WSLg/WSL2 虚拟交换机进行路由转发,主要是一些极特殊环境会用到

    好像每次重启都会失效(因此重启后需要再次配置),但是我们可以把它编写成“PowerShell 脚本”放到“本地组策略”里,实现开机自动重新配置
    本地组策略 -> 计算机策略 - Windows 设置 -> 脚本(启动/关机)

    1
    2
    3
    
    Get-NetIPInterface |
    Where-Object {$_.InterfaceAlias -match "^vEthernet\s.*(Default Switch|WSL|WSLCore|LAN).*$"} |
    Set-NetIPInterface -Forwarding Enabled -ErrorAction SilentlyContinue

    防火墙放行WSL的出入站规则(没用-未设置)

    1
    
    New-NetFirewallRule -WSL-mirror" -Direction Inbound -InterfaceAlias "vEthernet (Default Switch)" -Action Allow

    配置~\.wslconfig

    此代码块用于创建或打开配置文件 ~.wslconfig

    hostAddressLoopback是实验性功能。
    默认情况下,在wsl2中开启的网络端口,会通过localhost映射到win11上,在win11宿主机通过localhost访问,但是无法通过宿主机的局域网、公网IP访问。
    当这个选项设置为true之后,就会允许wsl与宿主机通过局域网、公网IP(仅支持分配给主机的 IPv4 地址)访问两者之间的服务,且保留了localhost访问的能力。

    1
    2
    3
    
    
    if (-not (Test-Path -Path "~/.wslconfig")) { New-Item -ItemType File ~/.wslconfig }
    pushd ~ ; code .wslconfig
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    # 推荐的一些配置项如下
    [wsl2]                      # 核心配置
    autoProxy=false             # 是否强制WSL使用 Windows 代理设置(按需启用)
    dnsTunneling=true           # WSL DNS 代理隧道,以便由 Windows 代理转发 DNS 请求(按需启用)
    firewall=true               #WSL的 Windows 防火墙集成,以便 Hyper-V 或者 WPF 能过滤子系统流量(按需启用)
    guiApplications=true        # 启用 WSLg GUI 图形化程序支持
    ipv6=true                   # 启用 IPv6 网络支持
    localhostForwarding=true    # 启用 localhost 网络转发支持
    memory=4GB                  # 限制WSL的最大内存占用
    nestedVirtualization=true   # 启用WSL嵌套虚拟化功能支持
    networkingMode=mirrored     # 启用镜像网络特性支持
    #pageReporting=true         # 启用WSL页面文件通报,以便 Windows 回收已分配但未使用的内存
    processors=8                # 设置WSL的逻辑 CPU 核心数为 8(最大肯定没法超过硬件的物理逻辑核心数)
    
    
    [experimental]                  # 实验性功能
    autoMemoryReclaim=gradual       # 启用空闲内存自动缓慢回收
    hostAddressLoopback=true        # 启用WSL和 Windows 宿主之间的本地回环互通支持
    sparseVhd=true                  # 启用WSL虚拟硬盘空间自动回收
    useWindowsDnsCache=false        # 和 dnsTunneling 配合使用,决定是否使用 Windows DNS 缓存池

    配置/etc/wsl.conf

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    # 此配置文件不能通过 cd /etc && ln -Ps /mnt/d/Devs/WSL/wsl.conf wsl.conf 来配置,只能通过拷贝副本
    # https://docs.microsoft.com/en-us/windows/wsl/wsl-config
    
    [automount]
    enabled=true
    mountFsTab=true
    options="metadata,dmask=0022,fmask=0077,umask=0022"
    root=/mnt/
    
    [filesystem]
    umask=0022
    
    [interop]
    enabled=true
    appendWindowsPath=false   # 不添加 Windows 环境变量 Path,防止路径变量污染带来的干扰
    
    # 其它网络配置
    [network]
    generateHosts=true
    generateResolvConf=true
    
    
    # boot command 暂不支持 nohup 后台启动
    # command=nohup service cron start >/dev/null 2>&1 &
    [boot]
    # command=/root/.start.sh
    systemd=true

    重启WSL

    1
    
    wsl --shutdown ; wsl

    测试

    1
    2
    3
    4
    5
    6
    7
    
    # 查询子系统的 IP Address 地址
    # 你将能看到一组和 Windows 宿主一致的双栈 IPv4/IPv6 地址及 MAC 地址
    # Just enjoy it!
    ip address show eth0
    
    # 你可以使用 python 临时启动一个 Web 服务器测试网络连通性
    python3 -m http.server -d . -b 0.0.0.0 54321

    WSL镜像网络,宿主机无法访问Docker容器问题

    镜像网络,win11宿主机无法访问wsl中的容器

    问题分析:
    当启用镜像网络后,本地主机转发的行为与之前的有所不同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    windows  --->  linux(WSL)                                 --->       Docker container
      localhostforwarding (until)
    ok    127.0.0.1   ---> 127.0.0.1(lo)    /   172.x.x.1(br-xxx)  --->  172.x.x.x(eth0)
    ok    127.0.0.1   <--- 127.0.0.1(lo)    /   172.x.x.1(br-xxx)  <---  172.x.x.x(eth0)
    
      netowrkingMode=mirrored
    ok    127.0.0.1   ---> 127.0.0.1(loopback0)/127.0.0.1(br-xxxx) --->  172.x.x.x(eth0)
                                              ~~~~~~~~~
    ng                                                           <- - -  172.x.x.x(eth0)

    本地转发设置(默认):源地址(Win11)是 docker 网络网关(=指向 linux)。

    镜像网络设置:源地址是127.0.0.1,因此,数据包会返回到127.0.0.1(wsl上的localhost),而不会到达 Win11。如果从另一个主机访问,则没有问题,因为源地址就是他的IP地址。

    1. 方案一(简单粗暴)

    禁用 iptables,使用userland-proxy(用户空间代理)

    禁用iptables后的数据流:

    本地转发(默认):

    1
    2
    3
    
    windows           --->     linux(WSL)
    ok    127.0.0.1   --->     127.0.0.1(lo)
    ok    127.0.0.1   <---     127.0.0.1(lo)

    镜像流量

    1
    2
    3
    
    windows           --->     linux(WSL)
    ok    127.0.0.1   --->     127.0.0.1(loopback0)
    ok    127.0.0.1   <---     127.0.0.1(loopback0)

    userland-proxy 是一种在 Docker 容器与外部网络之间进行端口转发的机制,它直接在用户空间进行网络数据包的处理,而不依赖于 iptables。
    缺点:

    1. 性能很差,数据包在内核空间、用户空间之间切换传递。
    2. 安全性差,无法对数据包进行过滤、转发、Nat等操作。
    3. 功能受限,不支持端口映射、网络隔离等功能。
      总之,iptables禁用后,类似于所有容器启动默认设置--network=host,几个容器的简单使用可以,但影响复杂场景的使用(比如端口复用)。

    /etc/docker/daemon.json中增加以下内容

    1
    2
    3
    
    {
        "iptables": false
    }

    并重启docker服务

    1
    
    service docker restart
    1. 方案二
      增加iptables规则,在PREROUTING阶段对来自loopback0接口且目的地址为127.0.0.1的数据包进行DNAT,将目的地址转换为127.0.0.1。添加此链目的是破坏PREROUTING钩子,并禁用PREROUTING链中设置的所有 Docker 规则。

    https://gist.github.com/shigenobuokamoto/b565d468541fc8be7d7d76a0434496a0

    以上两方案都是临时手段,仍等待后续官方解决方案。

    详情请见issue

    参考连接

    https://learn.microsoft.com/zh-cn/windows/wsl/wsl-config#configuration-settings-for-wslconfig

    https://blog.gazer.win/essay/wsl2-mirrored-network.html

    https://github.com/microsoft/WSL/issues/10494

    分享

    Hex
    作者
    Hex
    CloudNative Developer