隧道已经起来,wg show 显示最近完成过一次握手,你能 ping 通服务器的隧道地址,可它之外的任何东西都不回应。第一反应往往是重新生成密钥或重新分享配置。这恰好改动了路径中已经被证明可用的那一段。

一次成功的握手只说明两端在密钥上达成一致、UDP 能到达端点、加密路由接受这个数据包。它完全不说明数据包随后能否离开服务器。

所以别再猜,去追踪一个数据包。它可能死掉的只有四个地方,每个地方留下的痕迹都不同。

握手到底证明了什么

WireGuard 是一种 UDP 协议:先用 Noise 握手建立会话,然后为对端加密数据包。当 wg show 显示几秒前的握手和不断增长的传输计数时,端点、密钥以及对端公钥全都是正确的。

它没有证明的是:你的客户端到底有没有把发往网络的流量送进接口,服务器愿不愿意转发,以及回应能不能找到回来的路。这些是内核路由和防火墙的决定,都在隧道之外完成。

这就是为什么 ping 服务器的隧道地址是一个极具误导性的测试。那个数据包 是发给服务器自己的,所以在本地就结束了。它永远不需要转发,不需要地址转换,也不需要来自网络的返回路由。

AllowedIPs 是一个路由决定

在客户端上,AllowedIPs 承担两个职责。它既是加密密钥路由表,决定哪些目标地址进入这个对端的隧道,又是对端愿意接受你哪些源地址的清单。如果客户端配置里只写了 10.8.0.0/24,公网目标就永远不会进入接口,你原来的默认路由会把它们带出隧道。这时隧道工作得完美无缺,却没做任何你想要的事。

看客户端实际装了什么,而不是配置里写了什么:

wg show wg0 allowed-ips
ip route get 1.1.1.1

第二条命令才是决定性的。如果答案里出现你的物理接口和常规网关,说明数据包根本没被隧道接管,服务器端怎么改都无济于事。分别给 IPv4 加上 0.0.0.0/0,给 IPv6 加上 ::/0;只覆盖一族的通配规则会把另一族留在原路径上,这正是 IPv4 隧道能用而 IPv6 解析卡住的常见原因。

数据包在网关上死在哪里

如果路由正确而数据包仍然无处可去,说明它到了服务器就停住了。四项检查覆盖大多数网关。

转发。 sysctl net.ipv4.ip_forward 返回 0 是经典答案,很多发行版默认仍是 0。它必须是 1,而且要写进 sysctl 文件长期生效,而不是用一条重启就消失的命令。IPv6 转发是另一个开关。

过滤策略。 默认策略为 drop 的 FORWARD 链会丢掉隧道流量,却放行到主机自身的流量,这与症状完全吻合。要读正在生效的规则集,不是存盘文件:nft list ruleset 或 iptables -S。

地址转换还是路由。 使用私有隧道地址的客户端,要么在服务器出口接口上做源 NAT,要么使用上游网络知道如何回传的路由前缀。二者之间的选择取决于地址规划。给一个本来就会回传你前缀的网络再加 MASQUERADE 无害但无意义;而在没有路由的地方省掉它,回应就会指向一个不可路由的私有网段。

返回路径。 在路由式设计里,上游路由器需要一条指向你服务器的路由。云厂商的网络也可能拦下它:一些云在实例上开启了源/目的地址检查,会把源地址不是实例自身地址的数据包静默丢弃,直到你关掉这项检查。

DNS 的失败方式与路由不同

发往已知地址的数据包和域名解析是两种不同的故障,而报错信息会往相反方向误导你。如果 ping 1.1.1.1 能通而浏览器什么都打不开,那是路由没问题、解析有问题。如果 ping 本身就失败,DNS 就无关紧要,你该回到上一节。

wg-quick 可以从配置里的 DNS = 行设置解析服务器,而 systemd-resolved 对每个链路的处理又各不相同。一个用只能在隧道内访问的地址覆盖解析器的 VPN,会在隧道断开的瞬间让所有解析失败,看起来就像隧道把网络弄坏了。

SymptomLayer to inspect
Tunnel address pings, nothing else doesClient route, then gateway forwarding
Public IP pings, hostnames failResolver selection and reachability
Handshake completes, no reply everFORWARD policy or missing NAT on the server
Small requests work, large transfers stallMTU and TCP MSS clamping

一个能产出证据的顺序

先跑客户端侧的检查,因为它只有一条命令,却能排除一半可能。然后确认服务器本来是否被设计成网络出口;为私有访问而建的网关,在公网目标不通时并不算坏。

在服务器上,一边从客户端发 ping,一边看接口:tcpdump -ni wg0 icmp 应该显示请求,tcpdump -ni eth0 icmp 应该显示它带着或不带转换地出去。如果只在前者出现、后者从未出现,问题就停在服务器本地。如果它出去了却没有回应回来,问题在上游,那是另一回事。

最后让修复扛住一次重启。转发应该写进 sysctl 文件,防火墙规则应该交给加载它们的服务,客户端路由应该放进把接口拉起来的配置,而不是一个你迟早会忘掉的 shell。重连、重跑同样两项测试,确认计数在动。

相关文章