你把写好的一份 nginx 配置从临时目录挪到 /etc/nginx/conf.d/,nginx -t 或 reload 却报"权限被拒"。你 ls -l 一看,权限位是 644、属主属组也都对,任何用户都该能读——可 nginx 就是说读不了。盯着那个 -rw-r--r-- 看半天,怎么看都不像有权限问题。
现象
- 配置文件移到
/etc/nginx/conf.d/后,reload或启动失败; - 报错形如:
[emerg] open() "/etc/nginx/conf.d/site.conf" failed (13: Permission denied)
- 但
ls -l显示权限位正常:
-rw-r--r-- 1 root root 1024 ... /etc/nginx/conf.d/site.conf
644 + root 属主,全局可读,文件权限层面无懈可击,nginx 却读不了。
一个关键的对照实验:用 cat 读一下同一个文件——cat 能正常读出来(说明常规权限没问题),但 nginx 读不了。同一个文件、同一个用户视角,一个能读一个不能,这就把"权限位"这条路彻底排除了。再进一步,以 nginx 的身份去 cat,如果仍然能读出来却依然报错,那就更确定不是权限位的事。
根因
根因是 SELinux 拦的是"安全上下文(标签)",不是权限位;而这个文件带着错误的标签。
每个文件在 SELinux 下都有一个安全上下文,可以用 ls -Z 看到:
ls -Z /etc/nginx/conf.d/site.conf
# 期望类似:
# system_u:object_r:httpd_config_t:s0
# 如果是从 /tmp 挪来的,可能是:
# unconfined_u:object_r:user_tmp_t:s0
上下文里那个中间字段(object_r:xxx)就是文件的类型标签。nginx 运行在受限域下,策略规定:它只能读标签为 httpd_config_t(nginx 配置类型)的文件。
问题就出在你从临时目录挪过来的这个动作上:文件在 /tmp 里创建时,带的是临时文件标签(如 user_tmp_t / tmp_t)。当你用 mv(或 cp 后不重打标签)把它放到 /etc/nginx/conf.d/ 时,标签不会自动跟着目标目录的默认规则更新——于是文件虽然位置上进了 nginx 目录,标签还是"临时文件"。nginx 一看标签不对,直接拒绝读取,报 Permission denied。
这里的核心认知是:chmod 管的是"权限位",SELinux 管的是"标签",两者是两套并行的机制。 权限位再完美,标签不匹配照样被拦。这也是为什么"权限位看起来没问题,却读不了"——因为你检查的是错误的那个维度。
为什么 mv 特别容易踩到?因为 mv 在同一文件系统内只是改了目录项,文件本身(包括它的标签)原封不动。位置上进了新目录,标签还是旧的。而如果目标目录有默认标签规则,mv 并不会触发它。
解决
思路:让文件重新获得目标目录应有的标签。
用 restorecon 按目录的默认规则,把标签修复回来:
sudo restorecon -v /etc/nginx/conf.d/site.conf
restorecon:根据系统策略里该路径的默认标签规则,重新给文件打标签;-v:verbose,打印改了哪些,方便确认。
修复后再用 ls -Z 确认标签已变成 httpd_config_t:
ls -Z /etc/nginx/conf.d/site.conf
# system_u:object_r:httpd_config_t:s0
然后 sudo nginx -t 校验、reload,问题应解决。
预防习惯:凡是往 /etc/nginx/ 放文件,放完都补一次 restorecon。 无论是 mv、cp、还是用脚本生成的配置,只要它来自 /tmp 之类的位置,都要重打标签:
sudo cp site.conf /etc/nginx/conf.d/ && sudo restorecon -v /etc/nginx/conf.d/site.conf
(cp 默认不会保留源标签,有时反而"歪打正着";但 cp -a / mv 会保留,风险更大。稳妥起见,统一补 restorecon。)
延伸与预防
这条坑的通用教训是:chmod 修不了的"权限被拒",多半是 SELinux 标签问题。
可复用的排查流程:
- 报
Permission denied且权限位正常 → 看ls -Z。对比同目录下"本来就好用"的文件,标签不一样就基本坐实。 - 修复用
restorecon,别硬改权限。给文件chmod 777是没用的——它拦的是标签,不是位。正确做法是恢复到策略规定的默认标签。 - 把"跨目录搬运文件"当成会改标签的操作。从
/tmp、家目录搬进系统目录的文件,标签几乎一定需要重打。搬完顺手restorecon,能省下大量排错时间。 - 对整棵目录批量修复:
sudo restorecon -Rv /etc/nginx/。
一句话:权限位和 SELinux 标签是两套闸门,过了第一道,未必过了第二道。
最后,判断"到底是不是 SELinux 干的"有个一秒钟的办法:临时把 SELinux 设为宽容模式(setenforce 0)再试一次。如果问题消失,就确认是标签问题(记得测完改回 setenforce 1)。不过这只是确认手段,不是解决办法——生产环境不该把 SELinux 关掉,正确的做法始终是把标签修对。