Использование PHP-FPM. Настройка прав доступа.

Со времени моего первого знакомства с TinyCore наряду с "подросшим" багажом знаний в области Linux "подрос" и мой домашний сервер. Обзавелся алюминиевым компактным безвентиляторным корпусом, двухядерным четырехпоточным мобильным процессором core i5 и 8-мью гигабайтами оперативной памяти. Железо стало мощнее, тише и экономичнее. Появился приличный излишек производительности, который я захотел утилизировать с пользой для себя. Так родилась идея своего домашнего мини-хостинга для узкого круга хороших знакомых, также как и я ведущих свои небольшие интернет-блоги. Главным критерием теперь стала безопасность, ведь на мой сервер я собирался пустить посторонних с сомнительными скриптами. Доверительные отношения хороши в жизни, а в компьютерной технике безопасность превыше всего.

Затеяв воплотить в жизнь задуманное, я быстро пришел к выводу что связка APACHE + MOD-PHP5 совершенно не годится для этих целей. Решено было переходить на PHP7-FPM. Сейчас такое решение практикуется повсеместно, и подробных описаний по установке, настройке и запуску более чем достаточно. Я же хочу акцентировать внимание на грамотной настройке именно безопасности WEBSERVER + PHP7-FPM, поскольку статьи в интернете помогут вам лишь запустить сервер чаще всего с пресловутой ошибкой "file not found" исходящей от PHP-сервера либо "access denied" от WEB-сервера. Установка же прав 755 - не панацея от напасти а огромная дыра позволяющая читать и выполнять ваши файлы ЛЮБОМУ ПОСТОРОННЕМУ кто и меет хоть какой-то мало мальский доступ к серверу. Стоит ли дальше говорить об изоляции каждого пользователя в в своем личном "контейнере" не позволяя выходить ничему и никому за его пределы?

Я не стану подробно описывать процесс установки и настройки APACHE + PHP7-FPM, пройдемся лишь бегло по основным пунктам. Но в начале скажу лишь пару слов о преимуществе PHP-FPM перед PHP-MOD. PHP-FPM запускается отдельным демоном который висит в фоне и слушает назначеные в конфигурации порты или сокеты. Он принимает запросы от веб-серверов (apache, nginx), обрабатывает их и выдает обратно. Это отличное решение, ибо вебсервер постоянно работает только со статикой, а динамика обрабатываетя отдельно. В такой ситуации хорошо себя зарекомендовал именно NGINX поэтому связка NGINX + PHP-FPM является наиболее рекомендуемой многими системными администраторами занимающимися вопросами организации и обслуживания веб-серверов. Разделение обработчика скриптов и веб-сервера позволяет легко комбинировать эти две составляющие, к примеру обрабатывать динамические страницы написаные не только на PHP, но и на PYTHON. Вы легко можете переключаться между APACHE и NGINX не подключая никаких дополнительных модулей в самом веб-сервере. Ну и самый главный плюс WEBSERVER + PHP-FPM это то, что теперь выполнение скриптов можно запускать от имени различных пользователей, чего нельзя было сделать без плясок с бубном при монолитном APACHE + MOD-PHP. Как говорится "с каждым днём всё радостнее жить". А коль уж так, тогда приступим:

tce-load -iw apache2.4
tce-load -iw php7-fpm
tce-load -iw php7-ext
tce-load -iw libgd

В папке подключаемых файлов apache-вебсервера apache2/conf.d/ должен появиться файл php7-fpm.conf с таким содержимым:

php7-fpm.conf


 LoadModule proxy_module modules/mod_proxy.so
 LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
 DirectoryIndex index.php index.html
 <FilesMatch \.php$>
  SetHandler "proxy:fcgi://127.0.0.1:9000"
 </FilesMatch>

Так как PHP-сервер у нас будет сидеть на 9000 порту локального хоста, то подключим соответствующие модули и задействуем отправку на этот адрес всех файлов удовлетворяющих условию *.php. Если ранее вы уже использовали сервер apache, то модуль mod_php должен быть отключен.

