仪表盘想要一个“已连接多久”的数字,而 OpenVPN 并不提供。客户端知道隧道是什么时候起来的,但前提是在会话状态丢失之前有人记下那一刻。受支持的位置是 up 钩子,值得保存的值是原始 epoch 秒,而不是格式化字符串。之后的一切都是算术,外加几条决定这个数字是否仍然诚实的顺序规则。

确认钩子确实会运行

在围绕它做任何设计之前,先证明钩子会触发。最小客户端配置需要 script-security 2 以及显式路径 up 和 down;没有安全行,OpenVPN 会拒绝执行脚本并明确说明。同时加上 up-restart 和 down-pre,让重启与拆除路径的行为可预测。

让钩子的第一版只把自己的环境追加到文件里,别的什么都不做。这样你能看到构建实际导出的变量名,例如 dev 和 script_type,而不是凭另一版本记忆的那些。执行一次连接和一次断开,然后读取文件。如果什么都没出现,就停在这里,因为建立在永不运行的钩子上的时间戳设计,会让监控看起来坏上好几周。

检查钩子以哪个用户运行。设置 user 和 group 后,OpenVPN 会在运行脚本前降低权限,因此 root 可写的目录可能对钩子关闭。路径要从服务的角度选,而不是从你 shell 的角度。

记录一个 epoch 值,并有意清除它

up 钩子只需要一行,date +%s > /run/openvpn-client.started,路径按实例调整。epoch 值可以避免日后语言、时区与格式的疑问,而且它与监控代理自己的时钟比较时是同一个数字。

清除和写入同样重要。down 钩子应当清空或删除文件,而 down-pre 让这件事在接口关闭前发生,从而消除文件仍在描述一个已经消失的隧道的窗口。up-restart 随后在内部重启时刷新时间戳,而不是保留原值。没有这一对,ping 超时后的重启 会产生一个在隧道重建期间不断攀升的数字。

选择寿命合适的存放位置。/run 下的文件会在重启时消失,这符合现实;而 /tmp 下的文件可能被不可预测地清理,配置目录里的文件则会以误导的方式留存。设置权限,只让服务用户和监控读取者能读它。

不要相信日志行,自己计算运行时长

读取保存的秒,读取当前秒,相减。文件缺失或为空时,诚实的答案是没有任何隧道处于活动状态,而不是运行时长为零,因为这两种情况应当触发不同的告警。原始秒留在监控负载里,天和小时放到展示层格式化。在导出器内部四舍五入的格式化器,会丢掉事故中有人需要的精度。

解析日志时间戳是诱人的捷径,但它在三处失败:格式取决于所设选项,轮转会删掉历史,重连会表现为彼此独立的会话而非一条连续隧道。钩子在事件发生的那一刻只记录一次。

当检查在另一台主机上运行时,发送时间戳而不是时长,让读取者套用自己的时钟。这样小的负值会被读成时钟偏差,而不是钩子坏了。

覆盖重连、重启与多实例

一台主机上的两条隧道需要两个文件,键就是服务已经在用的实例名。共享路径意味着最近的连接会覆盖每个配置的开始时间,症状是第二条隧道启动时数字往回跳。

重连行为值得单独测试,因为看似合理的设计恰恰在这里悄悄报出错误。通过丢弃对端或给进程发信号来强制重启,然后把接口状态与文件对照。隧道回来的那一刻,时间戳应当向前推进。

在服务器端,状态文件为每个客户端带一个 connected-since 值,它回答的是另一个问题:服务器最后一次看到该对端是什么时候。把它用于交叉核对而不是替代,因为它会随所描述的会话一起消失。两种测量都说明不了隧道背后应用的健康状况。

运行能抓住顺序缺陷的三项检查

测试正常连接、强制重连和干净停止,每次之后把接口列表与文件一起读。干净停止后仍然存在的时间戳,或声称存在接口列表里没有的隧道的文件,都直接指向钩子顺序。检查要以服务用户身份、用服务的路径执行,因为 root shell 可能看到另一个文件并给出自信的错误答案。然后让一个慢速轮询器读取该文件,确认它从不写入。

每六十秒一次的轮询足以覆盖告警,任何钩子都不应依赖那个轮询器还活着。把这两项职责分开,无论哪一侧重新部署,数字都仍然可信。

相关文章