WireGuard 在移动数据下能用,连同一个 Wi-Fi 却不行
从 LAN 内部发往路由器公网地址的请求,与从移动数据进来的请求走的是不同路径。先在外部测试,再选择本地路径。
隧道在移动数据下能建起来,回到家却停在连接中,而中间是同一个配置文件、同一个对端密钥、没有任何配置改动。差别在路径上:从 LAN 内部发往路由器公网地址的请求,与从互联网进来的请求并不是同一趟旅程。多数家用网关要么没有实现回程路径,要么只对某些协议实现。于是症状看起来像密钥坏了,可密钥本身什么都没变。在动密钥、端口转发或客户端配置之前,先确定是哪条路径失败了。
同一个地址,两条不同的路径
从外部,数据包到达路由器的公网地址,端口转发规则把它交给内网主机上的 WireGuard 监听进程。从内部,客户端拨的仍是同一个公网地址,于是数据包离开客户端、到达路由器,必须被折回 LAN。两条路径正是在这个折返点分岔:路由器必须为一条既从同一接口出去、又回到同一接口的流做转换。许多实现干脆把这条流丢掉而不做转换,客户端便一直在等一个从未发出的握手。
这种行为通常叫 NAT 环回或发夹转换,它属于路由器而不属于 WireGuard;RFC 4787 列出了网关可能支持、也可能不支持的转换情形。WireGuard 本身对路径没有意见,因为它只看得见到达自己接口的数据包。路由器把流丢掉时,守护进程什么也收不到,客户端报握手超时,服务器日志则一直是空的。所以空日志是数据包停在哪里的线索,而不是服务器宕机的证据。这也正是重装毫无变化的原因。
反过来同样重要:服务器日志里出现来自 LAN 地址的握手尝试,说明数据包到了、路径是通的,调查就该转向密钥和允许地址。读日志要看它证明了什么,而不是看你希望看到什么。
先复现,再改动
用同一台设备、同一个配置文件,先在已获授权的外部网络测试,然后在 LAN 内部原样再测一次。外面握手成功、里面客户端的数据包根本出不了设备,这就复现了问题,而不是在猜。两次测试必须用同一个配置文件,否则比较毫无意义。
接着看这个设备把端点域名解析成什么地址。下面三条命令也会显示客户端实际拨的是哪个地址:
wg show wg0 endpoints
dig +short vpn.example.net A
resolvectl query vpn.example.net第一条命令报告接口当前使用的端点,后两条显示这个名字在这台机器上解析成什么。客户端与服务器处在同一网络却去拨公网地址,就是在走发夹路径。客户端拨 LAN 地址则不是,那种失败属于另一类调查。这个区分决定了下一节是否适用于你。
到达近处服务器的受支持做法
最干净的做法是分离视图应答:公网名字对互联网解析成公网地址,对内网客户端解析成服务器的 LAN 地址。配置文件只保留一个端点名字,由解析器选路径。这需要掌握内网 DNS 应答,家用路由器通常提供这项能力,组织也可以刻意安排。没有这份控制权,剩下的选择就是一个路由器特性,或者第二份配置文件。
路由器那个特性是反射:网关把从内到外的流做转换再送回内网。能用的时候不需要改客户端,不能用的时候任何客户端设置都救不回来。测试方法是内部尝试时盯着服务器日志,而不是去读路由器的功能列表。
把配置文件改成私网地址是诊断手段,不是设计方案。它能证明服务器在 LAN 上应答,但笔记本一离开家,同一份配置就断了。
| Approach | What it depends on | Where it fails |
|---|---|---|
| Split-horizon name | Control of the internal DNS answer | Clients that resolve through a server outside the zone |
| Router reflection | Gateway support for hairpin translation | Routers that drop the flow silently |
| Private address in the profile | Nothing beyond the LAN | Every network except the one at home |
让配置在家以外也能用
无论选哪条路径,都不要动身份校验。端点地址不是对端身份,地址变了也从不构成接受一把恰好会应答的无关公钥的理由。WireGuard 靠密钥认证,所以主机错了只会握手失败,不会放你进什么意外的地方。
公网地址会变的话,让它保持有用的是动态 DNS,而客户端的刷新行为决定了变更多快生效。地址变更后要到外部去测,LAN 环境验证不了这一点。要当心把旧应答缓存到记录生存期之后的解析器。还指向旧地址的名字,看起来和一台宕掉的服务器一模一样。
写下配置文件用的是哪个名字,以及这个名字在边界两侧各自解析成什么。一份依赖没人记录过的路由器特性的配置,就是一张等着下次固件升级来兑现的工单。这条记录也能告诉下一个人:内部失败是已知的发夹情形,还是新问题。