PHP
WardPane запускает PHP для WordPress, Laravel, Symfony и любых других PHP-сайтов. У каждого PHP-сайта свой php-fpm и свой пользователь Linux, а работает он в песочнице, из которой не видно ни других сайтов, ни панели. Взломанный плагин на одном сайте не прочитает и не изменит остальные, да и код собственного сайта поменять не сможет.
Включение PHP
Сначала PHP должен появиться на сервере. Установите его командой:
sudo ./install.sh --php
Если запустить install.sh из терминала, он спросит, ставить ли PHP. При обновлениях PHP сохраняется, если он уже установлен. На странице Среды выполнения показаны версии PHP, которые нашла панель (см. Среды выполнения).
Затем выберите PHP обработчиком сайта в окне создания или на вкладке Обработчик. После Сохранить изменения панель за несколько секунд настроит php-fpm сайта и покажет его состояние вверху настроек PHP.
Как это работает
Отдельный php-fpm для каждого сайта
У каждого PHP-сайта свой мастер-процесс php-fpm в отдельном юните systemd, wardpane-php@<id>.service. Общего пула нет, так что процессы, opcache и сессии у сайтов не пересекаются.
- Сайты, созданные в 2.0, запускают PHP от имени своего владельца (по умолчанию
www-root). - Сайты, созданные до 2.0, работают от имени собственного пользователя Linux,
wp_<id>.
Общий php8.3-fpm.service из дистрибутива замаскирован: он работает от имени www-data, а эта группа может иметь право записи во все сайты.
Песочница
php-fpm каждого сайта работает в песочнице systemd:
- других сайтов,
/opt/wardpane,/var/lib/wardpane,/homeи/rootвнутри неё не существует; - вся система доступна только для чтения;
- никаких привилегий и никаких capabilities;
- ограничения на память, процессор и количество процессов.
В режиме по умолчанию, PHP_SANDBOX=strict, PHP не может запустить и ни одну программу (файловая система смонтирована с noexec, кроме самого php-fpm и его библиотек), а системные вызовы фильтруются.
Сам PHP настроен так:
open_basedirограничен папкой сайта (а для новых сайтов ещё и папкойtmp/владельца);exec,system,shell_exec,proc_open,popen,putenvи подобные функции отключены;- файлы
.user.iniне читаются, и заданоexpose_php = Off; sendmail_path = /bin/false, поэтомуmail()не может запустить shell. Вместе с отключённымputenvэто закрывает трюк сLD_PRELOAD.
Панель никогда не получает root
Сама панель работает от имени непривилегированного пользователя. Она записывает нужное состояние в /var/lib/wardpane/php/desired.json, а небольшой root-помощник (bin/site-sync.js, который systemd запускает при каждом изменении этого файла) создаёт пользователей, папки, конфигурации и юниты. Помощник заново проверяет всё, что приходит от панели.
Если настройки сайта не проходят проверки помощника, сайт продолжает работать с прежней конфигурацией, а панель показывает ошибку с пометкой «продолжают действовать прежние настройки».
Код только для чтения и папки с правом записи
PHP может читать код сайта, но не менять его, поэтому взломанный скрипт не перепишет файлы сайта. Писать PHP может только сюда:
- закрытые папки сайта:
tmp/иlogs/(иdata/для сайтов, созданных до 2.0, у которыхtmp/иlogs/тоже лежат рядом сpublic/); - папки, перечисленные в поле Папки с правом записи на вкладке Обработчик, по одной на строку, относительно папки сайта.
Папки с правом записи для популярных приложений:
| Приложение | Папки с правом записи |
|---|---|
| WordPress | wp-content/uploads (на сайте из 1.x: public/wp-content/uploads) |
| Laravel | storage, bootstrap/cache |
Храните базы данных SQLite и другие закрытые файлы вне корня документов. На сайтах, созданных до 2.0, для них есть папка data/ рядом с public/.
Папка с правом записи не может находиться внутри другой папки с правом записи (uploads и uploads/cache): укажите только внешнюю, внутренняя доступна для записи как её часть. Иначе взломанный сайт мог бы заменить внутреннюю папку символической ссылкой, и systemd смонтировал бы в песочницу чужую папку. Настройки с такими вложенными папками, сохранённые более старой версией, очищаются автоматически, без ослабления защиты.
Никакого PHP из папок с правом записи
PHP никогда не выполняется из папки с правом записи: запрос к загруженному uploads/shell.php получит 404.
Чтобы ставить плагины и обновления из своей админки, WordPress должен выполнять код из папок с правом записи. Если вам это нужно, отметьте Выполнять .php из папок с правом записи, но учтите, что тогда выполнится и загруженный злоумышленником скрипт. При выключенной опции ставьте плагины и обновления по SFTP или через WP-CLI из терминала.
Маршрутизация
Маршрутизация работает как try_files $uri $uri/ /index.php в nginx:
- существующие файлы отдаются как статические;
- файлы
.phpвыполняются, с поддержкойPATH_INFO(/index.php/routeработает); - если отмечено Отправлять неизвестные пути в /index.php, всё остальное уходит в
/index.php. Это нужно WordPress, Laravel и Symfony для красивых URL.
Если обработка .htaccess включена и для пути запроса есть правила .htaccess с RewriteEngine On и хотя бы одним RewriteRule, маршрутизацию определяют эти правила, и запасного перехода в /index.php нет, как и в Apache. См. .htaccess.
Папка /.well-known/ (файлы для внешних сервисов, например для подтверждения домена) всегда отдаётся как статические файлы: скрипты в ней не выполняются, и такие запросы никогда не уходят в /index.php.
Исходные файлы PHP (.php, .php5, .inc, .phtml, .phar, .phps) никогда не отдаются как файлы. Это касается и PHP-сайтов, и статических, и сайтов, где PHP уже отключили. То же с их резервными копиями: wp-config.php.bak, config.php~, config.php.2024-01-31, .old, .orig, .save, .swp и похожие окончания, а также автосохранения Emacs #...#. x.php.txt и x.php.html остаются обычными файлами.
Что видят скрипты
REMOTE_ADDRсодержит настоящий IP посетителя, в том числе за Cloudflare.HTTPS=on, когда посетитель пришёл по HTTPS, в том числе через Cloudflare в режиме Flexible, поэтому WordPress не зацикливается.X-Forwarded-For,CF-Connecting-IPи подобные заголовки скриптам не передаются: их можно подделать, а настоящий адрес уже есть вREMOTE_ADDR.- Пароль сайта (HTTP Basic из настроек защиты панели) скриптам не виден.
Bot Fight на PHP-сайтах
На PHP-сайте Bot Fight пропускает формы и API (POST, PUT, PATCH, DELETE) и не считает пути вроде /wp-json/... поиском уязвимостей, потому что их может обслуживать index.php. Сканеры всё равно ловятся по ответам 404, которые возвращает PHP.
Настройки
Настройки PHP находятся на вкладке Обработчик сайта.
| Параметр | По умолчанию | Диапазон | Значение |
|---|---|---|---|
| Версия | Новейшая установленная | установленные версии | Версия PHP для сайта |
| Память на запрос, МБ | 128 | от 32 до 2048 | memory_limit |
| Лимит загрузки, МБ | 32 | от 1 до 2048 | upload_max_filesize и post_max_size |
| Лимит времени, секунд | 30 | от 5 до 600 | max_execution_time |
| Воркеры | 4 | от 1 до 64 | Сколько запросов выполняется одновременно |
| Лимит CPU, % | 100 | от 10 до 800 | 100% означает одно ядро, на весь сайт |
Панель показывает, сколько всего оперативной памяти может использовать сайт при выбранном числе воркеров и памяти на запрос.
Флажки:
- Отправлять неизвестные пути в /index.php - красивые URL в WordPress, Laravel, Symfony.
- Запретить исходящие соединения - сайт не может обращаться к внешним API, отправлять почту по SMTP или что-либо скачивать, а взломанный сайт не может атаковать других.
- Показывать ошибки в браузере - только для отладки, никогда на рабочем сайте.
- Выполнять .php из папок с правом записи - см. выше.
Где лежат файлы
Новые сайты (2.0), владелец www-root, домен example.com:
/var/www/www-root/data/www/example.com папка сайта (для PHP только чтение)
/var/www/www-root/data/tmp сессии и загрузки (запись разрешена)
/var/www/www-root/data/logs/example.com-php-error.log ошибки скриптов сайта
/etc/wardpane/php/<id>/ php.ini и php-fpm.conf (создаются панелью, не редактировать)
Сайты, созданные до 2.0:
/var/www/example.com/public корень документов (для PHP только чтение)
/var/www/example.com/data закрытые файлы, SQLite (запись разрешена)
/var/www/example.com/tmp сессии и загрузки (запись разрешена)
/var/www/example.com/logs/php-error.log ошибки скриптов сайта
/etc/wardpane/php/<id>/ php.ini и php-fpm.conf (создаются панелью, не редактировать)
Журнал ошибок PHP открывается в панели кнопкой Показать журнал ошибок PHP. Когда он дорастает до 10 МБ, от него остаётся последний 1 МБ.
Что нужно знать
- Установленные расширения: mysql, sqlite3, curl, gd, mbstring, xml, zip, intl, bcmath, opcache.
- WardPane 2.0 не устанавливает MySQL и MariaDB. WordPress и другим приложениям, которым нужна такая база, подойдёт внешний сервер баз данных или СУБД, которую вы сами поставили на этот сервер. Базы данных в панели появятся в версии 2.1; см. Базы данных.
mail()всегда сообщает «не отправлено». Отправляйте почту по SMTP с помощью SMTP-плагина или библиотеки. Почта на сервере появится в версии 2.5; см. Почта.- Общего лимита на длительность запроса нет, поэтому медленная загрузка большого файла не обрывается. Запрос обрывается, если его тело перестало поступать или посетитель перестал читать ответ дольше, чем на
max_execution_timeплюс 30 секунд; в тот же момент освобождается его место в очереди php-fpm. - После первых 10 секунд тело запроса должно поступать в среднем со скоростью 500 байт в секунду (иначе 408), а у одного посетителя (адреса, для IPv6 его
/64) может быть не больше 4 запросов к сайту, тело которых ещё передаётся (иначе 429). Без этих ограничений один посетитель мог бы занять медленными загрузками все слоты php-fpm. - Файлы сайта должны быть доступны для чтения всем (
644для файлов,755для папок). После загрузки по SFTP обычно так и есть.
Применить заново и исправить права
Кнопка Применить заново и исправить права на вкладке Обработчик возвращает PHP права на папки с правом записи и перезапускает php-fpm сайта. Используйте её после того, как загрузили файлы в папку с правом записи (например, uploads) от имени root, или когда PHP не может писать в папку, куда писать должен.
Решение проблем
Если php-fpm не запускается в песочнице strict (панель показывает журнал ошибок), переключитесь на базовую песочницу: задайте PHP_SANDBOX=basic в Настройки > PHP (или в /opt/wardpane/.env), перезапустите панель и сообщите о проблеме. Базовая песочница сохраняет всё, кроме запрета на запуск программ и фильтра системных вызовов.
Сообщения самого php-fpm (а не ваших скриптов) попадают в журнал systemd:
journalctl -u wardpane-php@<id> # php-fpm одного сайта
journalctl -t wardpane-php-<id> # то же самое, по тегу
journalctl -u 'wardpane-php@*' # все PHP-сайты
journalctl -u wardpane-sync # root-помощник, применяющий настройки
Чтобы узнать <id> сайта, посмотрите содержимое /etc/wardpane/php/: там по одной папке на каждый PHP-сайт, и в её php-fpm.conf указан домен.