Перед запуском сервера PHP необходимо переименовать файл /usr/local/etc/php-fpm.conf.default в /usr/local/etc/php-fpm.conf поскольку без конфигурационного файла демон PHP не запустится. Нам подойдут стандартные настройки. Единственное что сделал я - сменил опцию include на папку с конфигами пулов в дирректории /opt/conf/php-fpm.d/. Я с самого начала работы с TCL условился хранить все конфиги именно там. Это удобно и при каждом бекапе все файлы сохраняются автоматически.

Поговорим немного о пулах. Пул - это отдельный процесс PHP сервера запускаемый с правами конкретного пользователя и слушающего указанный порт (сокет). Количество конфигурационных файлов в /opt/conf/php-fpm.d/ равно количеству запущенных пулов. Конфиг каждого файла должен различаться как минимум тремя диррективами:

  • listen = 127.0.0.1:xxxx порт слушаемый данным пулом. По умолчанию 9001
  • [poolname] - именем пула. По умолчанию [www]
  • user = webmaster - учетная запись владельца скриптов от имени которого будет выполняться обработка динамических страниц

Теперь представим классическую ситуацию:

У нас есть несколько папок со скриптами вебсайтов пренадлежащими разным вебмастерам. Назовем их соответственно webmaster_1, webmaster_2 и webmaster_3. Объеденим их одной группой WEBMASTERS. Какждый из них заходит в свою дирректорию по FTP и создает/копирует файлы, владельцами которых являются они сами. Идеальным решением с точки зрения безопасности будет назначение прав 700 (никто кроме владельца не имеет доступа к файлам и папкам). Однако с точки зрения вебсервера это решение не работоспособно. Даже если PHP-процесс запущенный от имени владельца и получит доступ к скриптам на выполнение, вебсервер (apache, nginx) не сможет даже "заглянуть" в дирректорию вебмастера и мы получим классическое "access denied". Запускать вебсервер от имени конкретного пользователя тоже не вариант. Вебсервер один, а вебмастеров много. Решением данного вопроса может стать назначение прав на все файлы типа 755. Собственно так и рекомендуют горе-советчики нерадивым дилетантам не понимающим механизмов работы прав доступа Linux-подобных систем. Действительно, теперь вебсервер, от чьего имени он бы нибыл запущен получит права чтения файлов и каталогов, и всё будет работать. Равно как и абсолютно любой пользователь сможет читать и выполнять файлы всех вебмастеров, что неприемлемо. Так как же ограничить права для всех, читать и выполнять разрешить только вебсерверу, а полный доступ разрешить только конкретному владельцу файлов? Взглянем на диаграмму ниже:


распределение прав доступа

Цветами я выделил те части, которые удовлетворяют условию. Несложно увидеть, что для достижения нашей цели необхдимо в качестве владельца корневой папки каждого вебмастера указать его самого, но группа при этом должна быть WEBSERVERS, а не WEBMASTERS как в класическом варианте "по умолчанию". Теперь попробуем осуществить это на деле. Создаем группу WEBSERVERS, добавляем туда пользователя apache:

addgroup WEBSERVERS
adduser apache -G WEBSERVERS

Аналогичным образом создается группа WEBMASTERS, куда добавляются наши вебмастера. В файле httpd.conf зададим от имени кого будет запускаться apache:

httpd.conf


...
User apache
Group WEBSERVERS
...

Теперь редактируем все наши конфигурационные файлы PHP-пулов, один файл - один вебмастер:

/opt/conf/php-fpm.d/webmaster_1.conf


...
[webmaster_1]
user webmaster_1
group WEBMASTERS
listen = 127.0.0.1:9001
...


/opt/conf/php-fpm.d/webmaster_2.conf


...
[webmaster_2]
user webmaster_2
group WEBMASTERS
listen = 127.0.0.1:9002
...


/opt/conf/php-fpm.d/webmaster_3.conf


