场景概述
Permission denied 的含义是「这个身份对这条路径的某个环节没有权限」。关键在于路径不是单一文件,而是从根到目标的一整条链:任何一级目录缺少执行权限,访问就会在该级中断;此外进程的真实身份可能是服务账号而不是登录用户,文件系统之上还可能有 ACL、不可变属性与 SELinux 标签在起作用。按链条顺序排查,比反复修改目标文件的权限位有效得多。
场景信息
| 触发条件 | 服务或用户访问文件、目录、套接字时报 Permission denied |
|---|---|
| 排查目标 | 定位权限链条上的断点层级,而不是只调目标文件权限 |
| 工具链 | namei 查路径链 → id 与 /proc 查身份 → getfacl 查 ACL → lsattr 查属性 → audit 查安全模块 |
| 排查顺序 | 先看是不是身份不对,再看路径链,最后才怀疑安全模块与挂载参数 |
四个常见断点
- 身份不对:进程以服务账号运行,而你用 root 或自己的账号测试,结论不成立
- 路径中间目录缺 x:读写权限都有,但没有执行位就进不去目录
- ACL 覆盖了基础权限:getfacl 能看到额外的授权与拒绝条目
- 安全模块与挂载参数:SELinux 标签不匹配、NFS 的 root_squash 把 root 映射成匿名用户、挂载时带了 noexec 或 ro
四步排查命令
四步排查命令
# 第 1 步:目标与路径逐级检查
ls -l /srv/app/data/report.csv
namei -l /srv/app/data/report.csv # 逐级显示每层目录权限
# 第 2 步:核对进程实际身份与组
ps -o user,group,pid,cmd -p $(pgrep -f app.jar | head -1)
id appuser
cat /proc/$(pgrep -f app.jar | head -1)/status | grep -E '^Uid|^Gid'
# 第 3 步:检查 ACL 与特殊属性
getfacl -p /srv/app/data/report.csv
lsattr /srv/app/data/report.csv 2>/dev/null
# 第 4 步:安全模块与挂载参数
getenforce; ls -Z /srv/app/data/report.csv
ausearch -m avc -ts recent 2>/dev/null | tail
findmnt -T /srv/app/data -o TARGET,SOURCE,FSTYPE,OPTIONS现象对照根因
| 现象 | 常见根因 | 定位方式 |
|---|---|---|
| 文件权限看起来没问题但仍被拒 | 路径中间目录缺 x 权限 | namei -l 逐级对照 |
| 手工测试通过、服务运行报错 | 进程身份不同(服务账号 vs 登录用户) | 查 /proc/PID/status 的 Uid 与 Gid |
| ls -l 权限正常却仍被拒 | ACL 中有拒绝条目或额外授权 | getfacl -p 查看全部条目 |
| 日志里只有 Permission denied,dmesg 有 avc | SELinux 标签不匹配 | ls -Z 看标签、ausearch 看拒绝记录 |
| root 也无法访问网络存储上的文件 | NFS 服务端启用了 root_squash | 查服务端 /etc/exports 的导出选项 |
目录权限的三个位含义不同:r 允许列出目录内容,x 允许进入与访问其中已知名字的条目,w 允许在其中创建或删除条目。只有 r 没有 x 时能 ls 出名字却打不开文件;只有 x 没有 r 时知道路径能直接访问,但列不出来——这两种组合在排查中经常被混淆。
临时验证身份可以用 sudo -u 服务账号 执行同一条访问命令,能复现问题就说明是权限问题;也可以用 setpriv 或 runuser 更贴近服务运行环境。注意用 root 测试通过不能证明服务账号可以访问,这是排查中最常见的误导。
不要用 chmod -R 777 或关闭 SELinux(setenforce 0)来「解决」权限问题:前者打开了越权与篡改的口子,后者让整个宿主机的强制访问控制在排查结束后被长期遗忘。正确做法是校正属主与属组、补齐必要的 x 位,或在 SELinux 场景下用正确的上下文标签(chcon、restorecon、semanage fcontext)。
先弄清「谁被拒」比反复核对自己的权限更有效:确认报错来自哪个进程、以哪个用户身份运行,再沿着路径逐级核对执行权限、附加权限列表、挂载选项与安全模块策略,四层里任何一层的拒绝都表现为同一条 Permission denied。