Содержание
Настройка PHP-FPM и Apache в Debian 12
Исходные данные
Задача
- 2 виртуальных хоста с PHP 8.1 и 8.2 с отдельными fpm-pool с отдельными пользователями
- Сайт 1: пользователь batman, директория /srv/www/batman
- Сайт 2: пользователь joker, директория /srv/www/joker
- Ioncube для PHP 8.1
- http2
- SSL Let's Encrypt через модуль mod_md
- realip для Cloudflare
Установка Apache
apt -y install apache2
Добавить в файл /etc/apache2/apache2.conf
ServerName 127.0.0.1
У меня в Debian 12 по-умолчанию был mpm_event. Для других релизов Debian или для Ubuntu может быть потребуется выключить другие MPM и включить event
a2dismod mpm_prefork a2dismod mpm_itk a2enmod mpm_event
SSL/TLS
http2
Проверка через curl http/1.1
# curl -I https://foobar.com HTTP/1.1 200 OK Date: Thu, 10 Aug 2023 14:48:56 GMT Server: Apache/2.4.57 (Debian) Strict-Transport-Security: max-age=15768000 Last-Modified: Sat, 22 Jul 2023 17:39:30 GMT ETag: "c-60116dd1dd190" Accept-Ranges: bytes Content-Length: 12 Content-Type: text/html
Включаем модуль http2
# a2enmod http2
Enabling module http2.
To activate the new configuration, you need to run:
systemctl restart apache2
В VirtualHost добавляем
<VirtualHost *:443> Protocols h2 http/1.1 </VirtualHost>
Проверка через curl http/2
# curl -I https://foobar.com HTTP/2 200 strict-transport-security: max-age=15768000 last-modified: Sat, 22 Jul 2023 17:39:30 GMT etag: "c-60116dd1dd190" accept-ranges: bytes content-length: 12 content-type: text/html date: Thu, 10 Aug 2023 14:49:41 GMT server: Apache/2.4.57 (Debian)
Установка PHP
Следуем инструкции из readme.txt
wget -O sury.sh https://packages.sury.org/php/README.txt chmod +x sury.sh sh sury.sh
Устанавливаем PHP 8.1
apt install -y php8.1-bcmath php8.2-bz2 php8.1-curl php8.1-fpm php8.1-gd php8.1-intl php8.1-mbstring php8.1-mcrypt php8.1-mysql php8.1-opcache php8.1-xml php8.1-xmlrpc php8.1-zip
В конце установки будет предупреждение
NOTICE: Not enabling PHP 8.1 FPM by default. NOTICE: To enable PHP 8.1 FPM in Apache2 do: NOTICE: a2enmod proxy_fcgi setenvif NOTICE: a2enconf php8.1-fpm NOTICE: You are seeing this message because you have apache2 package installed.
Устанавливаем PHP 8.2
apt install -y php8.2-bcmath php8.2-bz2 php8.2-curl php8.2-fpm php8.2-gd php8.2-intl php8.2-mbstring php8.2-mcrypt php8.2-mysql php8.2-opcache php8.2-xml php8.2-xmlrpc php8.2-zip
В конце установки будет предупреждение
NOTICE: Not enabling PHP 8.2 FPM by default. NOTICE: To enable PHP 8.2 FPM in Apache2 do: NOTICE: a2enmod proxy_fcgi setenvif NOTICE: a2enconf php8.2-fpm NOTICE: You are seeing this message because you have apache2 package installed.
PHP-FPM
Проверяем статус php-fpm
systemctl status php8.1-fpm systemctl status php8.2-fpm
Для работы php-fpm в Apache нужны 2 модуля
Включаем модуль proxy_fcgi и setenvif
# a2enmod proxy_fcgi setenvif Considering dependency proxy for proxy_fcgi: Enabling module proxy. Enabling module proxy_fcgi. Module setenvif already enabled To activate the new configuration, you need to run: systemctl restart apache2
Для обработки PHP кода добавляем в VirtualHost
Для 8.1
<VirtualHost> # <FilesMatch ".+\.ph(ar|p|tml)$"> # расширенный FilesMatch для .phar, phtml <FilesMatch \.php$> SetHandler "proxy:unix:/var/run/php/php8.1-fpm.sock|fcgi://localhost/" </FilesMatch> </VirtualHost>
Для 8.2
<VirtualHost> <FilesMatch \.php$> SetHandler "proxy:unix:/var/run/php/php8.2-fpm.sock|fcgi://localhost/" </FilesMatch> </VirtualHost>
Создаём пользователей и директории для PHP 8.1 и 8.2
useradd batman passwd batman useradd joker passwd joker mkdir -p /srv/www/batman mkdir -p /srv/www/joker chown batman:batman /srv/www/batman chown joker:joker /srv/www/joker
Для дополнительной безопасности при создании пользователя можно добавить -s /bin/false
или /usr/sbin/nologin
в качестве шелла. Но из моего опыта 99% людей этого не делает т.к. надо или разворачивать git или придумывать ещё какие-то велосипеды, чтобы загружать/редактировать файлы сайта. Так что каждый решает сам, как лучше. Можно настроить авторизацию по ключам и уже будет лучше. Есть ещё вариант с ACL (setfacl). Но это всё частные случаи. Деплой только через git. К сожалению я и сейчас часто наблюдаю внесение правок на корпоративные сайты Битрикс через FTP. ССЗБ :)
Создаём файлы для вывода phpinfo();
echo '<?php phpinfo(); ?>' > /srv/www/batman/info.php echo '<?php phpinfo(); ?>' > /srv/www/joker/info.php
Создаём виртуальные хосты с именами batman.conf
и joker.conf
в /etc/apache2/sites-available/
Файл /etc/apache2/sites-available/batman.conf
<VirtualHost *:80> Protocols h2 http/1.1 DocumentRoot /srv/www/batman ServerAdmin admin@foobar.com ServerName batman.foobar.com ServerAlias bruce.foobar.com CustomLog "/var/log/apache2/batman_access.log" combined ErrorLog "/var/log/apache2/batman_error_log" # php-fpm handler <FilesMatch ".+\.ph(ar|p|tml)$"> SetHandler "proxy:unix:/var/run/php/php8.1-fpm.sock|fcgi://localhost" </FilesMatch> </VirtualHost> <Directory /srv/www/batman> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory>
Файл /etc/apache2/sites-available/joker.conf
<VirtualHost *:80> Protocols h2 http/1.1 DocumentRoot /srv/www/joker ServerAdmin admin@foobar.com ServerName joker.foobar.com ServerAlias harley.foobar.com CustomLog "/var/log/apache2/joker_access.log" combined ErrorLog "/var/log/apache2/joker_error_log" # php-fpm handler <FilesMatch ".+\.ph(ar|p|tml)$"> SetHandler "proxy:unix:/var/run/php/php8.2-fpm.sock|fcgi://localhost" </FilesMatch> </VirtualHost> <Directory /srv/www/joker> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory>
Включаем сайты через a2ensite
a2ensite batman joker systemctl restart apache2
FPM-POOL
Файл /etc/php/8.1/fpm/pool.d/batman-pool.conf
[batman-pool] listen = /var/run/php/php8.1-fpm.sock listen.owner = batman listen.group = batman listen.mode = 0660 user = batman group = batman pm = dynamic
Файл /etc/php/8.2/fpm/pool.d/joker-pool.conf
[joker-pool] listen = /var/run/php/php8.2-fpm.sock listen.owner = joker listen.group = joker listen.mode = 0660 user = joker group = joker pm = dynamic
Это минимальный конфиг для работы.
Добавляем пользователя www-data
в группу batman
и joker
.
usermod -a -G batman www-data usermod -a -G joker www-data
Ещё раз про chown
❌ Неправильно:
- www-data:www-data
- batman:www-data
- www-data:batman
✅ Правильно:
- batman:batman
- joker:joker
Если не добавить в группу будет нечто подобное
[proxy:error] [pid 50599:tid 140087278896832] (13)Permission denied: AH02454: FCGI: attempt to connect to Unix domain socket /var/run/php/php8.2-fpm.sock (*:80) failed [proxy_fcgi:error] [pid 50599:tid 140087278896832] [client 192.168.100.100:17782] AH01079: failed to make connection to backend: httpd-UDS
На что обратить внимание
- Каждый пул должен использовать отдельный сокет. Если несколько пулов используют один и тот же сокет будут проблемы.
- Директивы
user
иgroup
задают пользователя/группу, от имени которых будет запускаться процесс FPM. Они не связаны с пользователем/группой для сокета. - Директивы
listen.owner
иlisten.group
задают пользователя/группу, которую сокет использует для этого пула. - Директивы пула
listen.*
работают только для пулов. Их нельзя использовать в глобальном конфиге, вы должны указать их для каждого пула. - Права к сокету 0660
Для примера и в качестве заметки для себя приложу свой 💥 боевой конфиг
phpinfo
Проверяем вывод phpinfo по ссылкам batman.foobar.com/info.php и joker.foobar.com/info.php
php 8.1
php 8.2
Проверка FPM-POOL
Создаём файл uid-check.php
echo "<?php echo exec('id'); ?>" > /srv/www/batman/uid-check.php echo "<?php echo exec('id'); ?>" > /srv/www/joker/uid-check.php
В браузере uid-check.php
должен показать следующее
uid=1000(batman) gid=1000(batman) groups=1000(batman) uid=1001(joker) gid=1001(joker) groups=1001(joker)
FPM status
Настраиваем страницу состояния FPM.
В конфиг пула добавляем
pm.status_path = /status ping.path = /ping
В конфиг виртуального хоста добавляем
<LocationMatch "/(ping|status)"> SetHandler "proxy:unix:/var/run/php/php8.1-fpm.sock|fcgi://localhost" </LocationMatch> <IfModule alias_module> Alias /realtime-status "/usr/share/php/8.1/fpm/status.html" </IfModule>
Формат данных - html, json, openmetrics (какая-то новинка), xml
Также стоит добавить в конфиг Require local или Require ip 192.168.100.0/24, чтобы ограничить доступ к данным.
HTML
JSON
XML
Realtime
URL - https://foobar.com/realtime-status (файл /usr/share/php/ВЕРСИЯ_PHP/fpm/status.html
)
GIF 400+ КБ
На странице fpm_get_status увидел ссылку на PHP файл, который якобы будет работать без дополнительной настройки веб сервера - PHP-FPM real-time status page (Single file without the need for web server configuration).
Cloudflare realip/remoteip
Если для домена подключен Cloudflare, то необходима настройка для отображения реальных IP адресов с помощью mod_remoteip - Apache Module mod_remoteip. Пример настройки есть в статье Cloudflare: SSL сертификаты Full Strict
Ioncube
Основная статья Установка ionCube Loader, но она немного устарела.
Смотрим extension_dir для версии PHP 8.1
# php8.1 -i | grep extension_dir extension_dir => /usr/lib/php/20210902 => /usr/lib/php/20210902
Копируем .so файл по указанному выше пути
cd ioncube && cp ./ioncube_loader_lin_8.1.so /usr/lib/php/20210902/ioncube_loader_lin_8.1.so
Подключаем .so модуль к PHP
# echo "; priority=10\nzend_extension=/usr/lib/php/20210902/ioncube_loader_lin_8.1.so" >> /etc/php/8.1/mods-available/ioncube.ini
Теперь нужно активировать модуль через phpenmod. Т.к. установлено 2 версии PHP (Managing Multiple Versions)
# update-alternatives --query php Name: php Link: /usr/bin/php Slaves: php.1.gz /usr/share/man/man1/php.1.gz Status: auto Best: /usr/bin/php8.2 Value: /usr/bin/php8.2 Alternative: /usr/bin/php8.1 Priority: 81 Slaves: php.1.gz /usr/share/man/man1/php8.1.1.gz Alternative: /usr/bin/php8.2 Priority: 82 Slaves: php.1.gz /usr/share/man/man1/php8.2.1.gz
необходимо указать версию PHP, по-умолчанию phpenmod работает с версией PHP 8.2
phpenmod -v 8.1 ioncube
Если не указать priority=10
, то будет ошибка
NOTICE: PHP message: PHP Fatal error: [ionCube Loader] The Loader must appear as the first entry in the php.ini file in Unknown on line 0
И у ioncube будет приоритет 20
# ls -la /etc/php/8.1/fpm/conf.d/ | grep ioncube lrwxrwxrwx 1 root root 39 Jul 19 12:28 20-ioncube.ini -> /etc/php/8.1/mods-available/ioncube.ini
Проверяем, что модуль подключился
# php8.1 -v PHP 8.1.21 (cli) (built: Jul 16 2023 11:01:21) (NTS) Copyright (c) The PHP Group Zend Engine v4.1.21, Copyright (c) Zend Technologies with the ionCube PHP Loader v12.0.5, Copyright (c) 2002-2022, by ionCube Ltd. with Zend OPcache v8.1.21, Copyright (c), by Zend Technologies
mod_proxy
Вроде бы прокси запросы по-умолчанию запрещены, но лучше себя обезопасить дополнительно
If you want to use apache2 as a forward proxy, uncomment the
# 'ProxyRequests On' line and the <Proxy *> block below.
# WARNING: Be careful to restrict access inside the <Proxy *> block.
# Open proxy servers are dangerous both to your network and to the
# Internet at large.
Добавляем в /etc/apache2/mods-enabled/proxy.conf
<Proxy *> Require all denied Require local Require ip 192.168.100.0/24 </Proxy>
Дополнительные настройки
Включаем mod_rewrite - Apache Module mod_rewrite
a2enmod rewrite
Включаем mod_headers - Apache Module mod_headers
a2enmod headers
Ссылки
EOM
Обсуждение
Я давно искал, как запускать скрипты от имени другого пользователя, чтобы он мог создавать файлы и папки и удалять их. В хостинг-поддержке спрашивал - никто не знает, как это сделать. Насилу нашел эту статью. Спасибо создателям и авторам. Помогли. От Кво мастера примет всем)