WireGuard:wg-quick 中的两条半路由与 0.0.0.0/0
两个 /1 覆盖的地址与 /0 完全相同,但 wg-quick 对它们的处理方式不同。修改前请检查策略规则、标记和端点路径。
两个 /1 前缀覆盖的 IPv4 地址,与一个 /0 完全相同,而这种等价只在 peer 的允许前缀列表内部成立。一旦安装工具要决定如何路由,这种等价就失效了,因为 Linux 的 wg-quick 把默认前缀当作特殊情况处理,而不是当作算术。这条特殊分支会安装一张专用表、两条策略规则、一个套接字标记以及反向路径设置,而这些是一对半路由永远拿不到的。所以当修改后代理或浏览器停止工作时,前缀恰恰是这次改动中最不值得关注的部分。
前缀列表并不等于整个配置
AllowedIPs 同时承担两项职责,其中只有一项与覆盖范围有关。对 WireGuard 本身而言,这份列表是密码密钥路由:它决定哪些目标会被加密发往某个 peer,以及该 peer 可以使用哪些源地址。对 wg-quick 而言,同一份列表还充当路由规格,它生成的路由取决于列表恰好匹配到哪条分支。因此,两种不同形状的列表覆盖范围相同,并不说明主机状态相同。
默认配置把这个决定从你手里拿走了,因为 Table 默认是自动的,路由落在哪里由工具自行选择。换一个网络管理器、换一个发行版封装,或者自定义 Table 的值,结果又会不同,这也是为什么写给一台主机的建议在下一台上可能显得错误。
默认分支在路由之外还加了什么
一条 /0 记录会让 wg-quick 走默认路由分支:一张单独的路由表接收默认路由,一条策略规则把未标记流量送过去,第二条规则让主表中更具体的路由保持优先,接口本身则在自己的套接字上获得防火墙标记。这个标记就是环路保护。承载隧道流量的加密数据报文带着该标记发出,于是把所有其他流量推向隧道的那条规则不会吞掉隧道自身的传输报文。
而一对 /1 记录走的是普通路由分支。两条路由落入配置的表中,周围那套机制一个都不会出现,不是因为它们不必要,而是因为代码根本没进入那条路径。
两种安排在哪里分道扬镳
分歧首先以递归的形式出现。半路由放在主表里时,peer 自己的传输地址落在隧道前缀之内,于是本该承载隧道的数据报文匹配到一条指向隧道内部的路由。必须有东西把它排除在外:为当前端点加一条主机路由、手工打一个标记,或者一条接口专用的规则。主机路由只在端点地址不变时有效,而端点地址会变:基于 DNS 的端点、漫游客户端和多 peer 配置都会如此。
其次,任何基于标记做推理的东西都会改变行为。在本地重定向连接的透明代理会创建新的套接字,其标记和源地址与原始客户端流不同,而默认分支生成的策略规则正是当初把这些流区分开的一部分。读取代理自身的出站连接,与测试原始目标是否可达是两回事。
修改之前先读规则和标记
在隧道健康时抓取一次当前状态,改动之后再抓一次,然后比较这两份输出,而不是从配置文件推断。要包含策略规则、每一张表、接口标记以及已记录的端点。
ip rule show
ip -4 route show table all | grep -E 'default|wg1'
wg show wg1 fwmark
wg show wg1 endpoints
如果输出的第一行丢失了未标记流量规则,并且专用表是空的,就说明默认分支没有运行,再多的前缀算术也恢复不了它的效果。接口上原本有标记、现在变成零,也是同一个信号,只用一个值就能看出来。
上线之前先演练隧道中断
如果半路由布局是刻意的选择,它就必须明确补齐缺失的部分:能经受地址变化的端点路径、隧道断开时的预期行为,以及 IPv6 的同等处理。把这些写成需求,而不是在故障中才发现。然后演练失败场景,因为一条悄悄回落到物理上行链路的全隧道,比一条彻底停止工作的隧道更糟。
sudo ip -4 route add 198.51.100.20/32 via 192.0.2.1 dev eth0
ip -4 route get 198.51.100.20
sudo wg-quick down wg1
路由查找应当在不经过隧道的情况下从物理接口解析出端点,而关闭接口应当让主机停留在设计所承诺的状态。替换之后代理能用,只能证明生成的主机状态确实重要;它不能证明环路防护和关闭状态下的行为经受住了这次修改。