分享一个遇到的 Nginx 命令引发的故障

1 天前
 Yangsh853

提到 nginx -t 命令,绝大多数人的第一反应是 “测试 Nginx 配置文件语法是否正确”—— 毕竟执行后看到 “syntax is ok” 和 “test is successful”,就默认它只是个 “语法检查工具”。

但最近一次线上故障,让我彻底刷新了对这条命令的认知:它居然会悄悄修改服务器目录权限,甚至直接导致业务卡顿崩溃!

一、突发故障:页面频繁卡顿,日志报满 “权限拒绝”

某天突然收到业务反馈:某页面多次加载卡顿、频繁报 “502 Bad Gateway”,用户无法正常操作。

第一时间查看 Nginx 错误日志,发现大量类似报错,核心信息全是 “权限拒绝”:

[crit] 421611#421611: *429 open() "/var/lib/nginx/tmp/proxy/664/02/0000000027" failed (13: Permission denied) while reading upstream, client: 
16:48:57 [crit] 421611#421611: *399 open() "/var/lib/nginx/tmp/proxy/666/02/0000000026" failed (13: Permission denied) while reading upstream, client: , server: _, request: "GET /hp-prod/js/dist/block-editor.min.js?ver=b3b0b55b35e04df52f7c HTTP/1.1", upstream: "http://:9080/hp-prod/js/dist/block-editor.min.js?ver=b3b0b55b35e04df52f7c", host: "8.153.203.10", referrer: "http://hppd-admin/post.php"

日志指向很明确:Nginx 要读取 / 写入代理临时文件(/var/lib/nginx/tmp/proxy/)时,没有权限—— 这些临时文件是 Nginx 转发上游服务(如 WordPress 、后端 API )时,用来缓存大响应的关键文件,没权限操作就会导致请求中断。

二、紧急修复:麻溜恢复业务

既然是权限问题,先定位 Nginx 的运行用户:

ps aux | grep nginx

输出显示,Nginx worker 进程的运行用户是 app-u(这是我们之前为了安全,专门创建的低权限账号)。

再检查临时目录权限:

ls -ld /var/lib/nginx/tmp/proxy/

结果出乎意料:proxy 目录及所有子文件的属主 / 属组,居然变成了 nobody( Linux 默认的匿名用户)——app-u 对这些文件没有读写权限,自然会报错。

紧急执行权限修复命令:

# 递归将临时目录的属主/属组改为 app-u
chown -R app-u:app-u /var/lib/nginx/tmp/
# 确保目录有读写执行权限
chmod -R 755 /var/lib/nginx/tmp/

修复后通知用户重试,页面加载恢复正常,故障暂时解决。

三、溯源:是谁 “偷偷” 改了权限?

业务恢复后,核心问题来了:/var/lib/nginx/tmp/ 原本是 app-u 权限,为什么会变成 nobody

我们翻了故障前 1 小时的服务器操作日志(通过操作记录日志文件),发现一个关键操作:**故障前 2 分钟,有同事用 root 账号执行了 nginx -t -c /path/to/nginx.conf**。

“不就是测试个配置吗?怎么会改权限?” 带着疑问,去看了当时执行的 nginx.conf,发现配置文件里有一行:

user nobody;  # 指定 Nginx 运行用户为 nobody

但这里有个矛盾:我们实际启动 Nginx 时,用的是 app-u 账号,且启动命令里没指定配置文件(默认加载 /etc/nginx/nginx.conf,其中 user app-u;)—— 那同事手动指定的这个配置文件,为什么会影响目录权限?

四、测试验证:nginx -t 居然真的会改权限!

为了验证猜想,我在测试环境复现了整个过程:

  1. 初始状态:用 app-u 启动 Nginx ,/var/lib/nginx/tmp/ 属主是 app-u,权限正常;
  2. 执行命令:切换到 root 账号,执行 nginx -t -c /path/to/test.conf(这个 test.conf 里配置了 user nobody;);
  3. 检查结果:执行后立即查看 tmp 目录权限 ——proxyclient_body 等子目录的属主,果然从 app-u 变成了 nobody

至此真相大白: nginx -t 不仅会检查配置语法,还会根据配置文件中的 user 指令,自动创建 / 修复 Nginx 所需的临时目录(如 tmp/proxytmp/client_body),并将这些目录的属主改为 user 指令指定的用户

之前同事用 root 执行 nginx -t -c 错误配置文件 时,配置里的 user nobody; 触发了 Nginx 的 “目录权限修复” 逻辑 —— 直接把原本 app-u 权限的临时目录,改成了 nobody 权限,最终导致业务故障。

五、后续整改

这次故障完全是 “认知盲区” 导致的,后续我们做了 3 项整改,彻底杜绝同类问题:

1. 规范配置文件:user 指令与运行用户强一致

所有 Nginx 配置文件的 user 指令,必须与实际启动 Nginx 的账号保持一致 —— 比如用 app-u 启动,就统一写 user app-u;,禁止出现 nobodyroot 等不匹配的用户。

同时将核心配置文件(如 /etc/nginx/nginx.conf)设为只读,避免误修改:

chmod 444 /etc/nginx/nginx.conf

2. 平时变更类相关操作禁止使用 root 账号

3. 日志监控告警:提前发现权限问题

通过写脚本监控错误日志中的错误关键字,错误次数,一旦触发阈值就告警

最后:

以前总觉得 nginx -t 是 “安全无害” 的命令,这次故障才意识到:任何看似简单的命令,都可能藏着你不知道的细节

尤其是 Nginx 这类高频使用的工具,建议多翻官方文档(比如 nginx -t 的官方说明里其实提到了 “会验证配置相关的目录权限”),避免因 “想当然” 踩坑

1661 次点击
所在节点    程序员
12 条回复
mimiphp
1 天前
wget -c https://nginx.org/download/nginx-1.27.5.tar.gz
tar zxvf nginx-1.27.5.tar.gz
cd nginx-1.27.5 && ls
make clean
./configure \
--prefix=/usr/local/nginx \
--user=www \
--group=www \
--sbin-path=/usr/local/nginx/sbin/nginx \
--conf-path=/usr/local/nginx/nginx.conf \
--error-log-path=/usr/local/nginx/error.log \
--http-log-path=/usr/local/nginx/access.log \
--pid-path=/usr/local/nginx/nginx.pid \
--lock-path=/usr/local/nginx/nginx.lock \
--http-client-body-temp-path=/usr/local/nginx/client_temp \
--http-proxy-temp-path=/usr/local/nginx/proxy_temp \
--http-fastcgi-temp-path=/usr/local/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/usr/local/nginx/uwsgi_temp \
--http-scgi-temp-path=/usr/local/nginx/scgi_temp \
--with-file-aio \
--with-threads \
--with-http_addition_module \
--with-http_auth_request_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_mp4_module \
--with-http_random_index_module \
--with-http_realip_module \
--with-http_secure_link_module \
--with-http_slice_module \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_v2_module \
--with-mail \
--with-mail_ssl_module \
--with-stream \
--with-stream_realip_module \
--with-stream_ssl_module \
--with-stream_ssl_preread_module

make -j $(nproc)
make install
cat <<EOF > /etc/systemd/system/nginx.service
[Unit]
Description=nginx
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s quit
PrivateTmp=false

[Install]
WantedBy=multi-user.target
EOF

mkdir -p /usr/local/nginx/rewrite
mkdir -p /usr/local/nginx/ssl
mkdir -p /usr/local/nginx/ssl/default
mkdir -p /usr/local/nginx/vhost


这是我编写的编译安装 nginx 的代码片段。。。。我恰恰相反。web 服务器核心组件安装,比如就是 nginx 这个软件安装,必须用 root 权限来安装。执行权限交给 www ,并且是 nologin 账户。。。。普通用户就不应该可以操作修改重启核心组件。

这里我特别强调就是多用户维护服务器,最好是开放 SSH ,或者 nginx 配置 webdav ,不要再用 FTP 这种明文传输的协议。
yuedingwangji
1 天前
既然你专门配置了 app-u 的账号运行 ng 了, 你都不改 ng 配置里面的账号么
gearfox
1 天前
学到了
Yangsh853
1 天前
@yuedingwangji 这个服务很早就在跑了,平时也确实没有注意到这个配置,后面全系统的 ng 都已经检查过了
shinonome
1 天前
学习了
i4t
1 天前
这是很基础的故障了
bai4246464
1 天前
这个之前确实没注意,但是你既然用 app-u 用户启动了,就应该用 app-u 这个用户去 nginx -t
guanzhangzhang
1 天前
可以看看 docker nginx-unprivileged 的容器做了哪些配置和设置参考下,以及制作 rpm 安装包的。
都是非 root 用户加家目录,temp_path 要么单独家目录内,要么/tmp 目录下
ivyliner
22 小时 58 分钟前
赞, 学习了
githmb
21 小时 2 分钟前
你-t 了别的配置,又运行默认的配置,怪谁?
nskjbtm123
7 小时 12 分钟前
这种故障真是可遇不可求,配置都不改成那个权限用户,那之前可能是怎么绕过那个配置文件的用户,使用低权限用户运行成功的?还是说之前不是用这个配置文件启动的
Wyearn
34 分钟前
学习到了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1156428

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX