问题与目标

问题与目标

假设我们有两个接口的机器:ens160wg0

假设我们有如下路由: 172.29.248.128/25 dev ens160 proto kernel scope link src 172.29.248.134 192.168.2.0/24 dev wg0 proto kernel scope link src 192.168.2.2 我们的第一台机器有 IP:172.29.248.134、192.168.2.2

还有第二台机器: endpoint: 172.29.244.20:51820 allowed ips: 192.168.2.1/32

第一台机器可以通过两个 IP 172.29.244.20 和 192.168.2.1 连接到第二台机器。

现在,我想要实现的是,当第一台机器尝试访问 172.29.244.20 时,它应该通过wg0接口。类似于针对此特定 IP 的目标 IP 重写,从 172.29.244.20 到 192.168.2.1

有可能吗?如果可以,我们是否也可以在第二台机器上重写它,因此如果它wg0从 192.168.2.2 接口接收到任何内容,则源 IP 应改回 172.29.248.134 并传递给ens160

仅用 可以完成吗ip route?还是我们需要iptables

@dirkt 谢谢,这通常会起作用,我也试过了,但情况似乎有点复杂。实际上整个情况都更复杂,因为我正在尝试为 Kubernetes 集群设置机器之间的加密,但由于网络插件(Calico)的问题,需要以比直接使用 192.168.2.0/24 更透明的方式。基本上,通过第 3 层 VPN 隧道进行 IP-in-IP 封装和大量 iptables 规则。wg0 是 Wireguard(VPN)接口,它有 192.168.2.0/24 子网。我使用 192.168.2.1/32 为 VPN 指定单个 IP,因为我想创建网状网络。我无法为允许的 IP 指定 192.168.2.0/24,因为 wireguard 不允许多个对等方使用同一个子网。

现在我发现通过这个 IP 转发所有流量wg0可能是个愚蠢的想法,因为我可以看到tcpdump在机器上发送 ping 工作正常(ICMP 请求可见),但在接收机器上没有任何显示wg0

我在 wireguard IRC 上询问了这个问题,问题大概是这样的: The problem is that outgoing traffic to the other wireguard endpoint is no longer routed over ens160. But looped into the wireguard interface.

好的,但这里可能有一个解决方案,来自 wireguard文档 FwMark — a 32-bit fwmark for outgoing packets. If set to 0 or "off", this option is disabled. May be specified in hexadecimal by prepending "0x". Optional.

我发现文章关于这个“递归 VPN 问题”,但我的网络技能很差。我应该怎么做才能将流量路由到172.29.244.20/32 via 192.168.2.2 dev wg0除标有特定 fwmark 的数据包之外?

答案1

问题与目标

拥有数据包的一部分WireGuard尽管隧道内和隧道外使用相同的 IP,但信封(传输加密有效负载的 UDP 数据包)本身不受隧道封装。

方法

因为路由堆栈(ip route、ip rule……)在第 3 层(IP)工作,而不是在第 4 层(UDP,由信封使用),所以不能仅通过路由堆栈来解决这个问题,而需要额外的帮助来区分信封数据包和“数据”数据包。

幸运的是,可以用标记来标记数据包(纯内部标记,在数据包的 skbuff 上,当然不是在线路上)。然后可以将标记重新用作路由堆栈的 fwmark。能够标记数据包的工具(我知道的)有 iptables、nftables……以及 WireGuard 本身。

由于 Wireguard 仍然可以在封装后标记数据包,因此无需使用 iptables/nftables 作为路由的额外帮助。

目前,Wireguard 中存在一个令人讨厌的限制(该限制肯定存在,原因可能与路由处理有关):我找不到设置两个具有相同允许 IPS 值的对等点的方法。因此,无法简单地设置:

AllowedIps = 172.29.244.0/25,172.29.248.128/25

在每个端点上。尝试此操作将删除具有相同值的先前端点上的允许 IP。设置任何其他重叠范围(例如更改掩码)都将被接受,但会阻止某些重叠对等体工作。当然,只允许单个远程端点的 IP 是可行的。这是整个网格正常工作的唯一方法。

解决方案

使用标记区分信封数据包和隧道数据包。甚至不需要说明它必须是哪个 UDP 端口,WireGuard 会处理这部分。使用此标记对路由选择进行“例外处理”。

