故障现象
最近生产环境出现了1次app应用登录异常的故障,最后定位分析发现是因为在root用户下执行过nginx -t命令导致部分文件目录(1、fastcgi、proxy、scgi、uwsgi)所有者权限变为nobody导致。其实在nginx应用的error日志中也发现有大量权限问题的相关报错:[crit] 123744#0: *172221779 open() "/app/nginx20/1/21/0118488121" failed (13: Permission denied),只不过一开始没引起重视,因为查看有权限报错的这些目录下并没有任何文件,加上也咨询过研发认为没有影响,所以导致一开始的排查方向出现了偏差,最终导致此次故障还是经历了好几个小时。
原因分析
那么为什么执行nginx -t命令会导致临时文件所有者权限变为nobody,这才是写这篇文章的目的,下面进行测试。Nginx -t命令官方解释如下:
按照官网对nginx -t命令的解释,该命令只是去检查配置文件语法,并打开配置文件中预定义文件,按照这个字面意思理论上该命令不会对文件目录权限进行修改,但是在实际测试过程中发现使用普通用户如app运行nginx服务,并且nginx配置文件nginx.conf中没有配置user xxx参数内容时,如果使用root用户执行nginx -t命令,则会导致临时文件(fastcgi、scgi、uwsgi、client_body、proxy)目录权限所有者被更改为nobody,这使得启动 nginx 服务的用户失去了对这些目录的访问权限,从而引发应用程序的部分页面无法访问的问题。触发此问题的具体条件如下::
1)nginx服务进程使用非root和nobody用户启动;
2)nginx配置文件nginx.conf中缺少启动用户的定义:即配置文件中没有包含user app;这样的行来指定nginx服务启动时所使用的用户
3)使用root用户执行了nginx -t命令:这可能会在上述两个条件下更改临时目录的所有者为 nobody
测试验证
为了验证上述结论是否成立,我们进行以下测试验证。在/opt目录下安装了一个nginx应用,授权所有文件归属user及group为app用户。
[root@localhost opt]# chown -R app.app /opt/nginx20/
[root@localhost opt]# ll /opt/nginx20/
total 36K
drwx------ 2 app app 4.0K Aug 27 20:50 client_body_temp
drwxr-xr-x 2 app app 4.0K Aug 27 22:29 conf
drwx------ 2 app app 4.0K Aug 27 20:50 fastcgi_temp
drwxr-xr-x 2 app app 4.0K Aug 27 20:49 html
drwxr-xr-x 2 app app 4.0K Aug 27 22:34 logs
drwx------ 2 app app 4.0K Aug 27 20:50 proxy_temp
drwxr-xr-x 2 app app 4.0K Aug 27 20:49 sbin
drwx------ 2 app app 4.0K Aug 27 20:50 scgi_temp
drwx------ 2 app app 4.0K Aug 27 20:50 uwsgi_temp
然后我们切换到app用户来启动nginx(此时nginx.conf配置文件中是未配置 user app;内容的)
[root@localhost nginx20]# su - app
[app@localhost ~]$ /opt/nginx20/sbin/nginx -c /opt/nginx20/conf/nginx.conf
[app@localhost ~]$ ps -ef | grep nginx
app 1712 1 0 21:46 ? 00:00:00 nginx: master process /opt/nginx20/sbin/nginx -c /opt/nginx20/conf/nginx.conf
app 1713 1712 0 21:46 ? 00:00:00 nginx: worker process
app 1715 1660 0 21:47 pts/0 00:00:00 grep --color=auto nginx
接下来再次切换到root用户执行nginx -t命令
[root@localhost nginx20]# ll
total 36K
drwx------ 2 app app 4.0K Aug 27 20:50 client_body_temp
drwxr-xr-x 2 app app 4.0K Sep 29 21:46 conf
drwx------ 2 app app 4.0K Aug 27 20:50 fastcgi_temp
drwxr-xr-x 2 app app 4.0K Aug 27 20:49 html
drwxr-xr-x 2 app app 4.0K Sep 29 21:46 logs
drwx------ 2 app app 4.0K Aug 27 20:50 proxy_temp
drwxr-xr-x 2 app app 4.0K Aug 27 20:49 sbin
drwx------ 2 app app 4.0K Aug 27 20:50 scgi_temp
drwx------ 2 app app 4.0K Aug 27 20:50 uwsgi_temp
#上面是执行nginx -t命令之前的文件权限
[root@localhost nginx20]# /opt/nginx20/sbin/nginx -t
nginx: the configuration file /opt/nginx20/conf/nginx.conf syntax is ok
nginx: configuration file /opt/nginx20/conf/nginx.conf test is successful
# 下面是执行nginx -t命令之后的文件权限
[root@localhost nginx20]# ll
total 36K
drwx------ 2 nobody app 4.0K Aug 27 20:50 client_body_temp
drwxr-xr-x 2 app app 4.0K Sep 29 21:46 conf
drwx------ 2 nobody app 4.0K Aug 27 20:50 fastcgi_temp
drwxr-xr-x 2 app app 4.0K Aug 27 20:49 html
drwxr-xr-x 2 app app 4.0K Sep 29 21:46 logs
drwx------ 2 nobody app 4.0K Aug 27 20:50 proxy_temp
drwxr-xr-x 2 app app 4.0K Aug 27 20:49 sbin
drwx------ 2 nobody app 4.0K Aug 27 20:50 scgi_temp
drwx------ 2 nobody app 4.0K Aug 27 20:50 uwsgi_temp
通过上述测试证实了前面说的3点:
1)nginx服务进程使用非root和nobody用户启动;
2)nginx配置文件nginx.conf中缺少启动用户的定义:即配置文件中没有包含user app;
3)使用root用户执行了nginx -t命令:
这种情况下会更改某些目录的所有者权限为nobody,上面的client_body_temp、fastcgi_temp、proxy_temp、scgi_temp、uwsgi_temp目录文件所有者权限均变为了nobody。
接下来再来验证下在配置文件中配置启动用户的情况,在nginx配置文件中配置user app;内容(指定nginx启动用户为app)
[root@localhost nginx20]# vim /opt/nginx20/conf/nginx.conf
[root@localhost nginx20]# head -5 /opt/nginx20/conf/nginx.conf
user app;
worker_processes 1;
......
然后直接通过root用户启动nginx服务,可以发现nginx的worker进程运行用户为app,此时nginx安装路径下的文件权限也为app,前面*temp的几个文件开始所有者权限为nobody,在配置文件配置user app内容后重新启动权限也随之改变了过来,说明 user app配置内容是生效的。
[root@localhost nginx20]# ps -ef | grep nginx
root 1760 1 0 22:04 ? 00:00:00 nginx: master process /opt/nginx20/sbin/nginx -c /opt/nginx20/conf/nginx.conf
app 1761 1760 0 22:04 ? 00:00:00 nginx: worker process
root 1763 1326 0 22:04 pts/0 00:00:00 grep --color=auto nginx
[root@localhost nginx20]# ll
total 36K
drwx------ 2 app app 4.0K Aug 27 20:50 client_body_temp
drwxr-xr-x 2 app app 4.0K Sep 29 22:02 conf
drwx------ 2 app app 4.0K Aug 27 20:50 fastcgi_temp
drwxr-xr-x 2 app app 4.0K Aug 27 20:49 html
drwxr-xr-x 2 app app 4.0K Sep 29 22:04 logs
drwx------ 2 app app 4.0K Aug 27 20:50 proxy_temp
drwxr-xr-x 2 app app 4.0K Aug 27 20:49 sbin
drwx------ 2 app app 4.0K Aug 27 20:50 scgi_temp
drwx------ 2 app app 4.0K Aug 27 20:50 uwsgi_temp
接下来我们在不停nginx服务的情况下通过chown命令直接改变文件的归属权限,然后再次执行nginx -t命令看看
[root@localhost nginx20]# useradd -s /sbin/nologin weihu
[root@localhost nginx20]# chown -R weihu.weihu /opt/nginx20/
[root@localhost nginx20]# ll
total 36K
drwx------ 2 weihu weihu 4.0K Aug 27 20:50 client_body_temp
drwxr-xr-x 2 weihu weihu 4.0K Sep 29 22:02 conf
drwx------ 2 weihu weihu 4.0K Aug 27 20:50 fastcgi_temp
drwxr-xr-x 2 weihu weihu 4.0K Aug 27 20:49 html
drwxr-xr-x 2 weihu weihu 4.0K Sep 29 22:04 logs
drwx------ 2 weihu weihu 4.0K Aug 27 20:50 proxy_temp
drwxr-xr-x 2 weihu weihu 4.0K Aug 27 20:49 sbin
drwx------ 2 weihu weihu 4.0K Aug 27 20:50 scgi_temp
drwx------ 2 weihu weihu 4.0K Aug 27 20:50 uwsgi_temp
#上面直接新建了一个weihu用户然后授权/opt/nginx20/及其下的所有文件归属为weihu用户
#下面直接在root用户下执行nginx -t命令
[root@localhost nginx20]# /opt/nginx20/sbin/nginx -t
nginx: the configuration file /opt/nginx20/conf/nginx.conf syntax is ok
nginx: configuration file /opt/nginx20/conf/nginx.conf test is successful
[root@localhost nginx20]# ll
total 36K
drwx------ 2 app weihu 4.0K Aug 27 20:50 client_body_temp
drwxr-xr-x 2 weihu weihu 4.0K Sep 29 22:02 conf
drwx------ 2 app weihu 4.0K Aug 27 20:50 fastcgi_temp
drwxr-xr-x 2 weihu weihu 4.0K Aug 27 20:49 html
drwxr-xr-x 2 weihu weihu 4.0K Sep 29 22:04 logs
drwx------ 2 app weihu 4.0K Aug 27 20:50 proxy_temp
drwxr-xr-x 2 weihu weihu 4.0K Aug 27 20:49 sbin
drwx------ 2 app weihu 4.0K Aug 27 20:50 scgi_temp
drwx------ 2 app weihu 4.0K Aug 27 20:50 uwsgi_temp
通过上述测试可以发现即使nginx的相关文件所有者权限为其他用户,如果在配置文件中配置了user app;内容,那么在执行nginx -t 命令后,部分文件的所有者权限仍旧会改变为app用户。
注意:这里的部分文件是指nginx运行时产生的临时文件,如上面提到的client_body_temp、fastcgi_temp、proxy_temp、scgi_temp、uwsgi_temp等目录文件,有关这类临时文件用途说明如下:
- client_body_temp:这个目录用于存储客户端请求中的大请求体(如 POST 请求)。当客户端发送的数据量超过一定阈值时,Nginx 会将这部分数据写入磁盘上的临时文件中,以防止占用过多内存。
- fastcgi_temp:这个目录用于存储通过 FastCGI 协议传递的数据。当 Nginx 作为 FastCGI 网关时,它可能会将从后端应用程序接收到的响应数据暂时保存在这个目录中。
- proxy_temp:这个目录用于存储通过代理(Proxy)传递的数据。当 Nginx 作为反向代理服务器时,它可能会将从后端服务器接收到的响应数据暂时保存在这个目录中。
- scgi_temp:这个目录用于存储通过 SCGI 协议传递的数据。SCGI(Simple Common Gateway Interface)是一种类似于 FastCGI 的协议,用于 Web 服务器和应用程序之间的通信。Nginx 可能会将通过 SCGI 接收到的数据暂时保存在这里。
- uwsgi_temp:这个目录用于存储通过 uWSGI 协议传递的数据。uWSGI 是一个通用的应用程序服务器,支持多种协议,包括 uWSGI 协议、FastCGI 协议等。Nginx 可能会将通过 uWSGI 接收到的数据暂时保存在这个目录中。
这些临时目录的主要目的是为了提高性能并避免内存溢出。通过将部分数据存储在磁盘上,Nginx 可以有效地处理大请求和响应数据,而不会消耗过多的内存资源。
配置建议
为了防止这种情况发生,生产环境还是建议在nginx配置文件中明确指定nginx应用启动用户(如user app;),这将即可避免因错误的执行了nginx -t命令而导致临时文件权限异常进而触发业务异常的情况出现。