You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

公网VPS通过WireGuard向内网Docker化后端服务器转发端口并保留源IP的配置故障排查

公网VPS通过WireGuard向内网Docker化后端服务器转发端口并保留源IP的配置故障排查

嘿,我来帮你搞定这个WireGuard+Docker的端口转发难题!你已经把基础的隧道配置弄对了——宿主机上直接跑的服务能正常工作,说明核心的DNAT和策略路由逻辑是没问题的,问题确实出在Docker的网络处理上,咱们一步步拆解解决。

为什么Docker容器会失效?

Docker启动时会自动修改宿主机的iptables规则,并且容器的默认出口流量会走宿主机的默认网关(也就是你家里的LAN路由器),而不是你为WireGuard配置的vpsrt策略路由表。这就导致了一个「路由不对称」的问题:

  • 外部请求通过WireGuard隧道到达后端宿主机,被Docker的DNAT规则转发给容器;
  • 但容器的返回包会走宿主机的默认LAN网关,而不是走WireGuard隧道回前端VPS;
  • 前端VPS收不到返回包,连接自然就中断了。

核心解决方案:让容器返回流量强制走WireGuard隧道

我们需要调整路由规则,确保来自前端VPS的请求对应的容器返回流量,必须走WireGuard隧道回源。这里有两种实用方法:

方法一:标记WireGuard流量,绑定策略路由

这种方法不需要修改Docker网络配置,通过iptables标记流量,让返回包强制走vpsrt表:

  1. 先确认后端宿主机的WireGuard策略路由已经生效:

    ip rule show | grep vpsrt
    ip route show table vpsrt
    

    应该能看到from 10.99.1.1 table vpsrt的规则,以及指向前端VPS的路由。

  2. 修改后端的WireGuard配置,添加流量标记和路由规则:

    [Interface]
    PrivateKey = ####
    Address = 10.99.1.1
    Table = vpsrt
    PreUp = sysctl -w net.ipv4.ip_forward=1
    PreUp = ip rule add from 10.99.1.1 table vpsrt priority 1
    # 新增:标记从WireGuard进来的流量
    PreUp = iptables -t mangle -A PREROUTING -i wg0 -j MARK --set-mark 0x100
    # 新增:让标记过的流量走vpsrt表
    PreUp = ip rule add fwmark 0x100 table vpsrt priority 2
    PostDown = ip rule del from 10.99.1.1 table vpsrt priority 1
    PostDown = iptables -t mangle -D PREROUTING -i wg0 -j MARK --set-mark 0x100
    PostDown = ip rule del fwmark 0x100 table vpsrt priority 2
    
    [Peer]
    PublicKey = ####
    Endpoint = 123.456.789.123:51822
    AllowedIPs = 0.0.0.0/0
    PersistentKeepalive = 25
    

方法二:为容器创建专属网络,绑定路由

如果不想修改全局iptables,可以给需要转发的容器创建一个专属Docker网络,然后强制这个网络的流量走WireGuard路由:

  1. 创建自定义Docker网络:
    docker network create --subnet=172.20.0.0/16 wireguard-services
    
  2. 修改你的docker-compose.yml,让容器使用这个网络(可选指定静态IP,方便后续配置):
    services:
      whoami:
        image: "containous/whoami"
        networks:
          wireguard-services:
            ipv4_address: 172.20.0.10
        ports:
          - 80:80
        restart: always
    
    networks:
      wireguard-services:
        external: true
    
  3. 在后端WireGuard配置的PreUp中添加路由规则,让这个子网的流量走vpsrt表:
    PreUp = ip rule add from 172.20.0.0/16 table vpsrt priority 3
    PostDown = ip rule del from 172.20.0.0/16 table vpsrt priority 3
    

验证配置是否生效

完成配置后,重启WireGuard和Docker容器:

# 后端宿主机操作
wg-quick down wg0 && wg-quick up wg0
docker-compose restart whoami

然后做以下测试:

  • 从公网访问http://123.456.789.123,应该能看到容器返回的信息,且源IP是客户端的公网IP;
  • 从前端VPS访问http://10.99.1.1,源IP应该显示为10.99.1.2;
  • 从后端宿主机访问http://172.20.0.10,能正常访问容器服务。

关于Hairpin NAT的补充

你提到需要后端容器之间互相访问(比如其他容器连接SMTP容器):

  • 如果用了自定义Docker网络,容器之间可以直接用容器名或静态IP访问,不需要走公网;
  • 如果必须通过公网域名访问,在后端宿主机添加Hairpin NAT规则即可:
    iptables -t nat -A OUTPUT -d 123.456.789.123 -p tcp --dport 80 -j DNAT --to-destination 172.20.0.10
    
    这样后端容器访问公网IP时,流量会被转发到本地容器,不用走公网绕路。

备注:内容来源于stack exchange,提问作者Jeff Clement

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 10:08:14