如何让单个网站绕过 WireGuard 隧道
一条经 LAN 网关的 /32 路由即可让单个站点走普通连接,前提是防火墙放行,且 DNS 返回的地址一致。
把某个主机名排除在外,是隧道做不到的事。不过,一条路由可以让单个地址绕过隧道,经家庭网关出去,对单个站点来说通常够用。难点在于路由下游的一切必须保持一致:防火墙要放行数据包,DNS 要返回你所路由的地址,站点也要继续使用那个地址。三者中任何一个变动,例外都会在不声不响中失效。
碰隧道之前,先读目的地地址
在隧道开启时加载一次站点,然后查看浏览器实际连接的对方地址。在 Linux 上, ss -tunp 会列出每个已打开套接字的远端地址, dig +short 对同一名称显示解析器返回了什么。把两个数字都放在眼前。
两个答案经常不一致,而这正是全部问题所在。大型站点会在源站前面放一层内容分发网络,所以解析器现在返回的地址,未必是浏览器一分钟前用过的那个。要按你在套接字上观察到的地址做路由,而不是老教程里列出的地址。如果答案随地区不断轮换,那么地址例外对这个特定站点就是错误的工具。
换另一个网络再做一次查询,并把 IPv6 答案与 IPv4 答案一起记下。同时承载两族的隧道需要在两族上都做例外,否则浏览器会悄悄退回你从未碰过的 IPv6 路径。把地址和日期记在你会再来看的地方。
在客户端上加一条主机路由
假设接口名为 home-wg,LAN 路由器位于 192.168.1.1。NetworkManager 会接受一条网关为该路由器、前缀长度覆盖全部 32 位的路由,内核会优先选它,因为最长前缀胜出。
nmcli connection modify home-wg +ipv4.routes "198.51.100.20/32 192.168.1.1"
nmcli connection up home-wg
ip route get 198.51.100.20
网关必须在不走隧道的情况下可达;把下一跳指向隧道地址就违背了初衷,因为数据包会重新进入你正想避开的那块接口。连接重新起来之后, ip route get 对这条例外应当打印出 LAN 网关,而同一条命令对公共解析器仍应打印隧道设备。
wg-quick 配置文件没有路由字段,所以同样的例外放在一行 PostUp 里,它把主机路由加到主表,并以 LAN 接口作为设备。改完后要把接口关掉再打开,因为运行中的接口永远不会重读自己的文件。wg-quick 安装的 fwmark 规则只引导它自己封装的数据包,所以普通浏览器流量仍然走主表,并能找到你的 /32。
终止开关是下一个挡住它的东西
只允许隧道接口上通行的防火墙策略,即使路由完全正确,也会把例外丢掉。要去读开关背后的规则,而不是信任开关,因为实现终止开关的规则通常限定在某个接口名上。
你要的是针对物理接口和那一个目的地的一条窄许可,放在丢弃规则之前,同时默认策略保持不动。整体暂停终止开关确实能证明问题,但也会在你忘记重新打开之前一直让机器暴露着。
两个方向要分开测试。对例外地址的请求应当成功,而对无关站点的请求仍应从隧道出去。如果两者都成功,但第二个报告的是家庭地址,那是防火墙规则的顺序错了,而不是路由错了。
浏览器拨哪个地址,由 DNS 决定
当名称在隧道内解析时,运营商解析器可能返回与本地解析器不同的地址。于是你的路由作用在一个浏览器根本不会联系的地址上,页面照旧从隧道加载,仿佛什么都没变。这种故障看起来像例外坏了,尽管路由表是对的。
把这些名称的查询也交给本地解析器,或者干脆跳过名称,直接测试地址。把地址分流、却把 DNS 留在隧道内,是这套配置最常见的半成品,也最难察觉,因为站点仍然能用,只是不是你想要的方式。
在路由器上做同样的例外,一次,覆盖所有设备
隧道跑在路由器上时,主机路由属于路由器的策略路由表,指向 WAN 网关而不是 LAN 网关。它后面的每台设备都会继承这个例外,这对流媒体盒子或无法修改网络设置的工作笔记本很重要。
把隧道留作其他所有客户端的默认出口,然后从客户端机器而不是路由器控制台去验证。在同一浏览器会话中加载例外站点和第二个站点,比较出口地址;如果第二个仍显示隧道,说明策略表正在按你的要求工作。
把地址、日期和原因写进路由器的备注。围绕一个日后会迁移的地址建立的例外,一年后会变成没人解释得清的故障,而修复看起来总像谜团,直到有人找到那条旧备注。