...
[webmaster_3]
user webmaster_3
group WEBMASTERS
listen = 127.0.0.1:9003
...


Каждому вебмастеру по PHP-процессу, который будет обрабатывать его динамические страницы от его имени и предавать вебсерверу через соответсвующий порт. Обратите внимание, что пул запускается от имени группы WEBMASTERS, а папки вебмастеров имеют группу-владельца WEBSERVER. Таким образом вебсервер получает доступ [r-x] от имени группы, а вебмастер [rwx] от своего собственного имени. Остальные пользователи, включая и соседей по хостингу не имеют доступа вообще. Осталось создать каждому пользователю нашего мини-хостинга свою "песочницу" где вебмастер будет забавляться со своими сайтами и не мешать другим. Все сайты у меня расположены на жестком диске в папке hosts А посему создав эти папки откроем к ним доступ по FTP и назначим владельцев:

chown webmaster_1:WEBSERVERS -R /mnt/HDD/hosts/webmaster_1
chmod -R 750 /mnt/HDD/hosts/webmaster_1
chown webmaster_2:WEBSERVERS -R /mnt/HDD/hosts/webmaster_2
chmod -R 750 /mnt/HDD/hosts/webmaster_2
chown webmaster_3:WEBSERVERS -R /mnt/HDD/hosts/webmaster_3
chmod -R 750 /mnt/HDD/hosts/webmaster_3

Последний штрих - редактируем конфигурационный файл с папками наших вебмастеров. Разумеется VirtualHosts уже должен быть корректно настроен.

conf.d/userdir.conf


...
<Directory "/mnt/HDD/hosts/webmaster_1">
...
<FilesMatch \.php$>
 SetHandler "proxy:fcgi://127.0.0.1:9001"
</FilesMatch>
...
</Directory>
...

<Directory "/mnt/HDD/hosts/webmaster_2">
...
<FilesMatch \.php$>
 SetHandler "proxy:fcgi://127.0.0.1:9002"
</FilesMatch>
...
</Directory>
...

<Directory "/mnt/HDD/hosts/webmaster_3">
...
<FilesMatch \.php$>
 SetHandler "proxy:fcgi://127.0.0.1:9003"
</FilesMatch>
...
</Directory>
...

Запускаем apache и php-fpm

apachectl -f /opt/conf/apache2.4/httpd.conf -k start
/usr/local/etc/init.d/php-fpm start

Если всё сделано верно, то каждый вебмастер увидит в PHP-переменной $_SERVER['USER'] свой регистрационный логин (webmaster_1, webmaster_2 или webmaster_3)

Теперь ещё немного философии. Ситуацию с правами 755 можно обезопасить своеобразным костылём под названием php_admin_value[open_basedir] в конфиге пула. Это довольно полезная переменная не позволяющая "вылезти" пользователю за пределы дирректорий кроме тех, что указаны в php_admin_value[open_basedir]. Однако применив правило:

/opt/conf/php-fpm.d/webmaster_1.conf


...
php_admin_value[open_basedir] = "/mnt/HDD/hosts/webmaster_1"
...

готовьтесь к тому, что у пользователя webmaster_1 перестанет работать механизм сессий, а загрузка файлов на сервер станет невозможной. Причина кроется в доступе к дирректории /tmp где по умолчанию хранятся сессии а также временные файлы при загрузке их через POST, и нам придется переназначить источники хранения сессий и временных файлов:

/opt/conf/php-fpm.d/webmaster_1.conf


...
php_admin_value[open_basedir] = "/mnt/HDD/hosts/webmaster_1"
php_admin_value[upload_tmp_dir] = "/mnt/HDD/hosts/webmaster_1/tmp"
php_admin_value[session.save_path] = "/mnt/HDD/hosts/webmaster_1/sess"
...

ранее: "Резервирование интернет-канала"далее: "Создаем свои расширения"

Обсуждение темы ещё не открыто. Вы можете быть первым.


Оставить комментарий

Имя:

Сообщение: