只能有一个管理客户端:为什么你的监控会挤掉图形界面
管理套接字一次只接受一个连接。第二个订阅者要么连不上,要么从持有提示的组件那里夺走控制权。
桌面图形界面持有管理连接,所以想自己开一条连接的监控脚本什么也读不到,更糟的是界面不再响应提示。常见的结论是守护进程不稳定。在社区版守护进程里,这个限制是设计使然:控制接口一次只服务一个已连接的管理客户端,而这个数字与 VPN 能承载多少用户毫无关系。
一个套接字,一个持有者
管理通道只接受一个已连接的客户端。当第一个仍处于活动状态时来了第二个,它要么等待,要么被拒绝,并不存在藏在某个选项后面的并行订阅模式。断开当前持有者会让守护进程接受下一个,这是一次交接,而不是第二条通道。
这让每个额外工具都成了竞争者。如果图形界面持有连接,轮询脚本要么连不上,要么只有在界面放弃之后才能连上,而且这个顺序在下次重连时还会重演。如果脚本先连上,图形界面就失去了控制路径,它的提示也随之失效。两个工具都不会报告冲突,因为每个只看得见自己失败的那条命令。
| What you need | Mechanism that fits | What it cannot do |
|---|---|---|
| Periodic list of connected clients | Status file written on an interval | Deliver events as they happen |
| Interactive prompts and session control | The single management connection | Serve two controllers at once |
| Multiple consumers of either | One owner process that fans out | Exist without you writing it |
决定由哪个组件持有控制权
为交互通道选一个持有者,并让它成为唯一连接的东西。管理连接的图形界面、应答认证提示的脚本、终止会话的控制器都算持有者,而这个角色只能由一个来担任。
当图形界面持有连接时,使用该界面提供的集成,而不是与它争抢。和持有者争抢套接字会产生间歇性故障,在用户看来像是随机发生的,而调试的人通常只看到竞争的后半段。记下重启后哪个进程先连接,因为这个顺序每次都决定了谁胜出。比界面先启动的监控会一直占着通道,直到界面放弃。
让采集器使用状态快照
大多数监控需要的是一份清单,而不是一段对话。按固定间隔写出的状态文件就能回答这个问题,而且不必触碰控制通道,在图形界面保持连接的同时继续工作。
status /run/openvpn/server.status 10
management /run/openvpn/server.sock unix
间隔就是新鲜度的预算,所以要按照看板真正需要的来设定,而不是取看起来最安全的那个最小值。每隔几秒写一次的快照很便宜;每隔几秒就重连控制套接字的脚本则不便宜,因为每次重连都在和持有者竞争。
快照既不是历史,也不是事件流。在一个间隔内来去匆匆的连接永远不会出现,因此靠轮询一个不断自我覆盖的文件无法满足审计要求。如果你需要知道某个用户到底有没有连上过,快照就是错误的工具。
分发是一种设计,而不是一种模式
如果多个消费方确实需要实时的管理数据,就把一个可信组件放在套接字前面,让它把经过严格挑选的观测结果发布给其余各方。这个组件持有重连、认证提示以及什么可以安全暴露的决定权。
要有意识地构建它:定义允许哪些查询,记录是谁发起的,并避免让分发层意外变成远程控制面板。守护进程没有可以打开的多客户端模式,也没有任何配置行能造出一个。把持有者视为安全边界的一部分,因为它掌握着能断开人们连接的接口。重启它应当是例行操作,并有文档化的恢复路径。
把套接字当作特权,而不是指标
管理接口可以断开用户,改变状态,并读取点名到人的事件。它的密码只用于认证客户端,并不加密传输,因此网络监听者会把这段对话暴露给任何能观察到它的人。
优先使用本地套接字或严格受限的监听器,并检查你的采集器实际指向的是哪个进程,因为服务器产品自带的管理 API 行为并不相同。指向错误守护进程的采集器会得出自信却错误的结论。在调试一个什么都不返回的查询之前,先确认路径和所属进程。