Application: Apache2, PHP5 + SuPHP.
Обновляем набор системного и прикладного программного обеспечения:
# aptitude update
# aptitude full-upgrade
# aptitude full-upgrade
Устанавливаем сервер приложений и предположительно необходимый набор компонентов:
# aptitude install apache2 libapache2-mod-suphp php5-cgi
# aptitude install php5-curl php5-gd php5-imagick php5-mcrypt imagemagick php5-imap php5-sasl php5-xmlrpc
# aptitude install php5-curl php5-gd php5-imagick php5-mcrypt imagemagick php5-imap php5-sasl php5-xmlrpc
Инсталляция потянет "apache2-mpm-worker". Это модуль "поточной" обработки запросов, то есть, в нем каждый запрос обслуживается в отдельном потоке одного из дочерних процессов. Потоки - более лёгкие для ОС объекты, чем процессы, они более эффективно используют память и переключения контекста для них происходят быстрее. Однако, из-за того что каждый поток имеет доступ ко всей памяти процесса, "worker" MPM (Multi-processing module) более подвержен сбоям: сбой одного потока может повлечь падение всего процесса, в котором находился этот поток (именно поэтому "worker" MPM запускает несколько дочерних процессов с несколькими потоками в каждом). По причине потенциально большей неустойчивости выбранного "worker" необходимо будет предусмотреть механизм слежения за состоянием сервиса и принятием мер в случае сбоя в работе.
"Mod-suphp" представляет из себя своего рода "обёртку" для запуска PHP как CGI приложения для отрабатывания пользовательских скриптов с правами владельца файла. Практически то же самое можно получить с помощью ручной настройки комбинации Apache2+SUExec+PHP-CGI+FastCGI, но мы положимся на опыт и знания умных людей, которые подготовили для нас работоспособное решение.
При инсталляции Apache обычно автоматически создаётся пользователь "www-data" и одноимённая группа. Если этого не случилось (что уже говорит о том, что процесс идёт не гладко) - исправьте это, так как дальнейшие настройки будем осуществлять отталкиваясь от того, что сервер Apache запускается от имени пользователя и группы "www-data:www-data".
При инсталляции по умолчанию активны не все модули, которые нам в дальнейшем понадобятся. Дополняем конфигурацию, создавая символические ссылки (если они уже не созданы, конечно):
# ln -s /etc/apache2/mods-available/actions.load /etc/apache2/mods-enabled/actions.load
# ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/rewrite.load
# ln -s /etc/apache2/mods-available/include.load /etc/apache2/mods-enabled/include.load
# ln -s /etc/apache2/mods-available/headers.load /etc/apache2/mods-enabled/headers.load
# ln -s /etc/apache2/mods-available/suphp.load /etc/apache2/mods-enabled/suphp.load
# ln -s /etc/apache2/mods-available/suphp.conf /etc/apache2/mods-enabled/suphp.conf
# ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/rewrite.load
# ln -s /etc/apache2/mods-available/include.load /etc/apache2/mods-enabled/include.load
# ln -s /etc/apache2/mods-available/headers.load /etc/apache2/mods-enabled/headers.load
# ln -s /etc/apache2/mods-available/suphp.load /etc/apache2/mods-enabled/suphp.load
# ln -s /etc/apache2/mods-available/suphp.conf /etc/apache2/mods-enabled/suphp.conf
Займёмся настройкой web-сервиса. Припомним, что корневой конфигурационный файл несет в себе лишь базовые настройки, а все остальное в момент запуска сервера Apache подключается из указанных мест соответствующими директивами. Пробежимся по конфигурационным файлам и убедимся, что файлы сайтов лежат именно там, где им определено, возможен запуск сервера в конфигурации "по умолчанию" и при запросе сервер отвечает.
Проще говоря, в конфигурации "по умолчанию", файлы данных расположены в директории "/var/www" и при запросе на порт #80 сервер ответит "тестовой страницей", нечто вроде "It works!". Если это случилось, то сервер жив и готов подвергнуться дальнейшей настройке.
Web-сервер Apache2 на начальном этапе в настройке практически не нуждается, базовые настройки выставлены достаточно оптимально для среднестатистических запросов, а вопросы тонкой настройки мы будем рассматривать позже, применительно к работающей полнофункциональной схеме.
Для удобства и исключения сомнений в дальнейшем в конфигурационном файле "/etc/apache2/conf.d/charset" выставим значение кодировки отдаваемых страниц "по умолчанию" (персональные параметры ресурсов позже можно будет переопределить в конфигурации "виртуальных ресурсов"):
AddDefaultCharset UTF-8
В конфигурационном файле "/etc/apache2/conf.d/security" укажем базовые параметры корневой директории ресурсов с наследованием потомками (персональные параметры ресурсов позже можно будет переопределить в конфигурации "виртуальных ресурсов"):
<Directory /var/www>
DirectoryIndex index.php index.html
Options +FollowSymLinks -Indexes
# запрещаем переопределение конфигурации сервера в файле .htaccess из соображений увеличения производительности
AllowOverride None
Order deny,allow
Deny from all
</Directory>
DirectoryIndex index.php index.html
Options +FollowSymLinks -Indexes
# запрещаем переопределение конфигурации сервера в файле .htaccess из соображений увеличения производительности
AllowOverride None
Order deny,allow
Deny from all
</Directory>
Опишем режим использования модуля "SUPHP" в конфигурационном файле "/etc/apache2/mods-available/suphp.conf". Здесь мы велим серверу Apache2 при наличии модуля "mod_suphp" зарегистрировать привязку расширений файлов за типом ресурсов, далее к типу ресурсов привязываем обработчик таковых, и потом ограничиваем работу модуля целевой директорией:
<IfModule mod_suphp.c>
AddType application/x-httpd-php .php .php3 .php4 .php5 .phtml
suPHP_AddHandler application/x-httpd-php
<Directory /var/www>
suPHP_Engine on
</Directory>
</IfModule>
AddType application/x-httpd-php .php .php3 .php4 .php5 .phtml
suPHP_AddHandler application/x-httpd-php
<Directory /var/www>
suPHP_Engine on
</Directory>
</IfModule>
В конфигурационном файле Apache2 по адресу "/etc/apache2/conf.d/security" приведем ряд переменных к следующему виду:
ServerTokens Minimal
ServerSignature Off
TraceEnable Off
ServerSignature Off
TraceEnable Off
Опишем конфигурацию модуля "SUPHP" в конфигурационном файле "/etc/suphp/suphp.conf":
[global]
; Path to logfile
logfile=/var/log/apache2/suphp.log
; Loglevel
loglevel=info
; User Apache is running as
webserver_user=www-data
; Path all scripts have to be in
docroot=/var/www
; Security options
allow_file_group_writeable=false
allow_file_others_writeable=false
allow_directory_group_writeable=false
allow_directory_others_writeable=false
; Check wheter script is within DOCUMENT_ROOT
check_vhost_docroot=true
; Send minor error messages to browser
errors_to_browser=false
; PATH environment variable
env_path=/bin:/usr/bin
; Umask to set, specify in octal notation
umask=0027
; Minimum UID
min_uid=1000
; Minimum GID
min_gid=33
[handlers]
; Handler for php-scripts
application/x-httpd-php=php:/usr/bin/php-cgi
; Handler for CGI-scripts
x-suphp-cgi=execute:!self
; Path to logfile
logfile=/var/log/apache2/suphp.log
; Loglevel
loglevel=info
; User Apache is running as
webserver_user=www-data
; Path all scripts have to be in
docroot=/var/www
; Security options
allow_file_group_writeable=false
allow_file_others_writeable=false
allow_directory_group_writeable=false
allow_directory_others_writeable=false
; Check wheter script is within DOCUMENT_ROOT
check_vhost_docroot=true
; Send minor error messages to browser
errors_to_browser=false
; PATH environment variable
env_path=/bin:/usr/bin
; Umask to set, specify in octal notation
umask=0027
; Minimum UID
min_uid=1000
; Minimum GID
min_gid=33
[handlers]
; Handler for php-scripts
application/x-httpd-php=php:/usr/bin/php-cgi
; Handler for CGI-scripts
x-suphp-cgi=execute:!self
Директива "umask" в конфигурационном файле, могущая "вынести мозг" начинающим администраторам, это не значение правил доступа к создаваемым объектам, а маска, накладываемая на правила доступа "по умолчанию", то есть на значения для файла в "666" или директории "777"; в итоге значение "umask" в "027" даст при наложении на "666", для создаваемого PHP-файла, "640" (владельцу можно все, группе только чтение, другим запрещено все) и для директории, создаваемой PHP, "750" (владельцу разрешено все, группе просмотр и чтение, другим запрещено все).
Опишем в конфигурационных файлах Apache2 наши виртуальные ресурсы и укажем в них на режим запуска исполняемых скриптов. Создадим в директории "/etc/apache2/sites-available/" файлы "u000.local" и "u001.local" примерно со следующим содержимым, варьирующимся в зависимости от имён ресурсов и мест их расположения:
# touch /etc/apache2/sites-available/u1000
# touch /etc/apache2/sites-available/u1001
# touch /etc/apache2/sites-available/u1001
Пример содержания конфигурационного файла для виртуальной площадки пользователя "u1000":
<VirtualHost *:80>
ServerName u1000.local
ServerAlias www.u1000.local
ServerAdmin admin@u1000.local
DocumentRoot /var/www/u1000/www
# AddDefaultCharset windows-1251
suPHP_ConfigPath /var/www/php/u1000
# SetEnv PHP_INI_SCAN_DIR /var/www/php/u1000/conf.d
<Directory /var/www/u1000/www>
Options +ExecCGI
# AllowOverride All
Order allow,deny
Allow from all
</Directory>
LogLevel warn
ErrorLog /var/www/u1000/log/apache.error.log
CustomLog /var/www/u1000/log/apache.access.log combined
</VirtualHost>
ServerName u1000.local
ServerAlias www.u1000.local
ServerAdmin admin@u1000.local
DocumentRoot /var/www/u1000/www
# AddDefaultCharset windows-1251
suPHP_ConfigPath /var/www/php/u1000
# SetEnv PHP_INI_SCAN_DIR /var/www/php/u1000/conf.d
<Directory /var/www/u1000/www>
Options +ExecCGI
# AllowOverride All
Order allow,deny
Allow from all
</Directory>
LogLevel warn
ErrorLog /var/www/u1000/log/apache.error.log
CustomLog /var/www/u1000/log/apache.access.log combined
</VirtualHost>
Для некоторых версий модуля в пользовательской конфигурации можно указать принудительно, от имени какого пользователя и группы запускается скрипт с помощью директивы "suPHP_UserGroup user groupe". В нашей этого делать не нужно, скрипты будут исполняться от имени того пользователя - владельца исполняемого файла.
Выше опцией "suPHP_ConfigPath" мы указываем месторасположение индивидуального для площадки конфигурационного файла "php.ini". Модуль "suphp" каким-то своим способом подставляет его вместо системного, который по умолчанию располагается по адресу "/etc/php5/cgi/php.ini". Тут всё просто - каждой площадке выделяется по индивидуальному файлу "php.ini", в котором мы вольны задавать её параметры так вольно, как нам будет угодно.
Другая опция - "SetEnv PHP_INI_SCAN_DIR" - неактивна, но готова к применению. Она задаёт отличное от принятого по умолчанию "/etc/php5/conf.d/" месторасположение директории, содержимое которой добавляется к содержимому основного конфигурационного файла интерпретатора "php.ini".
Если опцию "SetEnv PHP_INI_SCAN_DIR" не использовать, как это делаю я, то загрузка конфигураций происходит в два этапа: вначале подключается индивидуальный файл "php.ini", месторасположение которого определено опцией "suPHP_ConfigPath", а вторым этапом загружаются динамически подключаемые файлы из места по умолчанию "/etc/php5/conf.d/". Это удобно в плане глобального применения настроек; чтобы мы не накрутили в индивидуальном "php.ini" площадки, всё можно переопределить в глобальной конфигурации, которая загрузится из "/etc/php5/conf.d/" уже после применения "php.ini". Удобно это так же и тем, что при установке дополнительных или обновлении уже работающих модулей не нужно менять настройки каждой площадки - достаточно сделать это один раз для содержимого "/etc/php5/conf.d/".
В случае активации опции "SetEnv PHP_INI_SCAN_DIR", указывающей на иное расположение динамически подключаемых конфигурационных файлов, мы получим для целевой площадки возможность полностью индивидуальной настройки. Это бывает полезно, когда из ста площадок только одной-двум нужна эксклюзивная конфигурация, конфликтующая с общепринятой на сервере. Понятно, что перед изменением режима необходимо сформировать содержимое целевой директории динамических конфигураций "/var/www/php/*/conf.d", а то подключать нечего будет.
Есть ещё возможность переписать значение переменных конфигурации непосредственно во время исполнения PHP-скрипта, но такому воздействию доступны далеко не все параметры, отчасти и потому, что это может быть запрещено предыдущими настройками в защитных целях.
Теперь займёмся непосредственно PHP-интерпретатором.
Скопируем в служебную директорию конфигурационный файлы интерпретатора PHP. Далее все работы будут проводится уже с ним:
# cp /etc/php5/cgi/php.ini /var/www/php/php.ini
Немного оптимизируем работу нашего сервера и ужесточим политики доступа на нашем сервере, усложнив работу желающим получить информацию о состоянии. Запретим исполнятся чему бы то ни было за пределами каталогов наших web-ресурсов. Запретим показ расширенной информации о наших ресурсах и сервере.
В используемом нами базовом конфигурационном файле PHP по адресу "/var/www/php/php.ini" приведем ряд переменных к следующему виду (чтение и применение параметров конфигурационного файла PHP производится последовательно, потому достаточно разместить свои параметры в конце файла, чтобы переопределить значения расположенные выше по тексту):
;; Custom global configuration ;;
max_execution_time = 90
max_input_time = 90
safe_mode = On
safe_mode_gid = On
allow_url_fopen = Off
allow_url_include = Off
enable_dl = Off
expose_php = Off
disable_functions = "exec, shell_exec, system, passthru, popen, proc_open, phpinfo, curl_exec, curl_multi_exec, parse_ini_file, show_source, ini_restore, com_load_typelib, symlink, eval"
mysql.allow_persistent = Off
pgsql.allow_persistent = Off
max_execution_time = 90
max_input_time = 90
safe_mode = On
safe_mode_gid = On
allow_url_fopen = Off
allow_url_include = Off
enable_dl = Off
expose_php = Off
disable_functions = "exec, shell_exec, system, passthru, popen, proc_open, phpinfo, curl_exec, curl_multi_exec, parse_ini_file, show_source, ini_restore, com_load_typelib, symlink, eval"
mysql.allow_persistent = Off
pgsql.allow_persistent = Off
С глобальными настройками мы закончили, проработаем теперь методику создания индивидуальных конфигураций для PHP-интерпретатора.
Скопируем конфигурационный файл интерпретатора PHP в директории индивидуальных пользовательских конфигураций для принудительного запуска скриптов пользователя с указанными файлами (кстати, использование индивидуальных конфигурационных файлов PHP спасет нам конфигурацию при обновлении PHP, так как базовый конфигурационный файл инсталлятором злостно заменяется без вопросов):
# cp /var/www/php/php.ini /var/www/php/u1000/php.ini
# cp /var/www/php/php.ini /var/www/php/u1001/php.ini
# cp /var/www/php/php.ini /var/www/php/u1001/php.ini
Применим правила доступа к служебным файлам:
# chown -R root:ug1000 /var/www/php/u1000
# chown -R root:ug1001 /var/www/php/u1001
# chmod -R o-rwx /var/www/php/u1000 /var/www/php/u1001
# chmod -R g-w /var/www/php/u1000 /var/www/php/u1001
# chown -R root:ug1001 /var/www/php/u1001
# chmod -R o-rwx /var/www/php/u1000 /var/www/php/u1001
# chmod -R g-w /var/www/php/u1000 /var/www/php/u1001
В создаваемых нами индивидуальных конфигурационных файлах "/var/www/php/u000/php.ini", "/var/www/php/u001/php.ini" и так далее, приведем ряд переменных к следующему виду или допишем их в конце конфигурационного файла для переопределения установленных "по умолчанию" в нем переменных (пример для площадки пользователя "u1000"):
;; Custom user configuragion ;;
open_basedir = /var/www/u1000
upload_tmp_dir = /var/www/u1000/tmp
session.save_path = /var/www/u1000/tmp
open_basedir = /var/www/u1000
upload_tmp_dir = /var/www/u1000/tmp
session.save_path = /var/www/u1000/tmp
Создадим тестовые файлы на виртуальных площадках пользователей "/var/www/u1000/www/info.php" и "/var/www/u1001/www/info.php" со следующим содержимым:
# touch /var/www/u1000/www/info.php
# touch /var/www/u1001/www/info.php
# touch /var/www/u1001/www/info.php
<?php
phpinfo();
?>
phpinfo();
?>
Размещаем символические ссылки в директории, откуда Apache2 берет конфигурационные файлы ресурсов:
# ln -s /etc/apache2/sites-available/u1000 /etc/apache2/sites-enabled/u1000
# ln -s /etc/apache2/sites-available/u1001 /etc/apache2/sites-enabled/u1001
# ln -s /etc/apache2/sites-available/u1001 /etc/apache2/sites-enabled/u1001
Удаляем символическую ссылку на конфигурацию "по умолчанию", что досталась нам от инсталлятора Apache2, чтобы не вносить путаницу в дальнейшее конфигурирование:
# rm /etc/apache2/sites-enabled/default
Заставляем Apache2 перечитать свои конфигурационные файлы и принять изменения:
# /etc/init.d/apache2 reload
Если все, что мы проделали выше - правильно, то при запросе в браузере целевого ресурса мы сможем узреть вывод функции phpinfo().
По завершению конфигурирования Apache закрываем от постороннего взгляда директорию конфигурации:
# chown -R root:root /etc/apache
# chmod -R o-rwx /etc/apache
# chmod -R o-rwx /etc/apache
После проделанных описанных выше работ мы получаем web-сервер в базовой конфигурации с разделением пользовательских ресурсов. Учитывая то, вызов системных функций отключён, разрешения выставлены достаточно жёстко, web-сервер имеет право только на чтение данных, а исполняемые скрипты запускаются с правами владельца файлов, можно полагать, что пользовательские ресурсы достаточно надёжно изолированны друг от друга.