星期一早上:星期五建立的隧道已经消失,接口仍在列表里,而日志里没有任何人注意到的记录。主机在线,仪表盘是绿色的,周末它发出的所有东西都走了物理接口。这里缺了两个独立的决定:一是客户端进程能否扛过一次瞬时故障,二是它上面的某个东西会不会在进程退出后重新启动它。只修其中一个,另一个缺口仍然敞开。

从单元实际做了什么开始,而不是从监控声称什么开始。systemctl status openvpn-client@office 和 journalctl -u openvpn-client@office -n 50 显示退出原因以及客户端离开前写下的最后一行。

没人注意时进程已经退出

无错误消失的客户端通常是主动退出的。决定这一行为的是配置,而不是服务管理器:ping-exit 在对端停止响应时立即结束进程,而 ping-restart 保留进程并在其中重新连接。会话上限和空闲计时器从外面看是同一幅图景:没有隧道,也没有告警。

两条 ping 指令不能同时出现在同一个配置里,所以选一条并把选择记录下来。也要看看服务端,因为它自己的 ping-exit 会关闭会话,不管客户端请求了什么,而只有服务端日志会记录谁做了这个决定。

在 ping-exit 与 ping-restart 之间选择

对需要自我修复的笔记本或远程站点选 ping-restart,对面是必须让死隧道停掉进程、好让别的东西接手时,才保留 ping-exit。数字不如关系重要:ping 10 配 ping-restart 60 意味着探测前沉默十秒、重启前沉默六十秒,这在对丢包敏感的移动链路上很激进,在有线链路上则合理。把原因写在那一行旁边,否则下一个读者会以为这些默认值是刻意为之。

然后确认退出的确实是进程。隧道已死却报告为活动状态的单元是另一种故障,下面会讨论。

真正能重启单元的监管

在 systemd 主机上,最小的持久改动是一个 drop-in 文件:Restart=on-failure 和 RestartSec=10 放在 /etc/systemd/system/[email protected]/restart.conf 里。drop-in 只有在 systemd 重新加载自身定义时才会被读取,所以之后要执行 systemctl daemon-reload。用 systemctl show -p Restart -p ExecMainStatus openvpn-client@office 去验证,而不是假定值已经生效。文件名里的实例必须是该单元真正加载的配置,所以为 office 写的文件对 openvpn-client@home 毫无作用。

on-failure 会忽略一种情况:状态为零的干净退出。有些拆卸路径就是这样结束的,所以要读取记录下来的状态,而不是相信手册页的措辞。如果你的客户端以零状态放弃,你需要 Restart=always,代价是真正损坏的配置会反复回来,直到启动速率限制生效、日志被填满。

Restart=always 在刻意停止之后也会触发。维护时用 systemctl stop,如果工作时必须让单元保持停止,就把它屏蔽掉。

在隧道断开时抓住泄漏

监管让隧道恢复;它并不能阻止隧道消失时流量照常出去。persist-tun 会在重连之间保留设备和它的路由,这很方便,也打开了一个窗口。设备还在,路由仍然指向它,数据包就毫无保护、也无从知晓地漏了出去。这是策略选择,不是中立的默认值。

当一台主机根本不允许发出未受保护的数据包时,只有防火墙才能保证这一点。允许隧道接口和 VPN 服务器地址、丢弃其余一切的输出规则,在客户端死掉之后依然有效,而客户端自身配置里的任何东西都无法过滤它没有承载的流量。

用刻意停止客户端来检验它。执行 systemctl stop openvpn-client@office,然后执行 curl -m 5 https://example.com,它应当失败,而不是通过物理链路成功。成功意味着防火墙规则缺失或过于宽松。

重新连接,确认隧道重新承载流量,然后才把检查加入监控。用 cron 轮询 ip addr 来检查 tun 设备,是最常失败的那种监管,因为有了 persist-tun,设备比会话活得更久,探测报告健康时其实什么都没在承载。

相关文章