我们遇到的情况是网络上有两个物理路由器网关,每个网关都连接到自己的 ISP。由于业务需求,我们无法将两个 WAN 合并到单个路由器上,因此必须存在两个路由器。
我们希望远程用户能够通过 WAN/路由器 VPN 接入,并能够访问内部 Web 服务器。通过下图,我知道远程用户需要设置两个单独的 VPN 应用程序/配置文件/帐户,每个 WAN/路由器一个,这没问题。
下图能满足我的要求吗?下面的基本思路是设置一个 HAProxy,将所有设备的默认网关设置为指向 HAProxy,并让 HAProxy 处理与路由器的连接。不幸的是,我对整个代理机制还不太熟悉,不确定 HAProxy 是否能满足我的要求。
仅供参考,下面显示了我们目前的情况,所有设备的网关都指向路由器网关 1。问题是,当远程用户通过 WAN-XYZ 通过 VPN 进入路由器网关 2 时,他们似乎无法访问内部 Web 服务器。据我了解,这个问题是由于内部 Web 服务器上的默认网关设置为路由器网关 1 造成的。
补充说明:路由器 2 将是 pfSense 路由器。
答案1
我只是用虚拟机主机上的三个虚拟机和两个(独立)桥接器模拟了您的场景/需求,并为其制定/测试了一个解决方案(这就是我在评论中提到的)。
VM 主机充当 Web 服务器,其中两个 VM 充当路由器,其中一个 VM 充当来自“Internet”的 Web 客户端:
VM 主机(Web 服务器)上的配置:
$ ip a show dev bridge1
4: bridge1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 3a:f6:7b:90:aa:bd brd ff:ff:ff:ff:ff:ff
inet 192.168.254.3/24 scope global bridge1
valid_lft forever preferred_lft forever
inet6 fe80::38f6:7bff:fe90:aabd/64 scope link
valid_lft forever preferred_lft forever
$ ip a show dev bridge2
5: bridge2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 1a:6f:58:86:72:55 brd ff:ff:ff:ff:ff:ff
inet6 fe80::186f:58ff:fe86:7255/64 scope link
valid_lft forever preferred_lft forever
(bridge1
用于模拟局域网,bridge2
用于模拟“互联网”,因此后者没有分配IPv4地址。)
$ ip rule
0: from all lookup local
32765: from all fwmark 0xb iif lo lookup 11
32766: from all lookup main
32767: from all lookup default
$ ip r show table main dev bridge1
10.10.10.0/24 via 192.168.254.1
192.168.254.0/24 proto kernel scope link src 192.168.254.3
$ ip r show table 11
10.10.10.0/24 via 192.168.254.2 dev bridge1
(这里192.168.254.1
假定为“主要”默认网关。iif lo
是一种改进,它导致规则仅适用于来自主机本身的流量,换句话说,除非 Web 服务器主机也充当某种路由器,否则它可能是不必要的。)
$ sudo nft list ruleset
table ip mangle {
chain input {
type filter hook input priority mangle; policy accept;
ether saddr 52:54:00:bb:bb:bb ip saddr != 192.168.254.2 ct mark set 0x0000000b
}
chain output {
type route hook output priority mangle; policy accept;
ct mark 0x0000000b meta mark set ct mark
}
}
(显然type
必须route
在链中hook output
才能使其工作。此外,来自路由器的流量与来自“互联网”的流量不同,可以根据其源 IP 地址进行区分,因此ip saddr != 192.168.254.2
需要指定以表明这一事实;实际上,这可能是不必要的改进。)
以下是在 Web 客户端 VM 上完成的tcpdump
两次运行的 VM 主机/Web 服务器上的捕获:curl
$ sudo tcpdump -eni bridge1 tcp port 80
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on bridge1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
16:54:43.602105 52:54:00:aa:aa:aa > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 74: 10.10.10.3.33132 > 192.168.254.3.80: Flags [S], seq 2320058647, win 64240, options [mss 1460,sackOK,TS val 3412464375 ecr 0,nop,wscale 7], length 0
16:54:43.602185 3a:f6:7b:90:aa:bd > 52:54:00:aa:aa:aa, ethertype IPv4 (0x0800), length 74: 192.168.254.3.80 > 10.10.10.3.33132: Flags [S.], seq 3987984937, ack 2320058648, win 65160, options [mss 1460,sackOK,TS val 3768307023 ecr 3412464375,nop,wscale 7], length 0
16:54:43.603460 52:54:00:aa:aa:aa > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 66: 10.10.10.3.33132 > 192.168.254.3.80: Flags [.], ack 1, win 502, options [nop,nop,TS val 3412464377 ecr 3768307023], length 0
16:54:43.604003 52:54:00:aa:aa:aa > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 140: 10.10.10.3.33132 > 192.168.254.3.80: Flags [P.], seq 1:75, ack 1, win 502, options [nop,nop,TS val 3412464377 ecr 3768307023], length 74: HTTP: GET / HTTP/1.1
16:54:43.604054 3a:f6:7b:90:aa:bd > 52:54:00:aa:aa:aa, ethertype IPv4 (0x0800), length 66: 192.168.254.3.80 > 10.10.10.3.33132: Flags [.], ack 75, win 509, options [nop,nop,TS val 3768307025 ecr 3412464377], length 0
16:54:43.604238 3a:f6:7b:90:aa:bd > 52:54:00:aa:aa:aa, ethertype IPv4 (0x0800), length 329: 192.168.254.3.80 > 10.10.10.3.33132: Flags [P.], seq 1:264, ack 75, win 509, options [nop,nop,TS val 3768307025 ecr 3412464377], length 263: HTTP: HTTP/1.1 200 OK
16:54:43.604636 52:54:00:aa:aa:aa > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 66: 10.10.10.3.33132 > 192.168.254.3.80: Flags [.], ack 264, win 501, options [nop,nop,TS val 3412464378 ecr 3768307025], length 0
16:54:43.605112 52:54:00:aa:aa:aa > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 66: 10.10.10.3.33132 > 192.168.254.3.80: Flags [F.], seq 75, ack 264, win 501, options [nop,nop,TS val 3412464379 ecr 3768307025], length 0
16:54:43.605133 3a:f6:7b:90:aa:bd > 52:54:00:aa:aa:aa, ethertype IPv4 (0x0800), length 66: 192.168.254.3.80 > 10.10.10.3.33132: Flags [F.], seq 264, ack 76, win 509, options [nop,nop,TS val 3768307026 ecr 3412464379], length 0
16:54:43.605270 52:54:00:aa:aa:aa > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 66: 10.10.10.3.33132 > 192.168.254.3.80: Flags [.], ack 265, win 501, options [nop,nop,TS val 3412464379 ecr 3768307026], length 0
16:54:47.528893 52:54:00:bb:bb:bb > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 74: 10.10.10.3.49270 > 192.168.254.3.80: Flags [S], seq 1866708541, win 64240, options [mss 1460,sackOK,TS val 1196345946 ecr 0,nop,wscale 7], length 0
16:54:47.528977 3a:f6:7b:90:aa:bd > 52:54:00:bb:bb:bb, ethertype IPv4 (0x0800), length 74: 192.168.254.3.80 > 10.10.10.3.49270: Flags [S.], seq 1756841838, ack 1866708542, win 65160, options [mss 1460,sackOK,TS val 3768310949 ecr 1196345946,nop,wscale 7], length 0
16:54:47.530210 52:54:00:bb:bb:bb > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 66: 10.10.10.3.49270 > 192.168.254.3.80: Flags [.], ack 1, win 502, options [nop,nop,TS val 1196345947 ecr 3768310949], length 0
16:54:47.530535 52:54:00:bb:bb:bb > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 140: 10.10.10.3.49270 > 192.168.254.3.80: Flags [P.], seq 1:75, ack 1, win 502, options [nop,nop,TS val 1196345948 ecr 3768310949], length 74: HTTP: GET / HTTP/1.1
16:54:47.530588 3a:f6:7b:90:aa:bd > 52:54:00:bb:bb:bb, ethertype IPv4 (0x0800), length 66: 192.168.254.3.80 > 10.10.10.3.49270: Flags [.], ack 75, win 509, options [nop,nop,TS val 3768310951 ecr 1196345948], length 0
16:54:47.530744 3a:f6:7b:90:aa:bd > 52:54:00:bb:bb:bb, ethertype IPv4 (0x0800), length 329: 192.168.254.3.80 > 10.10.10.3.49270: Flags [P.], seq 1:264, ack 75, win 509, options [nop,nop,TS val 3768310951 ecr 1196345948], length 263: HTTP: HTTP/1.1 200 OK
16:54:47.531434 52:54:00:bb:bb:bb > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 66: 10.10.10.3.49270 > 192.168.254.3.80: Flags [.], ack 264, win 501, options [nop,nop,TS val 1196345949 ecr 3768310951], length 0
16:54:47.532994 52:54:00:bb:bb:bb > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 66: 10.10.10.3.49270 > 192.168.254.3.80: Flags [F.], seq 75, ack 264, win 501, options [nop,nop,TS val 1196345951 ecr 3768310951], length 0
16:54:47.533092 3a:f6:7b:90:aa:bd > 52:54:00:bb:bb:bb, ethertype IPv4 (0x0800), length 66: 192.168.254.3.80 > 10.10.10.3.49270: Flags [F.], seq 264, ack 76, win 509, options [nop,nop,TS val 3768310954 ecr 1196345951], length 0
16:54:47.533925 52:54:00:bb:bb:bb > 3a:f6:7b:90:aa:bd, ethertype IPv4 (0x0800), length 66: 10.10.10.3.49270 > 192.168.254.3.80: Flags [.], ack 265, win 501, options [nop,nop,TS val 1196345951 ecr 3768310954], length 0
^C
20 packets captured
20 packets received by filter
0 packets dropped by kernel
如你所见,回复的目标 MAC 地址与相应原始流量的源 MAC 地址相匹配,这意味着它们被发送到相应原始流量所来自的路由器,即使 IP 地址相同。(另外,如截图所示,两次运行均成功获取了目标网页。)
nftable 规则集的基本原理
这ct mark
环境在hook input
链中,将导致为同一“连接”的所有流量设置标记。(我没有/不能真正深入研究这一点,但如果你想了解更多信息,请研究“conntrack”。)因此,在链中,hook output
你可以“选择”相应的回复,ct mark
匹配,并对它们执行,这意味着在与 相同的值的回复上meta mark set ct mark
设置(即,顺便说一下, 是任意值)。(您也可以将其设置为不同的值。)meta mark
ct mark
0xb
meta mark
对应于fwmark
IP 规则中的 ,因此,11
将查找一个额外的路由表(在示例中,也是一个任意值),以查找与规则中的meta mark
相等的流量,fwmark
前(由于优先级值较低)main
查找路由表。
由于路由表中11
存在一条 的路由,其下一跳(即)与路由表中的路由10.10.10.0/24
不同,因此选定的回复将被发送到“正确的”路由器。(当存在“覆盖”目标地址的路由时,不会执行进一步的查找。)via
main
虽然10.10.10.0/24
用 代替了default
aka 0.0.0.0/0
,并且“路由器”与 Web 客户端主机一起连接到同一座桥来模拟真实的互联网,但它不应该妨碍演练在真实情况下的工作。