跳至正文

php-fpm unix socket 权限的大坑

  • 2020年9月6日2022年5月18日

许多地方都提到配置 php-fpm 与服务器使用 unix socket 连接可以获得性能优势,自然值得一试。但在配置过程中关于套接字的权限却有一个大坑。

注意:本文基于 openSUSE Leap 15.2,不同操作系统配置文件位置与默认配置可能不同。

问题背景

之前配置服务器的时候使用的是 tcp 的方法来连接 php-fpm,配置 unix socket 的方法在网上有不少相关教程,这里不再赘述。问题的核心是 socket 的权限。

假设我们指定的 socket 是/var/run/php-fpm/php-fpm.sock,在相关配置文件(如 openSUSE 的/etc/php7/fpm/php-fpm.d/www.conf)中配置如下:

listen = /var/run/php-fpm/php-fpm.sock

重启 php-fpm 后检查/var/run/php-fpm/php-fpm.sock 的权限:

ls -l /var/run/php-fpm/php-fpm.sock

我们会发现其拥有者和组为 root,并且配置后的网站是无法正常访问的,apache 错误日志会产生类似这样的输出:

Permission denied: AH02454: FCGI: attempt to connect to Unix domain socket /var/run/php-fpm/php-fpm.sock (localhost) failed

看来似乎是权限配置错误,检查/etc/php7/fpm/php-fpm.d/www.conf 中的相关配置,发现在 listen.ownerlisten.grouplisten.mode 上有这样一行注释:

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

也就是说 unix socket 的默认拥有者和组为 “running user”,而权限为 660 。

鉴于 listen.ownerlisten.grouplisten.mode 默认是被注释掉的,而 usergroup 又分别设置为与 apache 对应的值,unix socket 为什么还是被 root 拥有呢?

尝试

于是我试着取消 listen.ownerlisten.grouplisten.mode 的注释,显式指定它们的值:

listen.owner = wwwrun
listen.group = www
listen.mode = 0660

这么一来果然奏效,不但/var/run/php-fpm/php-fpm.sock 的权限符合预期,网站也能正常访问了。

思考

既然显式指定值可以生效,那么说明默认值似乎并不像注释所述是 “running user”,即 usergroup 值。

难道是 openSUSE 这里写错了?遂去翻阅官方文档,描述却是一样的,默认值为 “running user” 。

这究竟是怎么回事呢?

解决问题

由于逐渐怀疑这是不是一个 bug,于是在 php bug 跟踪系统 中查找了一下,终于有所发现。

根据这个话题的阐述,这实在是注释的锅。这里所谓的 “running user”,并不是指 usergroup 所指定的值,而是 php-fpm 守护进程的用户,由于使用 systemd 管理,那么它通常就是 root 。

usergroup,指定的应该是 php-fpm childs 的用户和组。关于 php-fpm 守护进程和 php-fpm childs 的不同,其实可以直观地在进程中看到:

systemctl status php-fpm
...
CGroup: /system.slice/php-fpm.service
           ├─20873 php-fpm: master process (/etc/php7/fpm/php-fpm.conf)
           ├─20874 php-fpm: pool www
           ├─20875 php-fpm: pool www
           └─20948 php-fpm: pool www
...

这里所谓的 master process 值的就是守护进程,而 pool 就是 php-fpm childs 。

如果不指定 listen.ownerlisten.grouplisten.mode,那么它们就会遵循 “running user” 即守护进程的权限,一般就是 root 了。

吐槽

看到这里,一定会有不少人觉得这种有歧义的注释和文档真是神坑,事实上上面提到的那个话题是在 14 年提出的,而至今六年过去了这种歧义依然存在,以至于去年还有人在上面吐槽。

发表评论

您的电子邮箱地址不会被公开。