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

54 天前
 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 的官方说明里其实提到了 “会验证配置相关的目录权限”),避免因 “想当然” 踩坑

3011 次点击
所在节点    程序员
16 条回复
mimiphp
54 天前
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
54 天前
既然你专门配置了 app-u 的账号运行 ng 了, 你都不改 ng 配置里面的账号么
gearfox
54 天前
学到了
Yangsh853
54 天前
@yuedingwangji 这个服务很早就在跑了,平时也确实没有注意到这个配置,后面全系统的 ng 都已经检查过了
shinonome
54 天前
学习了
i4t
54 天前
这是很基础的故障了
bai4246464
54 天前
这个之前确实没注意,但是你既然用 app-u 用户启动了,就应该用 app-u 这个用户去 nginx -t
guanzhangzhang
54 天前
可以看看 docker nginx-unprivileged 的容器做了哪些配置和设置参考下,以及制作 rpm 安装包的。
都是非 root 用户加家目录,temp_path 要么单独家目录内,要么/tmp 目录下
ivyliner
54 天前
赞, 学习了
githmb
54 天前
你-t 了别的配置,又运行默认的配置,怪谁?
nskjbtm123
53 天前
这种故障真是可遇不可求,配置都不改成那个权限用户,那之前可能是怎么绕过那个配置文件的用户,使用低权限用户运行成功的?还是说之前不是用这个配置文件启动的
Wyearn
53 天前
学习到了
victorc
53 天前
没事不要去人为改变 nginx 的默认用户,会产生一系列维护成本,这属于没苦硬吃
guo4224
53 天前
很难见到像 nginx 这么省心的东西……
ioioj5
52 天前
还真是没遇到过
ruanimal
52 天前
典型自作自受

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

https://ex.noerr.eu.org/t/1156428

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

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

© 2021 V2EX