Tumbleweed 20201229 nginx 和 php7-fpm 的 Access Denied 问题

需要搭建一个 PHP 服务器,于是用了 openSUSE+nginx+php-fpm+postgresql

但是遇到了问题,启动不了,提示访问权限错误。请老鸟帮忙看一看

所有的软件包都是官方源 rpm 安装的,不是我自己拿源代码编译的,如果大神想要复现问题,可以直接安装系统的 rpm。实际上我一直认为系统 rpm 安装比手动源代码安装好的多。

openSUSE Tumbleweed 20201229
nginx 1.19.6
php7 7.4.13

这套配置其实跟 openSUSE 15.2 是一样的

nginx.conf 如下

> #user  nginx;
> worker_processes  1;
> 
> # load_module lib64/nginx/modules/ngx_http_fancyindex_module.so;
> # load_module lib64/nginx/modules/ngx_http_headers_more_filter_module.so;
> # load_module lib64/nginx/modules/ngx_http_image_filter_module.so;
> # load_module lib64/nginx/modules/ngx_http_perl_module.so;
> # load_module lib64/nginx/modules/ngx_http_xslt_filter_module.so;
> # load_module lib64/nginx/modules/ngx_mail_module.so;
> # load_module lib64/nginx/modules/ngx_rtmp_module.so;
> # load_module lib64/nginx/modules/ngx_stream_module.so;
> 
> #error_log  /var/log/nginx/error.log;
> #error_log  /var/log/nginx/error.log  notice;
> #error_log  /var/log/nginx/error.log  info;
> 
> #pid        /run/nginx.pid;
> 
> 
> events {
>     worker_connections  1024;
>     use epoll;
> }
> 
> 
> http {
>     include       mime.types;
>     default_type  application/octet-stream;
> 
>     #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
>     #                  '$status $body_bytes_sent "$http_referer" '
>     #                  '"$http_user_agent" "$http_x_forwarded_for"';
> 
>     #access_log  /var/log/nginx/access.log  main;
> 
>     sendfile        on;
>     #tcp_nopush     on;
> 
>     #keepalive_timeout  0;
>     keepalive_timeout  65;
> 
>     #gzip  on;
> 
>     include conf.d/*.conf;
> 
> #    server {
> #        listen       80;
> #        server_name  localhost;
> 
>         #charset koi8-r;
> 
>         #access_log  /var/log/nginx/host.access.log  main;
> 
> #        location / {
> #            root   /srv/www/htdocs/;
> #            index  index.html index.htm;
> #        }
> 
>         #error_page  404              /404.html;
> 
>         # redirect server error pages to the static page /50x.html
>         #
> #        error_page   500 502 503 504  /50x.html;
> #        location = /50x.html {
> #            root   /srv/www/htdocs/;
> #        }
> 
>         # proxy the PHP scripts to Apache listening on 127.0.0.1:80
>         #
>         #location ~ \.php$ {
>         #    proxy_pass   http://127.0.0.1;
>         #}
> 
>         # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
>         #
>         #location ~ \.php$ {
>         #    root           /srv/www/htdocs/;
>         #    fastcgi_pass   127.0.0.1:9000;
>         #    fastcgi_index  index.php;
>         #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
>         #    include        fastcgi_params;
>         #}
> 
>         # deny access to .htaccess files, if Apache's document root
>         # concurs with nginx's one
>         #
>         #location ~ /\.ht {
>         #    deny  all;
>         #}
> #    }
> 
> 
>     # another virtual host using mix of IP-, name-, and port-based configuration
>     #
>     #server {
>     #    listen       8000;
>     #    listen       somename:8080;
>     #    server_name  somename  alias  another.alias;
> 
>     #    location / {
>     #        root   /srv/www/htdocs/;
>     #        index  index.html index.htm;
>     #    }
>     #}
> 
> 
>     # HTTPS server
>     #
>     #server {
>     #    listen       443 ssl;
>     #    server_name  localhost;
> 
>     #    ssl_certificate      cert.pem;
>     #    ssl_certificate_key  cert.key;
> 
>     #    Allow TLS version 1.2 only, which is a recommended default these days
>     #    by international information security standards.
>     #    ssl_protocols        TLSv1.2;
> 
>     #    ssl_session_cache    shared:SSL:1m;
>     #    ssl_session_timeout  5m;
> 
>     #    ssl_ciphers  HIGH:! aNULL:! MD5;
>     #    ssl_prefer_server_ciphers  on;
> 
>     #    location / {
>     #        root   /srv/www/htdocs/;
>     #        index  index.html index.htm;
>     #    }
>     #}
> 
>     include vhosts.d/*.conf;
> 
> }

vhosts.d/example.com.conf 如下

server {
listen 80;
server_name example.com;
root /home/developer/mysite/public; # 前端的内容放在这个目录,记得调整目录权限,让运行 nginx 的用户有权访问,简单粗暴可以用 777
index index.html;
location / {
#root /home/developer/mysite/public/;
index index.html index.htm index.php;
}

#php-fpm configuration
location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;# 如果是 sock 连接则 fastcgi_pass unix:/dev/shm/php-cgi.sock;
    fastcgi_index index.php;
    #fastcgi_param APPLICATION_ENV production;
    include fastcgi.conf;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;# 没有这行就会显示空白页面
}# 和 php-fpm 通信

#css|js|ico|gif|jpg|jpeg|png|txt|html|htm|xml|swf|wav 这些都是静态文件,但应分辨,js、css 可能经常会变,过期时间应小一些,图片、html 基本不变,过期时间可以设长一些  
location ~* ^.+\.(ico|gif|jpg|jpeg|png|html|htm)$ {  
    #root         /home/developer/mysite/static/;  
    access_log   off;  
    expires      30d;  
}
location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {  
    #root         /home/developer/mysite/static/;  
    access_log   off;  
    expires      24h;  
}

}

/etc/php7/fpm/php-fpm.conf 和/etc/php7/fpm/php-fpm.d/www.conf 都是用的系统 rpm 安装的默认值

systemctl start php-fpm
systemctl start nginx

都能正常运行
但是当我用浏览器访问 http://localhost/phpinfo.php 时页面却显示 Access Denied

/var/log/nginx/error.log
的记录显示

2021/01/01 10:07:00 [error] 22887#22887: *1 FastCGI sent in stderr: “Unable to open primary script: /home/developer/mysite/public/phpinfo.php (Permission denied)” while reading response header from upstream, client: 127.0.0.1, server: example.com, request: “GET /phpinfo.php HTTP/1.1”, upstream: “fastcgi://127.0.0.1:9000”, host: “localhost”

但是我把 phpinfo.php 文件的权限设定为 777 也不行

我去查了一下,stackoverflow 上说这个问题大概率是 selinux 引起的,可是只有红帽系的发行版才用 selinux,openSUSE 上根本没有这个 selinux 啊。

请各位老鸟帮我看看到底什么情况,谢谢大家帮忙。

你看 nginx log 没有用,去 php-fpm 找原因,看它的 log。

你注释掉的这行:#user nginx;
声明 nginx 用默认的用户启动:


文档里说明默认用的是 nobody,你可以用 www, root 之类的能访问/home/developer/mysite/public/PHPinfo.php 的用户启动试试, 不太明白 php 的框架,不推荐用 root,只是排除权限问题可以试一下。

我也遇到了类似的问题。
TW 20201229,其中 php-fpm 的 www 池使用 wwwrun 账户运行。

如果我使用如下命令测试:

sudo -u wwwrun cat /srv/www/cgi-bin/test.php

那么 test.php 里的内容可以正常访问(即 test.php 里的内容被正常输出到标准输出),不会出现权限错误。
而 php-fpm 的 www.conf 在这样的配置下:
https://del.dog/yfomoroppi.txt

调用同样的脚本,会在 php-fpm.log 里出现如下的日志:
https://del.dog/cingignith

同时,strace 相应 php-fpm 进程的 PID 后返回的调用失败详细结果如下:
https://del.dog/pegnexomel

相应 Nginx 反向公开网站会返回如下内容(html):

Access denied. 

经查,是 fastcgi 给 Nginx 汇报的错误,不是 Nginx 自己扔出的 403。

相应目录的访问权限已经由下列命令改为 777:

sudo chmod 777 -R /srv/www/cgi-bin

同时使用下列指令更改所在用户和用户组:

sudo chown -R wwwrun /srv/www/cgi-bin
sudo chgrp -R www /srv/www/cgi-bin

奇怪的是,同样的配置环境(指除下文所述的升级 / 回滚系统软件并执行完上面操作后),在 10 月份左右的 Snapper 系统快照是正常的(即 php-fpm 正常拿到文件,不会报 Access denied)。但在更新系统后,100% 复现上述问题。

系统里的 php-fpm 配置:
https://del.dog/locirrelyd.txt

Nginx 配置(nginx.conf):
https://del.dog/attisunime.txt

相应 vhost conf:
https://del.dog/gralylylap.txt

为了跟踪是否是自己魔改 Tumbleweed 出现的问题,从零重装过一次 TW 20201229,使用上述配置后仍出现同样问题。

不知道有没有帮助。

1赞

@pokon548 @myleader

我给个思路,nginx 是用 nginx 用户启动的,php7-fpm 是用 wwwrun 用户启动的。

nginx 用户在 4 月份包更新后默认是没有 group 的:

而 php7-fpm 的东西是 wwwrun 用户 www 组且默认的 listen.mode 是 0660。

所以咯,nginx 是其它组,它的 listen 跑到了 0 也就是没权限里。

可以试试把 listen.mode 设置成 0777 看看我说的对不对。再不好用试试把 nginx 用户加入 www 组。

非常感谢苏姐的回复!

我在 /etc/php7/fpm/php-fpm.d/www.conf 里修改了如下内容:

...

; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a Web server. Many
; BSD-derived systems allow connections regardless of permissions. The owner
; and group can be specified either by name or by their numeric IDs.
; Default Values: user and group are set as the running user
;                 mode is set to 0660
listen.owner = wwwrun
listen.group = www
listen.mode = 0777

...

将 listen.mode 修改为 0777 并重启 php-fpm 后,不好使。

我使用了如下命令将 nginx 加入 www 组:

sudo usermod -aG www nginx

并重启系统以使更改生效。但似乎仍然不好使。访问相应位置 Nginx 依然返回 Access denied

我还应如何调试?

同样思路看看 /srv/www/cgi-bin/test.php 每级文件夹的权限?再不行我就得找找 php7 源代码了

感谢回复!不过我把自 www 向下的文件(夹)-R 递归改成 777(拥有者和组已经在之前就分别改为 wwwrun 和 www 了)后,依然不大行。

唔… 为了简化调试,我做了这么一个最小重现例子:

$ sudo mkdir /testweb

这是我打算在根目录下临时创建一个具有 777 访问权限设定的文件夹。其中放一个 index.php,用以测试权限访问情况。

$ sudo cat '<?php echo "你好,世界!";?>' > /testweb/index.php

创建一个单例调试用网站文件。

# chown -R wwwrun  /testweb
# chgrp -R www /testweb
# chmod -R 777 /testweb

保证权限在(理论上)应该正常。

# VIM /etc/nginx/vhosts.d/test.conf
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name [隐匿];

    root /testweb;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
        index index.php;
    }

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

Nginx 的最小 PHP 配置。
其余设定和之前我回复中给出的一致。但访问后依然报出 Access denied. ,而不是经过 PHP 解析后返回的你好,世界!

试试 root 运行 php7-fpm 和 nginx?就是在命令行下非 daemon 模式跑,两个的用户和组都用 root root?

唔… 结果仍然是 Access denied

我做了如下尝试:

sudo killall nginx
sudo killall php-fpm

将原先的进程杀掉。

# VIM /etc/nginx/nginx.conf
user root

将 Nginx 的运行用户(从配置文件上)改为 root.

# VIM /etc/php7/fpm/php-fpm.d/www.conf
user = root
group = root

...

listen.owner = root
listen.group = root

效果同上。

之后,我执行了如下命令:

# php-fpm -R &
# nginx &

分别以 root 主动启动的方式运行 Nginx & php-fpm。

# top
...

 5510 root      20   0   20288   1100      0 S 0.000 0.054   0:00.00 nginx                                                                                    
 5511 root      20   0   20444   5824   4252 S 0.000 0.288   0:00.02 nginx                                                                                    
 5632 root      20   0   55848  13832   6160 S 0.000 0.685   0:00.00 php-fpm                                                                                  
 5633 root      20   0   55848  14612   6876 S 0.000 0.724   0:00.00 php-fpm                                                                                  
 5634 root      20   0   55848  13656   5980 S 0.000 0.676   0:00.00 php-fpm

...

确认均为 root 进程启动。

回头我装一个试试看。

openSUSE 上没有 SELinux,但是有 apparmor,执行一下sudo aa-status看一下有没有类似的输出

0 profiles are in complain mode.
0 profiles are in kill mode.
0 profiles are in unconfined mode.
4 processes have profiles defined.
4 processes are in enforce mode.
/usr/sbin/dnsmasq (979) dnsmasq
/usr/sbin/php-fpm (3229) php-fpm
/usr/sbin/php-fpm (3230) php-fpm
/usr/sbin/php-fpm (3231) php-fpm
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
0 processes are in mixed mode.
0 processes are in kill mode.

1赞

自带的 php-fpm 规则配置啥都不能访问,把这条规则/home/developer/mysite/public/** r, 加到/etc/apparmor.d/local/php-fpm 里,应该就可以了

1赞

@pokon548 10 月份的快照可以用,找了以下 apparmor 的更新记录,11 月 5 日合并的更新里有一条- new profile for php-fpm,应该就是这个造成的。
https://build.opensuse.org/request/show/845533

非常感谢!在手动给指定目录加入放行规则后,php-fpm 正常执行了对应脚本。确认应该是 apparmor 拦截了相应位置的访问导致 :tada:

看起来这个安全措施挺 rocky 的。后面有时间要学习一下了。

apparmor 居然还有默认 enforce 的规则 :joy: 这家伙不应该默认是 complain 的么,活久见

本主题在最后一个回复创建后60分钟后自动锁定。不再允许添加新回复。