做出的选择

根据 OP 的评论,不需要数据包转发,因此没有测试转发。

这里做出的选择是借助 WireGuard 来标记不会进入隧道的数据包(另一个可行的解决方案是使用 iptables 而不是 WireGuard 来标记数据包,可以做相反的事情并标记进入隧道的数据包)。

指示

配置示例:

# ip -4 -br addr show dev ens160
ens160@if7       UP             172.29.248.132/25 
# ip route
default via 10.0.3.1 dev eth0 
10.0.3.0/24 dev eth0 proto kernel scope link src 10.0.3.147 
172.29.244.0/25 via 172.29.248.129 dev ens160 
172.29.248.128/25 dev ens160 proto kernel scope link src 172.29.248.132 

# wg show wg0
interface: wg0
  public key: 7FsVTBuIDNQsWj9flHWwMt+kCaE8urEE9bCwcvz6CBM=
  private key: (hidden)
  listening port: 51820

peer: +NJdQr+piwid1iuC58rbnXFgGo/vZ9d8Gs3xvl3/TxI=
  endpoint: 172.29.248.134:51820
  allowed ips: (none)
  transfer: 14.74 KiB received, 34.35 KiB sent

peer: KrZWEupjHGoHTywwWhg4XXQSyFl/disav7/pyhOK/1Q=
  endpoint: 172.29.244.20:51820
  allowed ips: (none)
  transfer: 11.06 KiB received, 15.65 KiB sent

由于最终一切都可以通过隧道重复使用相同的 IP,因此网络 192.168.2.0/24 不再使用。由于没有路由规则会引用这些 IP,因此甚至可以删除它们:wg0 上将没有 IP(如果愿意,仍然可以保留它们):

# ip addr flush dev wg0

配置 wg0 以使用某个任意选择的值(此处为 9)来标记信封数据包:

# wg set wg0 fwmark 9

配置每个 WireGuard 主机以接受隧道中的端点 IP,这就是目标:

# wg show wg0|awk 'BEGIN { RS="\n\n" } /^peer/ { printf "wg set wg0 peer %s allowed-ips %s\n",$2,$4 }'|cut -d: -f1 | sh

现在给出:

# wg show wg0
interface: wg0
  public key: 7FsVTBuIDNQsWj9flHWwMt+kCaE8urEE9bCwcvz6CBM=
  private key: (hidden)
  listening port: 51820
  fwmark: 0x9

peer: +NJdQr+piwid1iuC58rbnXFgGo/vZ9d8Gs3xvl3/TxI=
  endpoint: 172.29.248.134:51820
  allowed ips: 172.29.248.134/32
  transfer: 14.74 KiB received, 34.35 KiB sent

peer: KrZWEupjHGoHTywwWhg4XXQSyFl/disav7/pyhOK/1Q=
  endpoint: 172.29.244.20:51820
  allowed ips: 172.29.244.20/32
  transfer: 11.06 KiB received, 15.65 KiB sent

在(任意选择的)路由表 200 上添加路由,指定主机的 IP 作为源(以避免不幸选择了不相关接口的 IP)...

...只需一次,即可为两个 LAN 路由,但这需要对 LAN 中任何对等点的任何 IP 流量使用 WireGuard:

# localip=$(ip -4 -br addr show dev ens160|awk '{ print $3 }'|cut -d/ -f1)
# ip route add 172.29.244.0/25   dev wg0 table 200 src $localip
# ip route add 172.29.248.128/25 dev wg0 table 200 src $localip

这使:

# ip route show table 200
172.29.244.0/25 dev wg0 scope link src 172.29.248.132 
172.29.248.128/25 dev wg0 scope link src 172.29.248.132 

...添加每个对等路由:

# localip=$(ip -4 -br addr show dev ens160|awk '{ print $3 }'|cut -d/ -f1)
# wg show wg0|awk 'BEGIN { RS="\n\n" } /^peer/ { printf "ip route add %s dev wg0 table 200 src %s\n",gensub(":.*$","",1,$4),localip }' localip=$localip | sh

结果是:

# ip route show table 200
172.29.244.20 dev wg0 scope link src 172.29.248.132 
172.29.248.134 dev wg0 scope link src 172.29.248.132 

