背景
在 CentOS 7 上用 Nginx 部署一个静态站点,root 指向非标准目录 /data/www,监听自定义端口 12000。过程中连续踩了两个坑:
- Nginx 启动失败:
bind() to 0.0.0.0:12000 failed (13: Permission denied) - 启动成功后访问页面:
"/data/www/index.html" is forbidden (13: Permission denied)
两个错误的共同点都是 13: Permission denied,但根因都是 SELinux,而不是普通的文件权限或端口占用。本文把整个排查过程整理出来,供遇到类似问题的同学参考。
环境
- CentOS Linux 7 (Core)
- Nginx 反向代理 + 静态站点
- 站点根目录:
/data/www - 监听端口:
12000 - 代理上游:
127.0.0.1:180 - SELinux:Enforcing
问题一:Nginx 启动失败,端口绑定被拒
现象
1systemctl status nginx.service 2 3nginx: the configuration file /etc/nginx/nginx.conf syntax is ok 4nginx: [emerg] bind() to 0.0.0.0:12000 failed (13: Permission denied) 5nginx: configuration file /etc/nginx/nginx.conf test failed 6
原因分析
12000 虽然是高于 1024 的端口,普通用户理论上可以绑定,但在 SELinux Enforcing 模式下,SELinux 策略默认只允许 HTTP 服务绑定 http_port_t 类型里的端口。12000 不在允许列表中,所以 Nginx 的 worker 进程(非 root)绑定该端口时被 SELinux 拒绝。
解决方案
把 12000 加入 http_port_t 类型:
1semanage port -a -t http_port_t -p tcp 12000 2 3# 验证 4semanage port -l | grep http_port_t 5
如果
semanage不存在,先安装:1yum install -y policycoreutils-python 2
然后重启 Nginx:
1systemctl restart nginx 2
备选方案
- 临时验证:
setenforce 0后重启,能启动即确认是 SELinux 问题,但不推荐长期关闭。 - 开启
httpd_can_network_connect:可以解决绑定问题,但这个布尔值本来是放行出站连接的,用它解决绑定问题会放宽不必要权限,有安全风险。
推荐方案一:只放行单个端口,最小化改动。
问题二:页面 403 Forbidden
现象
Nginx 启动正常,访问 http://10.2.2.240:12000/ 有请求日志,但返回 403:
1"/data/www/index.html" is forbidden (13: Permission denied), 2client: 10.2.2.240, server: localhost, request: "GET / HTTP/1.1", 3host: "10.2.2.240:12000" 4
同时另一条日志:
1connect() to 127.0.0.1:180 failed (13: Permission denied) while connecting to upstream 2
排查过程
1. 先排除认证问题
配置里有:
1satisfy any; 2allow 192.168.34.0/24; 3deny all; 4auth_basic "Restricted Access"; 5auth_basic_user_file /etc/nginx/.htpasswd; 6
satisfy any 表示 IP 限制 和 Basic 认证 满足任意一个即可通过。
用 curl 分别测试:
1# 密码错误 → 401 2curl -v -u admin:admin123 http://10.2.2.240:12000/ 3# HTTP/1.1 401 Unauthorized 4 5# 密码正确 → 403 6curl -v -u admin:admin@123 http://10.2.2.240:12000/ 7# HTTP/1.1 403 Forbidden 8
从 401 变成 403 说明认证已通过,403 是认证之后的文件访问被拒,与登录凭据无关。错误日志里也明确写了:
1user "admin": password mismatch # 之前密码错 2"/data/www/index.html" is forbidden (13: Permission denied) # 认证通过后读文件被拒 3
2. 排除普通 Linux 权限
1namei -l /data/www 2
输出:
1dr-xr-xr-x root root / 2drwxr-xr-x root root data 3drwxrwxrwx gtdata sftpg www 4
1ls -l /data/www/index.html 2# -rwxrwxrwx. 1 gtdata sftpg 194818 Sep 1 13:57 /data/www/index.html 3
路径每一级都有 x 权限,文件本身也是 777。普通 Linux 权限完全没问题,所以排除。
3. 锁定 SELinux
既然普通权限没问题,而报错又是 13: Permission denied,同时还有对 127.0.0.1:180 的出站连接也被拒,两个现象高度一致地指向 SELinux。
1getenforce 2# Enforcing 3 4ls -Z /data/www/index.html 5# 上下文不是 httpd_sys_content_t 6
/data/www 不在标准 Web 目录下,SELinux 没有给它 httpd_sys_content_t 上下文,Nginx 读文件就被拦。
解决方案
1# 1. 添加永久上下文规则 2semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?" 3 4# 2. 应用规则 5restorecon -Rv /data/www 6 7# 3. 验证 8ls -Z /data/www/index.html 9
执行后再访问,返回 200,页面正常。问题解决。
完整修复清单
针对本次的两个问题,最终执行的命令:
1# 端口:允许 Nginx 绑定 12000 2semanage port -a -t http_port_t -p tcp 12000 3 4# 文件:给 /data/www 打上 httpd_sys_content_t 上下文 5semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?" 6restorecon -Rv /data/www 7 8# 上游:若 Nginx 需要反代本机其他端口,放行出站连接 9setsebool -P httpd_can_network_connect 1 10 11# 重启 12systemctl restart nginx 13
验证:
1curl -v -u admin:admin@123 http://10.2.2.240:12000/ 2# HTTP/1.1 200 OK 3
经验总结
1. 13: Permission denied 在 CentOS 7 上要优先怀疑 SELinux
普通权限用 ls -l、namei -l 一看便知,如果这些都没问题,但日志仍报 Permission denied,基本就是 SELinux。用 getenforce 和 ls -Z 确认。
2. 自定义端口要单独放行
SELinux 不会因为端口 > 1024 就自动放行,必须把端口加入 http_port_t,否则 Nginx 启动阶段就会失败。
3. 非标准目录要打上下文
/data/www 这类自定义路径不在 SELinux 预定义的 Web 目录里,需要 semanage fcontext + restorecon 显式指定 httpd_sys_content_t。
4. Nginx 反代本机端口要开布尔值
httpd_can_network_connect 控制 Nginx 能否主动发起网络连接。反代 127.0.0.1:180 这类上游时,如果没开,会看到:
1connect() to 127.0.0.1:180 failed (13: Permission denied) 2
5. 排查顺序建议
tail -f /var/log/nginx/error.log— 看具体报错namei -l <路径>— 排除普通权限getenforce— 确认 SELinux 状态ls -Z <路径>— 看 SELinux 上下文semanage port -l | grep http_port_t— 看端口是否放行
按这个顺序走,绝大多数 Nginx 的 Permission denied 都能快速定位。
6. 不要动辄关闭 SELinux
setenforce 0 只适合临时验证。生产环境应通过 semanage、restorecon、setsebool 精确放行,既解决问题又不牺牲安全性。
参考
man semanage-fcontextman nginx- CentOS 7 SELinux 官方文档
- Nginx 官方文档:
satisfy、auth_basic、try_files
《CentOS 7 下 Nginx 访问自定义目录报 403 的完整排查:从端口权限到 SELinux》 是转载文章,点击查看原文。