Сегодня у нас на повестке дня вопросы, касающиеся обеспечения безопасности Web-сервера на базе OpenBSD. Мы научим PHP, MySQL и Sendmail работать с Apache, который запускается в окружении chroot - измененном корневом каталоге /var/www с правами непривилегированного пользователя www. Таким образом, нам удастся обеспечить дополнительный уровень защиты и максимально снизить возможный ущерб в том случае, если злоумышленнику окажется под силу взломать нашу систему. Применив полученные знания на практике, в твоем арсенале будет настолько защищенная система, что ты сможешь совершенно спокойно взяться за разработку любого проекта, будь то личный блог, новостной сайт компании или даже интернет-магазин с мириадами клиентов.
предварительный ликбез
Последняя версия ультрасекьюрной OpenBSD (3.9 на момент написания статьи) как нельзя лучше подойдет для выполнения нашей миссии. Короткая история взломов, прекрасная реализация стека TCP/IP, отличный файервол Packet Filter (pf), залатанный Apache 1.3.29 с поддержкой SSL, наличие последних версий OpenSSH и OpenSSL, тысячи добротно протестированных прекомпилированных пакетов - все это говорит в пользу сделанного выбора. Хотя стоит отметить, что в качестве используемой операционной системы может выступать любая из Free/Net/DragonFlyBSD.
За основу нашей конструкции примем PHP и MySQL. Не секрет, что за последние годы эта связка стала стандартом де-факто для интернет-проектов различного масштаба. Поэтому давай не будем на этом останавливаться и перейдем непосредственно к настройке.
каждой службе - свой раздел
Прежде всего, необходимо грамотно подойти к разделению дискового пространства. Лично я предпочитаю для каждой критически важной сетевой службы выделять собственный раздел. Для наглядности приведу содержимое конфига fstab(5) полностью:
Настало время проверить работоспособность БД и запустить скрипт для создания типовых баз mysql и test:
# /usr/local/bin/mysql_install_db
Теперь в стартовом сценарии /etc/rc.local указываем опции для mysqld_safe - своего рода обертки, которая запускает mysqld с заданными параметрами, производит мониторинг состояния демона и при необходимости перезапускает главный процесс MySQL.
# vi /etc/rc.local
if [ -x /usr/local/bin/mysqld_safe ]; then
echo -n ' mysqld'
/usr/local/bin/mysqld_safe --user=_mysql \
--open-files=1000 --skip-networking \
--socket=/var/www/var/run/mysql/mysql.sock &
fi
Прокомментирую опции, которые мы указали при старте MySQL:
- --USER=_MYSQL - ЗАПУСК ДЕМОНА ОТ ИМЕНИ НЕПРИВИЛЕГИРОВАННОГО ПОЛЬЗОВАТЕЛЯ _MYSQL;
- --OPEN-FILES=1000 - МАКСИМАЛЬНОЕ ЧИСЛО ОТКРЫТЫХ ФАЙЛОВ;
- --SKIP-NETWORKING - MYSQLD НЕ ДОЛЖЕН БИНДИТЬСЯ НА СЕТЕВЫЕ АДРЕСА;
- --SOCKET=/VAR/WWW/VAR/RUN/MYSQL/MYSQL.SOCK - ТАК КАК НАША БД БУДЕТ ИСПОЛЬЗОВАТЬСЯ ТОЛЬКО ЛОКАЛЬНО УСТАНОВЛЕННЫМИ ПРОГРАММАМИ, РАБОТАЕМ ЧЕРЕЗ СОКЕТ;
- '&' - ВЫПОЛНЯЕМ ПЕРЕВОД MYSQLD_SAFE В ФОНОВЫЙ РЕЖИМ.
Чтобы не изобретать велосипед, давай возьмем предлагаемый разработчиками пример конфига MySQL, назначим ему корректные права доступа и отредактируем применительно к нашим задачам:
Как ты мог заметить, главное отличие от дефолтного my-medium.cnf заключается в определении местоположения абсолютного пути до сокета клиента и сервера MySQL. Вместо /var/run/mysql/mysql.sock мы будем использовать /var/www/var/run/mysql/mysql.sock, поэтому своевременно подготавливаем соответствующую поддиректорию:
# mkdir -p /var/www/var/run/mysql
# chown _mysql:_mysql /var/www/var/run/mysql
Для хранения временных файлов необходимо создать каталог /var/www/tmp с либеральными правами доступа:
# mkdir -p -m 777 /var/www/tmp
С установкой и конфигурированием разобрались, переходим к запуску демона на орбиту:
Опциональный шаг: выполняем ряд несложных операций по увеличению безопасности MySQL:
# /usr/local/bin/mysql -u root
// Пустой пароль для администратора MySQL-сервера нам не подходит, устанавливаем новый
mysql> set password for root@localhost=password("noidea");
// Удаляем базу данных test, которая была создана скриптом mysql_install_db
mysql> drop database test;
// Удаляем все MySQL'ные учетные записи, кроме root
mysql> use mysql;
mysql> delete from db;
mysql> delete from user where not (host="localhost" and user="root");
mysql> flush privileges;
// Чтобы усложнить атаки типа bruteforce, можно изменить имя главной учетной записи с root на admin
mysql> update user set user="admin" where user="root";
mysql> flush privileges;
// Настройка закончена
mysql> quit
хардкорные разборки с PHP
Далее на очереди - PHP4 со своими расширениями. Следующими командами мы установим пакет с основным движком - так называемый core-пакадж, модуль для работы с базами данных и библиотеку pear (набор специальных компонентов и расширений для PHP).
Для корректной работы электронной почты в Apache chroot необходимо установить статически слинкованную версию mini_sendmail. Этот фэйковый почтовик будет передавать из среды chroot всю исходящую почту полноценному транспортному агенту.
# cd /usr/port/mail/mini_sendmail
# env SUBPACKAGE=-chroot make install
# cp -p /bin/sh /var/www/bin
# mkdir -p /var/www/etc
# cp /etc/{hosts,resolv.conf} /var/www/etc
Теперь снова возвращаемся в php.ini и указываем абсолютный путь до mini_sendmail относительно /var/www (внимание: запись «-fwww@mydomain.ru» обязательно должна идти без пробела).
Разработчики OpenBSD выполнили львиную долю работы за нас, посадив Apache/mod_ssl в chroot-окружение. Нам лишь остается активировать поддержку PHP и разобраться с виртуальными хостами. Для этого переходим к редактированию главного конфигурационного файла индейца:
Allow from localhost 192.168.1.0/24 192.168.3.0/24 212.XX.XY.162
</Directory>
управляем MySQL с комфортом
PHPMyAdmin представляет собой набор PHP-скриптов для управления сервером MySQL. Прекрасно подходит для поклонников визуального администрирования и тех, у кого синтаксис SQL-запросов вызывает определенную сложность. Кроме того, с помощью PHPMyAdmin довольно удобно выполнять рутинные операции по бэкапу, созданию и модификации баз данных, таблиц, пользователей и т.д. Устанавливаем:
В конфиге config.inc.php указываем, что в качестве типа соединения у нас используется «сокет» (напомню, mysqld не подвешен даже на интерфейс обратной петли), а также имя и пароль администратора MySQL:
# vi config.inc.php
$cfg['Servers'][$i]['socket'] = '';
$cfg['Servers'][$i]['connect_type'] = 'socket';
$cfg['Servers'][$i]['auth_type'] = 'config';
$cfg['Servers'][$i]['user'] = 'admin';
$cfg['Servers'][$i]['password'] = 'noidea';
Описание поддомена phpmyadmin.mydomain.ru в httpd.conf будет выглядеть следующим образом:
CustomLog logs/virtual.phpmyadmin.mydomain-access_log common
</VirtualHost>
Совершенно очевидно, что доступ к phpMyAdmin необходимо ограничить. Этого можно добиться разными способами. Для расширения кругозора предлагаю воспользоваться аутентификацией по паролю. Чтобы проконтролировать доступ к каталогу /var/www/virtual/phpmyadmin.mydomain.ru и запретить по сети передавать пароли в открытом виде (директива SSLRequireSSL), создаем еще один управляющий файл - .htaccess. Преимущество использования такого подхода состоит в том, что мы не захламляем httpd.conf дополнительными директивами для описания правил доступа, указания местонахождения Auth-конфигов и методов аутентификации. Плюс к этому, при изменении конфигурации в файле .htaccess не придется перезагружать Web-сервер.
# vi /var/www/virtual/phpmyadmin.mydomain.ru/.htaccess
SSLRequireSSL
AuthType Basic
AuthName "Password Required"
AuthUserFile /var/www/conf/.htpasswd
AuthGroupFile /dev/null
<Limit GET POST>
require user admin
</Limit>
Аутентификационную базу /var/www/conf/.htpasswd (ни в коем случае не размещай .htpasswd в каталоге /var/www/virtual/phpmyadmin.mydomain.ru) будем вести с помощью утилиты htpasswd(1). Ключ '-c' отвечает за создание базы, ключ '-m' задает использование алгоритма шифрования MD5, вместо применяемой по умолчанию DES'овской функции crypt(3):
# htpasswd -cm /var/www/conf/.htpasswd admin
Только суперпользователь и демон httpd имеют право обращаться к базе с паролями:
# chown root:www /var/www/conf/.htpasswd
# chmod 640 /var/www/conf/.htpasswd
проводим безопасные транзакции по протоколу https
Чтобы получить возможность устанавливать защищенные сеансы по протоколу https, необходимо создать приватный ключ, ввести регистрационные данные и подписать сертификат собственным ключом. Начнем с генерации секретного RSA-ключа длиной 1024 бит:
Выполняем остановку и повторный запуск httpd(8), но уже с поддержкой https:
# apachectl stop
# apachectl startssl
Проверяем, забиндился ли апач на соответствующие порты:
% netstat -na -f inet | egrep '80|443'
tcp 0 0 *.80 *.* LISTEN
tcp 0 0 *.443 *.* LISTEN
В /etc/pf.conf создаем правило, разрешающее прохождение запросов к портам 80 и 443:
# vi /etc/pf.conf
$ext_if = "fxp0"
pass in log on $ext_if inet proto tcp from any to $ext_if \
port { www, https } flags S/SA keep state
Чтобы внесенные изменения вступили в силу, не забудь перезагрузить набор рулесетов файервола:
# pfctl -f /etc/pf.conf
В конфиге /etc/rc.conf следующими записями разрешаем автоматическую загрузку Apache SSL и Packet Filter при старте системы:
# vi /etc/rc.conf
httpd_flags="-DSSL"
pf=YES
постскриптумы
На этом настройку можно считать завершенной. Протестировать работу модуля php4 и его взаимодействие с MySQL можно с помощью php-файла, исходный код которого приведен ниже. Если при запросе www.mydomain.ru/test.php ты получишь лаконичный ответ «ok», смело начинай заливать любимую CMS'ку в каталог /var/www/virtual/www.mydomain.ru. Удачи.
# vi /var/www/virtual/www.mydomain.ru/test.php
<html><body>
<?php
mysql_connect("localhost", "admin", "noidea") or die("failed");