如何检查 Linux 上的 VPN 隧道是否真的在工作
接口标志、路由查询、表 220 和握手时间戳回答的是不同问题。脚本的每一步都应以能证明其前提的探测为门槛。
Cron 每十分钟重启一次完全健康的 VPN,而同一个任务却让一条死掉的隧道跑上一整周。这两种错误都源于把「已启用」当成一个事实,而它至少是四个。一个设备可以在管理上启用、路由正确、全程加密,却仍然什么有用的都传不了。先把这些观察拆开,检查逻辑几乎自己就写出来了。
链路标志描述的是设备,不是路径
所有人照抄的那项检查是搜索全零的硬件地址。这个技巧在部分驱动里能把真实接口和占位区分开,在其余情况什么也证明不了,因为 tun 设备本来就没有 MAC 可报告。 ip -brief link show tun0 而是给出标志:UP 表示管理员启用了该设备,而 LOWER_UP 表示驱动认为链路可用。两者合起来读,能说明设备存在且未被管理性关闭。它们都不能说明数据包有地方可去。
OpenVPN 在路由安装之前就创建了自己的 tun 设备,所以标志可以看起来完美无缺,而实际上什么都没被路由。把标志当作前提,永远不要当作答案。
问数据包会去哪儿
路由查询回答了标志回答不了的问题。ip route get 10.8.0.1 from 10.8.0.2 会指出接口、源地址以及内核将使用的表,而这正是分流隧道必须对照检查的东西。没有源地址,你问的就是另一个问题;在策略路由下,答案还可能因源而异。这是最便宜也最诚实的测试,因为它查询的规则与你流量将要使用的规则相同。
对 WireGuard 还要加上握手。 wg show 会打印每个对端最近一次握手的时间戳,时间戳停止前进就意味着对端已经消失,无论接口标志怎么说。配置好的接口没有近期握手,是自建监控中最常见的误报之一。
表 220 讲述 IPsec 的故事
strongSwan 在启用路由安装时,会把协商好的选择子路由装进表 220。 ip route list table 220 因而把「已协商」和「已路由」分开:已加载的连接旁边若是空表,说明 IKE 和 CHILD SA 已经建立,却没有流量被导入其中。还要检查 ip xfrm state 的输出,因为它列出已安装的安全关联及其端点。
陷阱策略就是需要留意的情形。它等待感兴趣的流量,而不是直接路由,所以在有东西触发隧道之前,表一直是空的。
管理器状态和守护进程状态是两份不同的清单
NetworkManager 只列出它自己管理的连接。 nmcli -t -f NAME,TYPE connection show --active 对直接由 wg-quick 或 OpenVPN 守护进程启动的隧道什么都不返回。
当脚本必须决定问两个管理器中的哪一个时,这个区别很重要。选在这台主机上真正拥有隧道的那个,忽略另一个的沉默。如果两者都存在,判定哪个是权威的并禁用第二个,因为两个管理器在同一接口上重写路由,会产生没人能复现的故障。某个条目缺失,说明的是管理器,而不是隧道。
用下一步真正需要的探测来设门槛
探测一个 UDP 端口对就绪状态毫无证明力,因为 UDP 没有握手,nc -u 只要数据包发出去就算成功。要探测下一步真正消费的东西:到转发端口的 TCP 连接、对隧道内主机的请求、只能在那里解析的名字。用超时和尝试次数限制等待,耗尽时大声失败。
客户端提供钩子时,钩子胜过轮询。OpenVPN 的 --up 和 --down 脚本在接口状态改变之后运行,这彻底消除了竞态,但它们以守护进程保留的权限运行。如果 OpenVPN 通过 user 和 group 降到了非特权账户,钩子就以那个账户运行。此时重启系统服务需要 sudo 精确放行那条命令。把重试交给服务管理器而不是 cron 循环:带启动上限的有界重启策略会让坏掉的隧道浮出水面,而无界循环会把它无限期地藏起来。