
У нашего заказчика возникло очень стойкое желание разместить на боевом сервере (там конечно же FreeBSD) форум phpBB и Joomla. Всё ничего, если бы на этом самом сервере не размещались важные финансовые данные. Беря во внимание этот факт, и то, что инсталлировать и поддерживать этот форум должен посторонний человек, встала задача организовать chroot окружение. С помощью nginx и php-fpm это можно сделать не затратив больших усилий.
Чтобы все выглядило красиво, создадим папку /www/chroot а в ней папка joomla, phpbb ну и другие. В конфиге nginx.conf нужно создать виртуальный сервер
server {
listen 80;
server_name joomla;
index index.html;
root /www/chroot/joomla/www/;
location ~.php$ {
fastcgi_pass 127.0.0.1:9003;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /joomla/www$fastcgi_script_name;
include fastcgi_params;
}
}
Главная особенность здесь fastcgi_param SCRIPT_FILENAME. Этот параметр указывает не на абсолютный путь (/www/chroot/joomla/www), а на относительный (/joomla/www). Почему так, станет понятно позднее.
В остальном это достаточно стандартный кусок для проксирования запроса на fastcgi серверс php. Директива include fastcgi_params подключает файл с дополнительными директивами, такими как "fastcgi_param QUERY_STRING $query_string;", "fastcgi_param REQUEST_METHOD $request_method;" и т.д.
Для php-fpm создадим новый пул воркеров, который будет слушать запросы на 9003 порту. Основная хитрость состоит в параметре "chroot":
<value name="chroot">/www/chroot/</value>
В результате этот пул запустится в указанном chroot-e. При отсылке запроса, например http://joomla/index.php, nginx на основе конфига установит SCRIPT_FILENAME "/joomla/index.php". Запрос придет к php-fpm и он будет искать файл /joomla/www/index.php относительно корня chroot-а, т.е. /www/chroot/joomla/www/index.php, что нам и требовалось. При этом ни php-шный glob(), ни exec() не даст возможность выйти за пределы указанной директории.
Половина задачи просто, успешно и очень изящно решена. Осталось ограничить доступ для администратора joomla-ы. Рецепт тоже достаточно прост. Подробно он описан здесь. Если коротко, то создается chroot окружение для sftp/scp доступа с помощью маленькой софтины rssh.
Как обычно ставим через порты.
# cd /usr/ports/shells/rssh/
# make install clean
После инсталяции в списке доступных shell будет добавлен rssh. Добавим пользователя joomla с помощью adduser и в качестве shell надо указать rssh. Далее нужно немного поднастроить /usr/local/etc/rssh.conf, а именно снять комментарий со строк
#allowscp
#allowsftp
#umask = 022
и добавить строку
user = "joomla:022:00011:/www/chroot/joomla"
Дальше необходимо организовать chroot окружение для отдельно взятой пользователя и директории. Для этого в папке /www/chroot/joomla выполнить следующие команды :
# mkdir -p usr/bin usr/libexec usr/local/libexec usr/lib lib libexec bin dev etc
# cp /usr/bin/scp usr/bin/
# cp /usr/libexec/sftp-server usr/libexec/
# cp /usr/local/libexec/rssh_chroot_helper usr/local/libexec/
Дальше смотрим какие либы нужны для скопированных команд:
# ldd /usr/bin/scp
/usr/bin/scp:
libssh.so.4 => /usr/lib/libssh.so.4 (0x28081000)
libcrypt.so.4 => /lib/libcrypt.so.4 (0x280ba000)
libcrypto.so.5 => /lib/libcrypto.so.5 (0x280d3000)
libz.so.4 => /lib/libz.so.4 (0x2822c000)
libc.so.7 => /lib/libc.so.7 (0x2823e000)
libgssapi.so.9 => /usr/lib/libgssapi.so.9 (0x2833a000)
libkrb5.so.9 => /usr/lib/libkrb5.so.9 (0x28341000)
libasn1.so.9 => /usr/lib/libasn1.so.9 (0x2837b000)
libcom_err.so.4 => /usr/lib/libcom_err.so.4 (0x283a4000)
libmd.so.4 => /lib/libmd.so.4 (0x283a6000)
libroken.so.9 => /usr/lib/libroken.so.9 (0x283b5000)
Полученные либы копируем к себе: /usr/lib/* в /www/chroot/joomla/usr/lib/; /lib/* в /www/chroot/joomla/lib/.
Скопируем загрузчик:
# cp /libexec/ld-elf.so.1 libexec/
и sh + его зависимые либы (libedit.so.6 и т.д. в соответствующие локальные папки)
# cp /bin/sh bin/
# ldd /bin/sh
/bin/sh:
libedit.so.6 => /lib/libedit.so.6 (0x28097000)
libncurses.so.7 => /lib/libncurses.so.7 (0x280ac000)
libc.so.7 => /lib/libc.so.7 (0x280ef000)
Для sftp-server нужны псевдоустройства из /dev. FreeBSD ползволяет подмаунтить /dev в любое место:
# mount -t devfs devfs /www/chroot/joomla/dev
и добавить строку в /etc/fstab
none /www/chroot/joomla/dev devfs rw 0 0
Последний шаг - скопировать /etc/passwd к себе в локальный /etc/, предварительно удалив все записи, кроме созданной специально для chroot-а.
# cp /etc/passwd /etc/master.passwd etc/
# pwd_mkdb -d /www/chroot/joomla/etc /www/chroot/joomla/etc/master.passwd
Всё готово, можно тестировать. Всё получилось в целом красиво и безопасно. Для доступа использовать:
$ sftp joomla@joomla
Этот проект посвящен интересным и позновательным фактам, новостям, событиям из жизни web-разработчика.
Акцент размещенных здесь статей смещен в сторону решения задач, связанных построения сложных, нетривиальных и просто необычных систем.

Хотел было тоже поковыряться в поисках решения, но это сделали быстрей меня. Оставляю здесь решение, чтобы не потерялось.
Нужно скопировать из корневого /etc в сhroot-овый etc файлы host.conf, hosts, localtime, resolv.conf, nsswitch.conf (по идеи только его должно хватить, но не уверен). Также cp -a /lib/libnss_{dns,files,compat}* /chroot/lib/ и рестарт php-fpm, чтобы glibc подхватил нужные либы