一次 SELinux 排障实战:从 Permission denied 到彻底解决
一次看似简单的服务启动失败,背后隐藏着 SELinux 对软链接的"特殊关照"
一、事故现场:服务起不来了
上班第一天,测试同事发来消息:"服务起不来了,报 Permission denied。"
查看服务状态:
[root@S-756 test]# systemctl status FrameWorkA_dfitc.service
● FrameWorkA_dfitc.service - FrameWorkA_dfitc
Loaded: loaded (/etc/systemd/system/FrameWorkA_dfitc.service; disabled)
Active: activating (auto-restart) (Result: exit-code)
Process: 538504 ExecStart=/root/gyb/test/FrameWorkA_dfitc (code=exited, status=203/EXEC)
Main PID: 538504 (code=exited, status=203/EXEC)
查看日志:
[root@S-756 test]# journalctl -u FrameWorkA_dfitc.service
Jun 24 10:19:34 S-756 systemd[538504]: FrameWorkA_dfitc.service: Failed to locate executable /root/gyb/test/FrameWorkA_dfitc: Permission denied
Jun 24 10:19:34 S-756 systemd[538504]: FrameWorkA_dfitc.service: Failed at step EXEC spawning /root/gyb/test/FrameWorkA_dfitc: Permission denied
status=203/EXEC 这个错误码很明确——systemd 尝试执行程序时失败了。而 Permission denied 则给了我们第一个线索:这不是文件不存在,而是权限问题。
二、排查思路:从文件权限到 SELinux
第 1 步:检查文件是否存在
[root@S-756 test]# ll /root/gyb/test/FrameWorkA_dfitc
lrwxrwxrwx. 1 root root 24 Jun 23 17:51 /root/gyb/test/FrameWorkA_dfitc -> /root/gyb/test/FrameWorkA
文件存在,是软链接,权限 777,没问题。
第 2 步:快速定位——关闭 SELinux
排障的基本原则:先确认问题范围。执行 setenforce 0 临时关闭 SELinux:
[root@S-756 test]# setenforce 0
[root@S-756 test]# systemctl restart FrameWorkA_dfitc.service
[root@S-756 test]# systemctl status FrameWorkA_dfitc.service
● FrameWorkA_dfitc.service - FrameWorkA_dfitc
Active: active (running) # ✅ 起来了!
确认了,是 SELinux 的问题。
⚠️ 注意:
setenforce 0仅用于测试定位,重启后失效,不要用它作为永久解决方案。
第 3 步:查看 SELinux 上下文
[root@S-756 test]# ls -Z ./FrameWorkA*
unconfined_u:object_r:admin_home_t:s0 ./FrameWorkA
unconfined_u:object_r:admin_home_t:s0 ./FrameWorkA_dfitc
发现问题了:
- 当前目录是
/root/gyb/test/ - 文件的 SELinux 类型是
admin_home_t - 这是 root 主目录下文件的默认类型
核心知识点:systemd 服务能否执行一个文件,取决于文件的 SELinux 类型。
| SELinux 类型 | 说明 | systemd 能否执行 |
|---|---|---|
bin_t |
系统可执行文件 | ✅ 允许 |
usr_t |
/opt 目录文件 | ✅ 允许 |
admin_home_t |
root 主目录文件 | ❌ 拒绝 |
user_home_t |
普通用户主目录文件 | ❌ 拒绝 |
tmp_t |
/tmp 临时文件 | ❌ 拒绝 |
真相大白了:程序放在 /root/ 下,SELinux 类型是 admin_home_t,systemd 拒绝执行。
三、尝试修复:chcon 为什么不生效?
既然知道了是类型不对,那就改类型:
[root@S-756 test]# chcon -t bin_t FrameWorkA_dfitc
[root@S-756 test]# ls -Z FrameWorkA_dfitc
unconfined_u:object_r:admin_home_t:s0 FrameWorkA_dfitc # 没变!
奇怪,为什么 chcon 没生效?
原因 1:chcon 默认行为是跟随软链接
chcon -t bin_t link # 修改的是 link 指向的目标文件
chcon -h -t bin_t link # 修改 link 本身(需要 -h 参数)
原因 2:软链接上下文固化
软链接在创建时继承了当时父目录的上下文。即使父目录后来改了,软链接本身的上下文也不会自动更新。
# 查看父目录上下文
[root@S-756 test]# ls -Zd /root/gyb/test/
unconfined_u:object_r:admin_home_t:s0 /root/gyb/test/
父目录是 admin_home_t,软链接创建时继承了这个类型,然后"固化"了。
四、解决方案:两招搞定
方案一:先改源文件,再重建链接(推荐)
这个方案的逻辑是:先确保源文件类型正确,然后重建软链接,让新链接继承正确上下文。
# 1. 修改目标文件为 bin_t
chcon -t bin_t FrameWorkA
# 2. 删除旧链接,重建新链接
rm -f FrameWorkA_dfitc
ln -s FrameWorkA FrameWorkA_dfitc
# 3. 验证
[root@S-756 test]# ls -Z FrameWorkA*
unconfined_u:object_r:bin_t:s0 FrameWorkA ✅
unconfined_u:object_r:bin_t:s0 FrameWorkA_dfitc ✅
# 4. 恢复 SELinux,启动服务
setenforce 1
systemctl start FrameWorkA_dfitc.service
方案二:彻底规避——放到 /opt 目录
如果不想每次都处理 SELinux 上下文,直接把项目放到系统标准路径下。
# /opt 的 SELinux 类型是 usr_t
[root@S-756 dfitc]# ls -Zd /opt
system_u:object_r:usr_t:s0 /opt
# 程序自动继承 usr_t,systemd 允许执行
[root@S-756 dfitc]# ls -Z FrameWorkA
unconfined_u:object_r:usr_t:s0 FrameWorkA ✅
完整操作:
cp -r /root/gyb/test /opt/dfitc
cd /opt/dfitc/ && ./pobobash.sh
systemctl start FrameWorkA_dfitc.service
五、脚本改进:自动化处理
如果项目必须留在 /root/ 或 /home/ 下,可以修改部署脚本,在创建软链接前先处理 SELinux 上下文:
create_symlink_with_context() {
local src="$1"
local dst="$2"
# 先修改源文件上下文
sudo chcon -t bin_t "$src" 2>/dev/null || true
# 创建软链接
ln -s "$src" "$dst"
# 验证
echo "Context: $(ls -Z "$dst" | awk '{print $1}')"
}
六、知识点总结
1. SELinux 上下文类型速查
| 命令 | 作用 |
|---|---|
getenforce |
查看 SELinux 状态 |
setenforce 0 |
临时关闭(测试用,重启失效) |
setenforce 1 |
恢复强制模式 |
ls -Z <文件> |
查看文件 SELinux 上下文 |
ls -Zd <目录> |
查看目录 SELinux 上下文 |
2. 软链接的 SELinux 坑
- 软链接有自己的 SELinux 上下文,不与目标文件同步
- 创建时继承父目录的上下文
- 删除重建是刷新上下文最可靠的方式
chcon默认修改目标文件,改链接本身需要-h参数
3. systemd 执行权限速查
| 路径 | SELinux 类型 | systemd 能否执行 |
|---|---|---|
/usr/local/bin/ |
bin_t |
✅ 允许 |
/opt/ |
usr_t |
✅ 允许 |
/root/ |
admin_home_t |
❌ 拒绝 |
/home/ |
user_home_t |
❌ 拒绝 |
/tmp/ |
tmp_t |
❌ 拒绝 |
4. 排障流程
服务启动失败 (status=203/EXEC)
↓
检查文件是否存在 → 否 → 修复路径
↓ 是
检查执行权限 → 否 → chmod +x
↓ 是
setenforce 0 测试
↓
服务启动成功 → SELinux 问题
↓
ls -Z 检查上下文
↓
方案一:chcon -t bin_t + 重建链接
方案二:移到 /opt 目录
↓
setenforce 1 恢复
↓
服务正常运行 ✅
七、写在最后
这次排查花了不少时间,根源在于对 SELinux 和软链接交互行为的认知不足。有几个要点值得记住:
setenforce 0是排障利器,快速确认问题范围,但永远不要用它做永久解决方案- 软链接不是透明的,它有自己独立的 SELinux 上下文
chcon默认跟随软链接,修改链接本身需要-h参数/opt是第三方应用的理想归宿,SELinux 类型为usr_t,天然支持 systemd 执行- 永久关闭 SELinux 是下下策,用正确方式配置它,而不是绕过它
希望这次踩坑记录能帮你少走弯路。
相关文档:SELinux 官方文档 | systemd 服务管理

浙公网安备 33010602011771号