公网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表:
先确认后端宿主机的WireGuard策略路由已经生效:
ip rule show | grep vpsrt ip route show table vpsrt应该能看到
from 10.99.1.1 table vpsrt的规则,以及指向前端VPS的路由。修改后端的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路由:
- 创建自定义Docker网络:
docker network create --subnet=172.20.0.0/16 wireguard-services - 修改你的
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 - 在后端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规则即可:
这样后端容器访问公网IP时,流量会被转发到本地容器,不用走公网绕路。iptables -t nat -A OUTPUT -d 123.456.789.123 -p tcp --dport 80 -j DNAT --to-destination 172.20.0.10
备注:内容来源于stack exchange,提问作者Jeff Clement
相关产品推荐
相关产品推荐

