你以 root 身份运行 up 脚本,它能跑完;可同一个脚本由服务调用时却失败了。文件权限没有任何改动,于是下一个自然的动作是再去放宽权限,而这什么都解决不了。在 SELinux 主机上,决定因素往往不是进程是谁,而是它运行在哪个域里,而被限制域中的 root 依然受限。

同一条命令,两个不同的安全域

类型强制会把源域、目标标签和所请求的操作放在一起评估。你的终端会话运行在不受限域或管理域中,而守护进程运行在其单元所分配的域里。因此相同的命令会得到不同的判定,这种差异是有意设计的,而非策略的缺陷。

正因如此,只有比较两者的上下文,两次运行的对比才有意义。分别从 shell 和从钩子执行同一条命令:shell 版本只能证明二进制存在、参数正确,却完全不能证明服务是否有权执行它。用两种方式各跑一遍,比较的是域,而不只是退出码。正是这种比较,把含糊的权限抱怨变成关于一个域、一个标签的具体问题。

这类故障的典型形态是辅助程序去调用外部工具。钩子本身可能被允许,而它调用的工具带着服务域无权执行的标签,或者写入标签禁止的目录。

读钩子报错旁边的 AVC 记录

AVC 拒绝会点名几个决定性的字段:进程的源上下文、对象的目标上下文、对象类别,以及被拒的具体权限。形成结论之前,四个都要读,因为执行被拒与写入被拒的修法完全不同。

ausearch -m AVC,USER_AVC -ts recent
ls -Z /usr/local/sbin/vpn-up.sh
ps -eZ | grep openvpn

源上下文应当与服务的实际运行域一致,目标上下文应当与操作触及的文件或目录标签一致。若其中任何一项与你追查的报错对不上,你读到的就是另一个事件。

在记录旁边记下策略版本与可执行文件的准确路径。某个发行版策略里出现的域名,只能作为那套安装的证据,并不能保证另一份构建会带上相同的规则。正是这些上下文让他人能在自己的主机上复现你的结论。没有它,记录读起来就像轶事,而不是证据。

标签、配置还是策略:三种修法

第一种修法是标签与文件实际安装位置不符。用 cp 拷到位置上、或用编辑器写出来的脚本,可能带着服务域无法使用的默认标签,恢复应有的上下文即可修好,完全不必动策略。

第二种是配置。发行版可能提供了受支持的操作方式、为辅助程序规定的目录,或已经放行的更新策略包。把布局改成受支持的做法,比写策略更便宜,也能在更新中活下来。

第三种是规则真的缺失,它应当是最后手段。用审计工具生成候选规则,读清它实际放行了什么,把它收窄到你观察到的操作和域,并在它接近生产环境之前请别人复核。

为什么旧 allow 模块不是答案

关于这个故障的搜索结果里全是现成模块,往往对应钩子早已不再调用的工具。装上一个会授予与你的拒绝毫无关系的权限,并在留下永久缺口的同时,把真正的不匹配留在原处。

它还会掩盖下一次失败。一个放行大类操作的模块会让这个报错消失,却把下一个变成更难归因的问题。日后卸载模块也是一件没人愿意排期的事,于是这条捷径往往变成永久的。

有纪律的比较,是在你能读到的拒绝与你提议的改动之间进行的。Red Hat 的 SELinux 故障排查指南 之所以把标签与配置检查放在生成策略之前,正是这个原因。如果记录里没有对应的拒绝,SELinux 很可能不是你的问题,而 路径、沙箱或权限问题仍然没有排除。

相关文章