php-fpm в chroot и некоторые трудности

Относительно недавно я отказался от php-fcgi и apache и перевел все свои сервера под связку nginx + php-fpm, как-нибудь я напишу отдельно об этом. Сейчас же я хочу рассказать немного о php-fpm в chroot и некоторых проблемах, возникающих при помещении php-fpm в chroot.

php-fpm в chroot – для чего это нужно?

Для начала хотелось бы пояснить, для чего я это сделал. Дело в том, что всем известно, что любая программа содержит ошибки, а php, как язык с очень большими допущениями и низким порогом вхождения, прямо-таки провоцирует программистов и тех, кто себя ошибочно таковыми считает, допускать серьезные ошибки при написании скриптов, что ведет к брешам в безопасности. К тому же мало кто пишет сам для себя, за частую используются коммерческие или бесплатные cms.

Любая более-менее крупная cms имеет массы возможностей, часто написанных разными людьми. И, соответственно, чем больше возможностей, тем больше потенциальных мест для уязвимостей. Теперь представим, что у вас на сервере размещено несколько проектов (а такое как правило и бывает, ибо делать отдельный сервер или пусть даже виртуальную машину для каждого сайта слишком расточительно). Или, быть может, вы даже решили предоставлять услугу shared hosting’а.

Теперь представим, что один из проектов был скомпрометирован нехорошими людьми. Потенциально эти люди могут получить доступ к каждому из проектов, находящихся по соседству со взломанным сайтом. Даже если вы все, казалось бы, предусмотрели, проставили нужные права, включили кривой safe mode или черт-знает-что еще сделали для защиты, у опытных хакеров может оказаться туз в рукаве в виде только что открытой дырки в по, позволяющей обойти ваши защиты. Что же делать? На помощь приходит php-fpm с полезной опцией помещения исполняемых процессов в chroot.

Какие возникают трудности при помещении php-fpm в chroot?

Проблема номер один

Самая первая проблема, которая у меня возникла – это пропал коннект к mysql. Расследование показало, что невозможно отрезолвить доменные имена (dns не работал). Замена localhost на 127.0.0.1 проблему решила, но не полностью. Немного погуглив, я нашел решение, оно оказалось простым (элегантность, конечно, под вопросом). В папку /etc (отсчет от корня chroot) мы кладем файлы nsswitch.conf и resolv.conf. В папку lib (lib64 для 64-битных систем) кладем libnss_dns-2.X.so и делаем на него симлинк libnss_dns.so.2. Всё, после этого не забудьте сделать жесткий рестарт (restart) для php-fpm, т.к. мягкий рестарт (reload) не подгружает библиотеки.

Проблема номер два

Проблема номер два: перестали работать сессии. Решил я это очень просто: ведь нужен еще и временный каталог, а он остался за пределами chroot. Создаем /tmp (если у вас в php.ini другой путь для сессий, то либо создайте и его, либо исправьте на /tmp). Не забудьте назначить владельца и группу root для /tmp и сделать 777 + sticky bit. Для тех, кто не в курсе, sticky bit запрещает удаление чужих файлов в таком каталоге. Для чего это нужно, если у нас chroot? А для того, чтобы пользователь не мог удалить темп, мы создаем пустой файлик (н-р, .no_delete) с правами рута. Теперь юзер не может удалить .no_delete, а значит не может удалить и tmp – всё просто

Проблема номер три

Проблема номер три: перестали работать некоторые самописные скрипты. Тут всё просто, в скриптах были прописаны абсолютные пути – меняем на относительные или на новые абсолютные с учетом chroot.

Проблема номер четыре

Проблема номер четыре: сломался mod_python для php. Поскольку я сомневаюсь, что его использует сколько-нибудь значимое число людей, я не буду вдаваться в подробности. Скажу лишь, что решение было некрасивым, но оно работает.

Проблема номер пять

Проблема номер пять: перестала работать функция mail(). Дело в том, что sendmail (почтовик по умолчанию) оказался за пределами chroot. Копировать его, как libnss категорически неприемлемо, т.к. это потянет за собой кучу библиотек от libC. Такое решение неверно в корне. Однако, заокеанские друзья подсказали решение. Нужно поставить статически линкованный mini_sendmail. Но, чтобы им воспользоваться надо внести небольшое изменение в код mini_sendmail.

Открываем mini_sendmail.c и ищем:

username = getlogin();
заменяем на:
username = "mini_sendmail";

Причем имя юзера может быть любым, даже несуществующим: это простая формальность, которая не влияет на работу mini_sendmail.

Но это еще не все, для запуска sendmail придется поместить какой-нибудь шелл в chroot. Я последовал примеру из оригинала и статически скомпилировал busybox. Перед компиляцией я сделал make menuconfig и снял все галочки, кроме статической компиляции и ash. В бизибоксе есть еще sendmail, но почему-то он у меня не заработал. Времени разбираться особо не было, но если у кого-то получилось обойтись без mini_sendmail, то пишите свой рецепт в комментах. Скомпилировав busybox, я поместил его сюда /bin/sh в чруте. На всякий случай делаем restart для php-fpm. И, вуаля, mail() работает

Вот и все проблемы. Вкратце опишу, почему еще php-fpm, а не что-то иное. Прежде всего мы избавляемся от прожорливого и избыточного apache. Во-вторых, мы получаем фактически php-backend, поскольку php-fpm не вызывается динамически, а висит демоном постоянно. php-fpm контролирует выделение памяти и позволяет потенциально сократить расходы. Также это решение несколько ускоряет работу сервера за счет исключения затрат на запуск и выгрузку процессов php, если раньше вы использовали php-cgi/fcgi. По сравнению с php-cgi/fcgi php-fpm обеспечивает корректное завершение процессов по таймауту, и я стал гораздо реже получать 502-ошибку в логах.

Оригинал: http://linuxa.ru/blog/php-fpm-v-chroot-i-nekotorye-trudnosti