Давным-давно я написал статью под названием Stop Disabling SELinux!. И вот что побудило меня к этому. Многие пользователи, хостинговые компании и разработчики без колебаний отключают SELinux, добровольно и, на мой взгляд, совершенно напрасно отказываясь от важного инструмента обеспечения безопасности. В этой статье я описал несколько простых шагов, позволяющих настроить SELinux для типовой установки Drupal. Но они также подойдут для любого LAMP-приложения (плюс memcached).
Я все еще являюсь ярым сторонником использования SELinux и стараюсь убеждать в этом других. Поэтому я бы хотел рассказать о том, как в Tag1 Consulting мы управляем настройками SELinux с помощью Puppet. Использование системы управления конфигурацией позволяет нам настраивать SELinux на большом количестве хостов с минимальными усилиями, а также быть уверенными, что мы ничего не напутали и нигде не ошиблись (как это бывает при ручной настройке).
Краткая версия
«Я, знаете ли, не собираюсь читать многостраничную статью только для того, чтобы узнать, что этот парень скажет про Puppet и SELinux — давайте сразу код!»
Не вопрос! Да, иногда я бываю чересчур многословным. Если вы хотите сразу посмотреть на связку Puppet и Selinux в действии, вот модуль site_selinux в нашем git-репозитории Puppet CentOS.
А вот краткое описание классов модуля:
- init.pp — должен вызываться на каждом сервере; для включения SELinux используется класс puppet/selinux;
- drupal.pp — типовые настройки SELinux для веб-серверов Drupal; может быть модифицирован с учетом специфики вашей системы;
- newrelic.pp — нужен для установки модуля SELinux, выдающего необходимые разрешения на доступ к веб-серверу, на котором развернут New Relic;
- php-systemd.pp — конфигурация SELinux для веб-серверов, где php-fpm работает на сокетах systemd. Для получения более подробной информации об этой конфигурации см. статью Грега (Greg): Zero Downtime PHP-FPM Restarts Using Systemd;
- puppetmaster.pp — конфигурация SELinux для серверов Puppet Master.
Далее в этой статье я буду говорить исключительно о init.pp
и drupal.pp
, рассматривая настройку SELinux на веб-сервере Drupal.
О SELinux-модуле для Puppet
Мы используем модуль puppet/selinux (GitHub repo), который изначально был написан James Fryman, но теперь поддерживается группой сопровождения модулей Puppet Vox Pupuli.
Puppet/selinux предоставляет простой фреймворк для работы с SELinux в манифестах Puppet, включая: разворачивание пользовательских (custom) модулей SELinux, контроль логических значений (booleans) SELinux, а также управление контекстами SELinux, касающимися файлов и портов.
В файле README дана краткая инструкция по использованию модуля. Я расскажу об этом подробнее чуть позже, когда буду описывать настройку под CentOS веб-серверов, обслуживающих приложения на Drupal.
Обзор типовой конфигурации SELinux для Drupal
Модуль site_selinux::drupal, разработанный Tag1, содержит типовую конфигурацию SELinux для веб-серверов, обслуживающих приложения на Drupal. Несмотря на то что в каждом конкретном случае могут понадобиться специфические изменения, основные задачи остаются схожими:
- Установить необходимые логические значения SELinux: например, разрешить
httpd
доступ к MySQL и Memcached по сети или отправлять email. - Настроить касающиеся работы с файловой системой контексты SELinux, чтобы разрешить
httpd
доступ «только на чтение» или «чтение-запись» к различным директориям (отдельно от разрешений UNIX). - Загрузить пользовательские модули SELinux, необходимые для вашего окружения. В нашем случае это Solr и Varnish, позволяющие
httpd
(илиphp
) получить доступ к сетевым портам, по которым осуществляется управление Solr и Varnish. - Управлять настройками пользовательских типов портов SELinux. В нашем примере мы конфигурируем типы портов, используемые для управления доступом к Solr.
Возможно, чтобы учесть специфику вашей системы, вам придется добавить или убрать какие-то части типовой конфигурации, но она тем не менее является отличной основой для настройки большинства веб-серверов.
Нулевой шаг: включаем SELinux
Включение SELinux выполняется путем добавления класса site_selinux
в конфигурацию Puppet. Установка в hiera значения selinux::mode: 'enforcing'
обеспечивает включение SELinux (в CentOS по умолчанию будет режим targeted
).
Если система SELinux до этого была выключена, может потребоваться перезагрузка.
Шаг первый: управление логическими значениями
Логические значения SELinux — это быстрый и легкий способ управления разрешениями, которые обычно выдаются приложениям. В CentOS по умолчанию уже сконфигурировано несколько логических значений для httpd (выполните getsebool -a | grep httpd
, чтобы вывести их полный список).
Для Drupal обычно включается насколько логических значений SELinux, имеющих отношение к httpd: httpd_can_network_connect_db
, httpd_can_network_memcache
и httpd_can_sendmail
. В hiera они указаны следующим образом:
site_selinux::drupal::selbooleans:
'httpd_can_network_connect_db':
persistent: true
ensure: 'on'
'httpd_can_sendmail':
persistent: true
ensure: 'on'
'httpd_can_network_memcache':
persistent: true
ensure: 'on'
Эти настройки могут быть заданы в hiera per-host, per-server-group или там, где вы считаете нужным. Главное использовать хеш site_selinux::drupal::selbooleans
. Этот хеш считывается модулем site_selinux::drupal
с помощью следующего фрагмента кода:
$drupal_selbooleans = hiera_hash('site_selinux::drupal::selbooleans', {})
create_resources('selinux::boolean', $drupal_selbooleans)
Вместо передачи значений в виде параметров класса мы получаем их из hiera с помощью hiera_hash
. Это упрощает подгонку конфигурации под свои нужды. Например, вы можете сделать так, чтобы хеш применялся ко всем веб-серверам, а дальше продолжать кастомизацию настроек hiera уже для отдельных хостов, расширяя существующий хеш и/или переопределяя интересующие значения. При этом вам не нужно включать первоначальный хеш в определение каждого хоста.
Шаг второй: управление контекстами файлов
В поставке SELinux по умолчанию есть большое количество контекстов файлов. Это позволяет свести к минимуму необходимость дополнительной настройки. Например, если у вас есть директория Drupal sites/default/files/
в /var/www/html
, возможно, не придется вообще что-то делать, так как fcontext httpd_sys_rw_content_t
позволяет httpd изменять файлы в этой директории (что необходимо для работы Drupal). Однако многие сайты используют и другие директории, и, возможно, потребуется установить контексты файлов по умолчанию, чтобы обеспечить требуемый уровень доступа к файлам со стороны httpd. Чтобы настроить эти контексты с помощью Puppet, перечислим в hiera необходимые пути, используя следующие хеши: site_selinux::drupal::drupal_file_paths
, которому по умолчанию присваивается httpd_sys_rw_content_t
, если в hiera не указаны другие контексты, а также site_selinux::drupal::httpd_readable_paths
, которому по умолчанию присваивается httpd_sys_content_t
(httpd сможет прочитать, но не сможет записать). Поскольку эти два содержащих пути хеша обрабатываются практически одинаково, мы посмотрим только на rw-вариант:
# Хеш, содержащий путь (включает регулярное выражение) для присваивания контекстов selinux.
# Используется контекст по умолчанию 'httpd_sys_rw_content_t' (httpd-writable), если не переопределено ниже.
site_selinux::drupal::drupal_file_paths:
'localdev-private-files':
pathname: '/var/www/drupal_private_files(/.*)?'
restorecond_path: '/var/www/drupal_private_files'
'localdev-public-files':
pathname: '/var/www/drupal_public_files(/.*)?'
restorecond_path: '/var/www/drupal_public_files'
Эти настройки считываются классом site_selinux::drupal
с помощью следующего фрагмента кода:
$httpd_readable_paths = hiera_hash('site_selinux::drupal::httpd_readable_paths', {})
create_resources('selinux::fcontext', $httpd_readable_paths, { context => 'httpd_sys_content_t' })
Это очень похоже на работу с логическими значениями SELinux, но в данном случае мы устанавливаем контекст файлов по умолчанию равным httpd_sys_rw_content_t
. Если для какого-то пути нужно установить другой context
, добавьте его в хеш hiera, и он будет использован вместо дефолтного. Если в строке пути есть подстановочные символы (что обычно и случается), необходимо добавить установку restorecond_path
без регулярных выражений. Этот путь будет передан команде restorecon
, которая regexp не понимает.
Шаг третий: установка пользовательских модулей SELinux
Иногда появляется необходимость установки пользовательского модуля SELinux. Вопрос написания пользовательских модулей мы оставим для другой статьи, а сейчас сконцентрируемся на их развертывании с помощью Puppet. В этом примере мы установим два модуля: первый позволяет httpd взаимодействовать с Solr по сети, а второй — с портами управления Varnish (это может оказаться полезным при программном удалении объектов из кэша при помощи, например, модуля Drupal Varnish).
Модуль puppet/selinux
содержит определение selinux::module
, которым мы воспользуемся для установки пользовательского модуля. Следующий фрагмент hiera определяет, какие модули мы хотим установить на целевом сервере:
# Хеш устанавливаемых пользовательских модулей SELinux.
site_selinux::drupal::selinux_modules:
'httpdsolr':
source: 'puppet:///modules/site_selinux/httpdsolr/httpdsolr.te'
'httpdvarnish':
source: 'puppet:///modules/site_selinux/httpdvarnish/httpdvarnish.te'
Эти настройки считываются модулем site_selinux::drupal
с помощью следующего фрагмента кода:
$drupal_selinux_modules = hiera_hash('site_selinux::drupal::selinux_modules', {})
create_resources('selinux::module', $drupal_selinux_modules)
Каждая запись хеша hiera содержит значение source
, определяющее путь к копируемому на сервер файлу с расширением .te
(относительно структуры каталогов Puppet). Тип selinux::module
определяет копирование файла на целевой сервер. При этом человекочитаемый файл .te
компилируется в подгружаемый модуль SELinux с расширением .pp
, а затем файл модуля .pp
загружается в SELinux.
Такая конфигурация значительно облегчает добавление и обновление модулей SELinux: внесите изменения в файле модуля .te
(не забудьте изменить номер версии!), поместите его в структуру каталогов Puppet, и во время следующего запуска Puppet выполнит установку/обновление!
Шаг четвертый: пользовательские типы портов SELinux
Необходимость присвоения портам пользовательских меток с контекстами SELinux — это еще более редкий случай. Однако это нужно для нашего модуля Solr, поэтому не стоит удивляться. Мы изменим метки портов с помощью блоков exeс
, поскольку в зависимости от интересующего порта может потребоваться выполнение различных команд (например, если ему уже назначен какой-либо контекст по умолчанию).
В этом примере мы установим пользовательский тип solr_port_t
(определенный в пользовательском модуле SELinux httpdsolr.te, который мы скопировали ранее). Мы ищем порт в /etc/selinux/targeted/modules/active/ports.local
, и если его там нет, выполняем semanage port
, чтобы его настроить. Процедура одинакова для всех трех интересующих нас портов, поэтому код приведен только для одного из них:
exec { 'semanage-port-8112':
command => '/usr/sbin/semanage port -a -t solr_port_t -p tcp 8112',
unless => '/bin/grep solr_port_t /etc/selinux/targeted/modules/active/ports.local | /bin/grep -q 8112',
require => Selinux::Module['httpdsolr'],
}
Exec
требует установленный модуль httpdsolr
, поскольку в нем определен тип solr_port_t
.
Шаг пятый: наслаждайтесь безопасностью
Это все! В большинстве случаев описанный класс может быть использован лишь с незначительными изменениями в hiera. Нужно совсем немного, чтобы ваши серверы начали работать с включенным SELinux. Изложенные здесь идеи с минимальными изменениями могут быть адаптированы и для работы с другими приложениями, поэтому нет смысла откладывать этот вопрос на потом. Берите Puppet и автоматизируйте свою работу с SELinux!