php-fpm unix socket 权限的大坑

许多地方都提到配置 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 年提出的,而至今六年过去了这种歧义依然存在,以至于去年还有人在上面吐槽。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です