隧道在移动数据下能建起来,回到家却停在连接中,而中间是同一个配置文件、同一个对端密钥、没有任何配置改动。差别在路径上:从 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 上应答,但笔记本一离开家,同一份配置就断了。

ApproachWhat it depends onWhere it fails
Split-horizon nameControl of the internal DNS answerClients that resolve through a server outside the zone
Router reflectionGateway support for hairpin translationRouters that drop the flow silently
Private address in the profileNothing beyond the LANEvery network except the one at home

让配置在家以外也能用

无论选哪条路径,都不要动身份校验。端点地址不是对端身份,地址变了也从不构成接受一把恰好会应答的无关公钥的理由。WireGuard 靠密钥认证,所以主机错了只会握手失败,不会放你进什么意外的地方。

公网地址会变的话,让它保持有用的是动态 DNS,而客户端的刷新行为决定了变更多快生效。地址变更后要到外部去测,LAN 环境验证不了这一点。要当心把旧应答缓存到记录生存期之后的解析器。还指向旧地址的名字,看起来和一台宕掉的服务器一模一样。

写下配置文件用的是哪个名字,以及这个名字在边界两侧各自解析成什么。一份依赖没人记录过的路由器特性的配置,就是一张等着下次固件升级来兑现的工单。这条记录也能告诉下一个人:内部失败是已知的发夹情形,还是新问题。

相关文章