通过直接使用 WireGuard 标记的数据包(那些 51820/UDP 数据包)的主路由表,提前纠正此规则之后的规则可能引入的“先有鸡还是先有蛋”的问题:

# ip rule add priority 32000 fwmark 9 lookup main

查找表 200 后“插入”。实际上,只有上面为 WireGuard 设置了路由的数据包才会匹配并使用通过 wg0 的隧道,其他数据包将继续进入(通常的)主表:

# ip rule add priority 32100 lookup 200

这些规则 32100 和表 200 取代了在 wg0 上设置 IP 时在 wg0 内部完成的通常路由。

现在给出:

# ip rule
0:  from all lookup local 
32000:  from all fwmark 0x9 lookup main 
32100:  from all lookup 200 
32766:  from all lookup main 
32767:  from all lookup default 

唉,现在需要 wg0 时,来自正常接口(此处为 ens160)的非 WireGuard 传入流量会触发反向路径过滤器。这包括查找同一 LAN 中的对等点以及另一个 LAN 的网关所需的 ARP 回复数据包(对此我没有很好的解释)。所以rp_过滤器必须将正常接口(此处为 ens160)设置为松散模式:

echo 2 > /proc/sys/net/ipv4/conf/ens160/rp_filter

在其他对等点上重复此设置后,所有对等点现在都可以使用自己的 IP 相互通信,就像没有隧道一样(在网格中可能使用最多 n*(n-1)/2 个隧道)。

示例:主机收到单个 ping 并回复:

# tcpdump -n -s0 -i wg0 -p
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wg0, link-type RAW (Raw IP), capture size 262144 bytes
05:22:51.553463 IP 172.29.244.20 > 172.29.248.132: ICMP echo request, id 2083, seq 1, length 64
05:22:51.553525 IP 172.29.248.132 > 172.29.244.20: ICMP echo reply, id 2083, seq 1, length 64

在路由器上看到的加密流量是(可能因为最近没有活动而产生一些开销):

# tcpdump -n -s0 -i br248 -p
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br248, link-type EN10MB (Ethernet), capture size 262144 bytes
05:22:51.553317 ARP, Request who-has 172.29.248.132 tell 172.29.248.129, length 28
05:22:51.553358 ARP, Reply 172.29.248.132 is-at 2e:02:6e:cf:6d:4f, length 28
05:22:51.553383 IP 172.29.244.20.51820 > 172.29.248.132.51820: UDP, length 128
05:22:51.553583 IP 172.29.248.132.51820 > 172.29.244.20.51820: UDP, length 128
05:22:51.554112 IP 172.29.248.132.51820 > 172.29.244.20.51820: UDP, length 148
05:22:51.555198 IP 172.29.244.20.51820 > 172.29.248.132.51820: UDP, length 92
05:22:51.555748 IP 172.29.248.132.51820 > 172.29.244.20.51820: UDP, length 32

答案2

您没有提供完整的网络描述,因此我假设主机 A(第一台)和主机 B(第二台)直接连接到 上的同一网段,wg0主机 A 上的 是192.168.2.2/24,而主机 B 上的 是92.168.2.1/??/32您说的 可能是一个错误 - 网段上的每台主机都应该有相同的网络掩码,因此您的意思是/24)。如果不正确,请使用网络描述、网络掩码和端点 IP 编辑您的问题。

接下来,如果你想要路由,使用路由功能内核。我不知道为什么这么多人认为这iptables是用于路由的。它不是,无论你在网上读到什么。如果普通路由不够,请使用策略路由。如果策略路由不够,那么你可以考虑用 做一些有趣的事情iptables

内核的路由功能经过了优化,可以快速做出路由决策。但对于 来说并非如此iptables

(抱歉,我发牢骚了,但这是我最讨厌的事情)。

192.168.2.2因此,就您而言,您所要做的就是在 A 上添加一条路由,告诉 A在尝试联系时应该通过以下方式路由72.29.244.20

ip route add 72.29.244.20/32 via 192.168.2.2 dev wg0

然后带有目标地址的数据包72.29.244.20将被发送到192.168.2.2,即主机B。当主机B收到这些数据包时,它会发现自己已经有一个具有该地址的接口,因此接受这些数据包。

您甚至可以考虑扩大72.29.244.20/3272.29.244.0/24其他什么,如果该范围内还有其他主机可以更方便地通过这条路线到达。

相关内容