该放弃 chroot 还是把 iproute2 复制进监狱?
不完整的监狱在启动之后失败,而不是启动时。在放宽加固与把二进制文件和库复制进新根目录之间做出选择。
服务启动了,客户端也连上了,可服务器端始终不出现路由。或者守护进程拒绝启动,并报出一个无法执行的命令。两种结果都出自同样的三行配置,而这三行通常是照着加固指南一起抄来的:切换到非特权账户、放弃用户组、更改文件系统根目录。三者中只有一项会隐藏文件,也只有一项 剥夺修改路由表的权限。把它们拆开来测,你才知道自己真正想要哪一行。
监狱取走了什么,而守护进程仍然需要
更换根目录发生在启动之后,所以守护进程保留已经打开的一切,却失去之后才会打开的一切。此后连接所运行的任何辅助程序,都必须存在于新根目录之内,连同它加载的共享库。只用配置文件和证书搭起来的监狱,两样都没有。
切换到非特权账户是另一项限制。交出 root 的进程即使二进制就在旁边,也可能无法修改路由或拉起网卡,因此这两种故障必须分开排查。
三个条件决定你需要哪种修法
第一个条件是你的构建如何安装路由。较新的 OpenVPN 版本在进程内配置网卡和路由表,而较旧的软件包和自定义配置则依赖外部工具,或依赖 up 与 down 脚本。如果日志报出某个命令,说明涉及外部工具,是监狱把它藏了起来。如果日志报的是权限错误,那问题就在账户上。
第二个条件是维护。复制二进制的监狱必须在提供它们的软件包每次升级时刷新,否则第一次升级就会留下一条断掉的隧道。第三个条件是某项控制是否真的需要 chroot;如果这项要求其实只是权限分离,那么只切换账户可能就够。
| Signal in the log | Other side of the boundary | Repair direction |
|---|---|---|
| Command not found after startup | Binary and its libraries absent inside the new root | Populate the jail, or drop the chroot line |
| Operation not permitted | Account lost the privilege to edit routes | Keep the account privileged, or pre-create routes elsewhere |
| No such device | Jail lacks the tun device node | Create the node inside, or open it before the root change |
一个能用的监狱必须包含什么
运行命令的监狱需要二进制、它链接的每一个库,以及守护进程稍后才打开的每个设备节点。把这些部件复制进去,在没有 VPN 的情况下单独测试:
ldd /usr/sbin/ip
chroot /etc/openvpn/jail /sbin/ip route show
chroot /etc/openvpn/jail /sbin/ip link set tun0 up
第一条命令列出依赖,而这一条恰恰最常被跳过。复制进来的二进制找不到库时,报错与二进制缺失并不相同,而从客户端看,两者都像 OpenVPN 故障。在监狱里手动运行该命令,不用重启服务就能复现故障。
把配置、证书和任何 CRL 也一并复制进监狱,因为守护进程期望在启动后重读的文件,一旦根目录变了就找不到。记下复制了什么,因为网络软件包的下一次升级会替换原件,把监狱留在旧副本上。
反过来缩小加固范围
如果监狱只是因为某份指南这么写才存在,那么删掉那一行、保留账户限制,是站得住脚的结果。守护进程保住自己的文件系统视图,问题也消失了,而降权仍然把 root 从长驻进程中拿走。
把剩下的写下来,也写下理由。配置悄悄丢了一项控制却没有备注,加固就是这样走偏的,而下一次审计会问那一行为什么不见了。
选择之前先把三行拆开
复制配置,禁用全部三条指令,确认客户端能连上、预期路由出现。然后再只启用账户相关的行,最后单独启用 chroot。 隧道在哪一步断掉,边界就在哪里,这样你就不必重建一个从来不是原因的监狱。
恢复路由之后,以更高的日志级别跑一次客户端会话,读出那条指明命令或权限失败的行。这一行比客户端最终报告的超时更有价值。