目前,我已设置了 1 个物理网卡,该网卡具有公共 IP 地址、标准 docker 环境以及 GRE 隧道和网桥。如果我在端口(例如 80)上运行容器,我将能够通过公共 IP 地址直接连接到容器。
但是,当尝试通过 GRE 隧道连接时,流量会被路由回默认路由。
通过隧道尝试连接:
00:52:43.899400 tun0 In IP {client_ip}.57122 > {tunnel_ip}.80: Flags [S], seq 1853400133, win 64240, options [mss 1400,nop,wscale 8,nop,nop,sackOK], length 0
00:52:43.899501 enp3s0f0 Out IP {tunnel_ip}.80 > {client_ip}.57122: Flags [S.], seq 3563157661, ack 1853400134, win 64240, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
通过公网IP连接成功:
00:52:48.822480 enp3s0f0 In IP {client_ip}.57112 > {public_ip}.80: Flags [P.], seq 3944436694:3944437201, ack 1907727361, win 1029, length 507
00:52:48.823843 enp3s0f0 Out IP {public_ip}.80 > {client_ip}.57112: Flags [P.], seq 1:109, ack 507, win 501, length 108
所以我的问题是流量被路由出tunnel -> docker container -> public interface
隧道而不是返回隧道。我读过多篇有关 docker 网络主题的文章,但找不到有关此类问题的具体信息。任何帮助都非常感谢。
答案1
您需要标记传入的数据包(进入虚拟网卡),然后根据标记设置路由规则,最终设置辅助路由表。有时,基于源的路由也可以工作。
看这里: https://unix.stackexchange.com/questions/4420/reply-on-same-interface-as-incoming
答案2
尽管使用的是 WireGuard 而不是 GRE 隧道,但仍在解决同样的问题。主机提供本地服务(SSH、Nginx 反向代理)和 docker 容器服务(IMAP、SMTP 等)。
从主机到互联网的其他通信仍然使用公共接口上的默认路由,忽略隧道。
这些(PostUp
指令)应该提示它是如何工作的:
- 任何新联系从隧道接口
wg0
已标记编号为 2。 - 任何返回数据包从主机上的应用程序网络级别(OUTPUT 链)已标记与连接标记(2)相同的标记。
- 任何返回数据包来自 DNATed 连接(docker 容器)已标记与连接标记(2)相同的标记。
包裹必须标明前做出路由或重新路由决定 - 参见 netfilter 图https://upload.wikimedia.org/wikipedia/commons/thumb/3/37/Netfilter-packet-flow.svg/2560px-Netfilter-packet-flow.svg.png
# cat /etc/wireguard/wg0.conf
[Interface]
Address = 10.0.37.2/24
PrivateKey = redacted
# If `AllowedIPs = 0.0.0.0/0`, then wg-quick forces a default route to _every_ packet to go via
# WG tunnel. If we want to keep default route untouched, set:
Table = off
# And create routing manually only for WG related communication.
PostUp = iptables -t mangle -I PREROUTING 1 -i wg0 -m conntrack --ctstate NEW -j CONNMARK --set-mark 2
PostUp = iptables -t mangle -I OUTPUT 1 -m connmark --mark 2 -j CONNMARK --restore-mark
PostUp = iptables -t mangle -I PREROUTING 2 -m connmark --mark 2 -j CONNMARK --restore-mark
PostUp = ip rule add fwmark 2 lookup 2
PostUp = ip rule add table main suppress_prefixlength 0
PostUp = ip route add default dev wg0 table 2
PostUp = sysctl -q net.ipv4.conf.all.src_valid_mark=1
PostUp = sysctl -w net.ipv4.conf.wg0.rp_filter=2
# Removal when `wg0` goes down
PostDown = iptables -t mangle -D PREROUTING -i wg0 -m conntrack --ctstate NEW -j CONNMARK --save-mark
PostDown = iptables -t mangle -D OUTPUT -m connmark --mark 2 -j CONNMARK --restore-mark
PostDown = iptables -t mangle -D PREROUTING 2 -m connmark --mark 2 -j CONNMARK --restore-mark
PostDown = ip rule del fwmark 2 table 2
PostDown = ip rule del table main suppress_prefixlength 0
PostDown = ip route del default dev wg0 lookup 2
[Peer]
Endpoint = my.vps.server:51800
PublicKey = redacted
PresharedKey = redacted
AllowedIPs = 10.0.37.1/32,0.0.0.0/0
PersistentKeepalive = 25
与此答案提出的基于源的路由相比,上述解决方案更为费力:https://unix.stackexchange.com/a/23345
但是,基于源的路由对于可以通过两个接口(本地接口和隧道)从同一个公共 IP 地址访问的主机来说似乎不够。