systemd 服务启动被拒:从 SELinux AVC 日志到沙盒指令的排查顺序
来源:Linux systemd 服务权限深度排查:从 SELinux 到沙盒配置的完整指南
这类问题跳出了「用户-组-其他」rwx 权限检查的思路。systemd 是深度集成的系统与服务管理器,安全边界比一条 shell 命令复杂:表象是执行命令被拒绝,根源可能藏在文件系统属性、安全模块或路径解析里。
排查维度一:强制访问控制(MAC)的拦截
SELinux(多见于 RHEL/CentOS/Fedora)与 AppArmor(多见于 Ubuntu/Debian)定义了进程能访问哪些资源。传统权限允许的操作,它们同样可以拒绝。
- 确认 SELinux 状态:
sudo sestatus。状态为enforcing时,它很可能就是原因。 - 查看实时拒绝日志:
sudo ausearch -m avc -ts recent
也可以直接看审计日志:
sudo grep "avc:.*denied" /var/log/audit/audit.log | tail -20
日志会记录哪个进程(scontext)试图访问哪个资源(tcontext)、被拒绝了什么操作(tclass)。
- 解读与修复。假设日志中有一行:
type=AVC msg=… scontext=system_u:system_r:init_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file { read }
含义是运行在 init_t 域的进程试图读取一个标记为 default_t 类型的文件,被拒绝。
- 临时验证(生产环境慎用):把 SELinux 切到宽容模式确认问题来源:
sudo setenforce 0。服务能启动,基本可以确定是 SELinux 问题,但不要把它当永久方案。 - 正确做法:修改文件安全上下文,用
semanage fcontext配合restorecon。应用文件在/opt/myapp/下时,为它设置合适的上下文(如bin_t):
sudo semanage fcontext -a -t bin_t "/opt/myapp(/.*)?"
sudo restorecon -Rv /opt/myapp
排查维度二:systemd 沙盒指令
systemd 提供了一批以 Protect、Private、Restrict 开头的沙盒指令来限制服务的运行环境。配置不当,服务会被关在笼子里,访问不到必要资源。
ReadWritePaths、ReadOnlyPaths:显式指定服务可写与只读的路径。服务需要写入的目录不在列表里就会失败。PrivateTmp=yes:服务拥有私有的/tmp和/var/tmp。服务脚本若期望使用系统共享的临时文件,就会出问题。ProtectSystem=strict/ProtectHome=yes:严格保护系统目录与家目录,使其只读或不可访问。NoNewPrivileges=yes:阻止服务进程提升权限。CapabilityBoundingSet:限制服务可用的 Linux 能力(Capabilities)。服务需要绑定 1024 以下端口(如 80),但没有CAP_NET_BIND_SERVICE、又不是以 root 运行时,在ProtectSystem=yes下启动就会失败。
沙盒配置原则
- 按服务实际需求放宽限制,而不是全部关闭。服务只需要写
/var/log/myapp,就设置ReadWritePaths=/var/log/myapp,不必关掉所有保护。 - 服务需要特定能力时(例如绑定低端口),可添加:
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=~CAP_NET_BIND_SERVICE
波浪号表示保留该能力。
- 临时诊断方法是:在服务文件的
[Service]段注释掉(或设置为no)所有Protect*、Private*、Restrict*指令,重载后尝试启动服务。启动成功再逐一加回指令,定位具体是哪条限制导致的问题。改动 unit 文件后可用systemd-analyze verify检查语法与指令拼写,再systemctl daemon-reload生效。