Помню свой первый случай с SELinux на продуктивном сервере. Развернул Apache, настроил виртуальные хосты, разложил файлы по новым директориям. Уверенно запускаю, проверяю в браузере... и вижу 403 Forbidden. В логах лаконично: "SELinux is preventing httpd from read access". Тогда я, как многие начинающие администраторы, полез в поисковик и нашёл "чудодейственное решение" - setenforce 0. Сработало! Но через несколько месяцев понял, какую брешь в безопасности я открыл. С тех пор подхожу к SELinux иначе. Сегодня расскажу, как правильно настроить взаимодействие Apache и SELinux, используя все возможности современных систем на базе RHEL, CentOS и Fedora.

Что происходит за кулисами блокировки

Security-Enhanced Linux работает по принципу обязательного контроля доступа (MAC - Mandatory Access Control). Каждый процесс получает свой контекст безопасности, каждый файл тоже помечен специальной меткой. Когда процесс httpd (работающий в домене httpd_t) пытается получить доступ к файлу, SELinux проверяет совместимость их контекстов. Если файл имеет метку default_t или user_home_t вместо ожидаемой httpd_sys_content_t, система моментально блокирует операцию.

Можно сказать, это как пропускная система в здании: у вас есть бейдж сотрудника (контекст httpd_t), но он даёт доступ только к определённым помещениям (файлам с меткой httpd_sys_content_t). Попытка войти в серверную комнату с неподходящим пропуском закончится отказом, даже если физически дверь не заперта.

Политики SELinux определяют, какие типы файлов может читать процесс в домене httpd_t. По умолчанию Apache имеет доступ к контенту с типом httpd_sys_content_t, но не может трогать файлы Samba (samba_share_t) или пользовательские домашние директории (user_home_t). Такое разделение спасает в критических ситуациях: если злоумышленник взломает веб-сервер через уязвимость в PHP-скрипте, он окажется в изолированной песочнице. SELinux не позволит ему читать системные файлы или атаковать другие службы.

Типичные проблемы возникают, когда администраторы размещают контент вне стандартных путей. Вместо /var/www/html/ выбирают /srv/myweb/ или /home/user/public_html/. Новые файлы наследуют контекст родительской директории, получая метки вроде var_t или user_home_t. Apache смотрит на такой файл и получает от SELinux категорический отказ в доступе.

Читаем журнал аудита: где искать правду

Первое правило работы с SELinux: всегда начинай с логов. Все события блокировки записываются в /var/log/audit/audit.log с меткой типа AVC (Access Vector Cache). Эти записи выглядят устрашающе, но содержат всю необходимую информацию.

Команда ausearch позволяет фильтровать записи аудита по различным критериям. Чтобы увидеть недавние блокировки Apache, выполните:

ausearch -c "httpd" -m AVC,USER_AVC,SELINUX_ERR -ts recent -i

Флаг -i преобразует числовые значения в читаемый вид (например, UID в имена пользователей). Типичная запись выглядит так:

type=AVC msg=audit(1395177286.929:1638): avc: denied { read } for pid=6591 comm="httpd" name="index.php" dev="dm-0" ino=2112 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file

Разберём по частям: denied { read } говорит о попытке чтения, comm="httpd" указывает на процесс Apache, scontext показывает контекст источника (httpd_t), tcontext демонстрирует контекст цели (default_t), tclass=file означает, что объект - файл. Всё ясно: процесс httpd не может прочитать файл с неподходящей меткой.

Если установлен пакет setroubleshoot-server, жизнь становится проще. Утилита sealert анализирует логи и предлагает решения:

sealert -l UUID_из_журнала

Можно просто просмотреть последние предупреждения:

sealert -a /var/log/audit/audit.log

Вывод включает описание проблемы, потенциальные причины и рекомендации по устранению. Иногда предлагается изменить контекст файлов, иногда активировать boolean-параметр, иногда создать пользовательскую политику.

Для быстрого просмотра всех AVC-событий за сегодня используйте aureport:

aureport -a -ts today

Эта команда выводит сводку: сколько блокировок произошло, какие процессы затронуты, какие объекты были целью. Удобно для общей картины, когда нужно понять масштаб проблемы.

Контексты файлов: краеугольный камень доступа

Самая распространённая причина блокировок Apache через SELinux - неправильные метки на файлах и директориях. Проверить текущий контекст можно командой ls с флагом -Z:

ls -Z /var/www/html/index.html

Вывод покажет что-то вроде:

-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html

Здесь важна часть httpd_sys_content_t - это тип файла. Для статического веб-контента Apache ожидает именно этот тип. Если видите default_t, var_t или user_home_t, доступ будет заблокирован независимо от прав UNIX.

Временное изменение контекста выполняется командой chcon:

chcon -t httpd_sys_content_t /srv/myweb/index.php

Для рекурсивного применения к директории добавьте флаг -R:

chcon -R -t httpd_sys_content_t /srv/myweb/

Однако такие изменения не переживают полную перемаркировку файловой системы или выполнение restorecon. Для постоянного решения используйте semanage:

semanage fcontext -a -t httpd_sys_content_t "/srv/myweb(/.*)?"

Регулярное выражение "/srv/myweb(/.*)?" означает: применить контекст к директории /srv/myweb и всему её содержимому рекурсивно. После добавления правила примените его:

restorecon -R -v /srv/myweb/

Флаг -v (verbose) покажет, какие файлы изменились:

restorecon reset /srv/myweb context unconfined_u:object_r:default_t:s0->system_u:object_r:httpd_sys_content_t:s0
restorecon reset /srv/myweb/index.php context unconfined_u:object_r:default_t:s0->system_u:object_r:httpd_sys_content_t:s0

Для директорий, куда Apache должен записывать данные (например, загрузки пользователей, кэш, сессии), используйте тип httpd_sys_rw_content_t:

semanage fcontext -a -t httpd_sys_rw_content_t "/srv/myweb/uploads(/.*)?"
restorecon -R -v /srv/myweb/uploads/

Этот тип даёт права на чтение и запись. Для логов Apache существует специальный тип httpd_log_t:

semanage fcontext -a -t httpd_log_t "/srv/myweb/logs(/.*)?"
restorecon -R -v /srv/myweb/logs/

Проверить, соответствуют ли файлы ожидаемым контекстам, можно командой matchpathcon:

matchpathcon -V /var/www/html/*

Если есть несоответствия, вывод укажет на них. Чтобы восстановить контексты по умолчанию для стандартных путей:

restorecon -R -v /var/www/html/

Удалить пользовательское правило можно так:

semanage fcontext -d "/srv/myweb(/.*)?"

После удаления правила файлы сохранят текущие контексты до следующего restorecon.

Boolean-переключатели: гибкость без компромиссов

SELinux предлагает набор boolean-параметров для быстрой настройки политик без перезагрузки или создания модулей. Для Apache доступны десятки таких переключателей. Посмотреть список:

getsebool -a | grep httpd

Вывод покажет текущее состояние каждого параметра:

httpd_can_network_connect --> off
httpd_can_network_connect_db --> off
httpd_enable_homedirs --> off
httpd_enable_cgi --> on

Для получения описания параметров установите пакет selinux-policy-devel и используйте semanage:

semanage boolean -l | grep httpd

Допустим, ваше веб-приложение обращается к базе данных MySQL по сети. По умолчанию SELinux запретит такое соединение. Включите соответствующий boolean:

setsebool -P httpd_can_network_connect_db on

Флаг -P делает изменение постоянным (записывается в /etc/selinux/targeted/modules/active/booleans.local). Без этого флага настройка сбросится при перезагрузке.

Если приложению нужен доступ к любым сетевым ресурсам (не только базам данных), используйте более широкий параметр:

setsebool -P httpd_can_network_connect on

Это разрешит Apache устанавливать исходящие TCP/UDP соединения, что необходимо для работы с внешними API, прокси-серверами, кэш-хранилищами типа Redis или Memcached.

Для обслуживания файлов из домашних директорий пользователей:

setsebool -P httpd_enable_homedirs on

Но помните: даже после включения boolean нужно правильно промаркировать директории. Для ~/public_html/ выполните:

chcon -R -t httpd_sys_content_t ~/public_html/

Если Apache должен выполнять CGI-скрипты (по умолчанию отключено):

setsebool -P httpd_enable_cgi on

При этом сами скрипты нужно пометить типом httpd_sys_script_exec_t:

semanage fcontext -a -t httpd_sys_script_exec_t "/var/www/cgi-bin(/.*)?"
restorecon -R -v /var/www/cgi-bin/

Для доступа к файловым системам NFS или CIFS:

setsebool -P httpd_use_nfs on
setsebool -P httpd_use_cifs on

Для отправки email из PHP-скриптов:

setsebool -P httpd_can_sendmail on

Проверить, что изменения применились:

getsebool httpd_can_sendmail

Вернуть параметр в исходное состояние:

setsebool -P httpd_can_sendmail off

Работа с нестандартными портами

По умолчанию Apache может слушать ограниченный набор портов: 80, 81, 443, 488, 8008, 8009, 8443, 9000. Попытка запустить сервер на порту 9876 приведёт к ошибке и записи в аудите. Проверить разрешённые порты:

semanage port -l | grep http_port_t

Вывод покажет:

http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000

Добавить свой порт:

semanage port -a -t http_port_t -p tcp 9876

Теперь Apache сможет слушать порт 9876 без конфликтов. Проверить:

semanage port -l | grep 9876

Удалить порт из списка:

semanage port -d -t http_port_t -p tcp 9876

Для портов кэш-прокси существует отдельный тип http_cache_port_t (по умолчанию 8080, 8118, 8123, 10001-10010):

semanage port -a -t http_cache_port_t -p tcp 3128

Создание пользовательских политик через audit2allow

Когда ни правильные контексты, ни boolean не помогают, остаётся последний вариант: создание пользовательского модуля политики. Утилита audit2allow анализирует логи аудита и генерирует правила для разрешения заблокированных действий.

Сначала соберите информацию о блокировках:

ausearch -c "httpd" -m AVC,USER_AVC --raw | audit2allow -M myhttpd

Команда создаст два файла: myhttpd.te (исходный код политики) и myhttpd.pp (скомпилированный модуль). Перед установкой изучите содержимое .te файла:

cat myhttpd.te

Вы увидите что-то вроде:

module myhttpd 1.0;

require {
    type httpd_t;
    type var_t;
    class file { getattr open read };
}

allow httpd_t var_t:file { getattr open read };

Это правило разрешает процессу httpd_t читать файлы типа var_t. Убедитесь, что такое разрешение безопасно и действительно необходимо. Только после этого устанавливайте модуль:

semodule -i myhttpd.pp

Проверить установленные модули:

semodule -l | grep myhttpd

Удалить модуль:

semodule -r myhttpd

Для генерации политики из всех недавних блокировок:

ausearch -m AVC -ts recent | audit2allow -M myhttp-all

Важное предупреждение: audit2allow создаёт максимально разрешающие правила. Если в логах накопились записи о различных попытках доступа (легитимных и нет), модуль откроет слишком широкий доступ. Используйте этот метод только после исчерпания других вариантов и тщательного анализа генерируемых правил.

Флаг -w покажет, почему создание политики может быть плохой идеей:

ausearch -m AVC -ts recent | audit2allow -w

Альтернативный подход: создать политику для конкретного действия. Найдите UUID блокировки в выводе sealert и сгенерируйте модуль только для этого случая:

ausearch -m AVC -ts recent | grep "UUID_из_лога" | audit2allow -M myspecific

Практический кейс: развёртывание приложения в /srv

Разберём реальную ситуацию. Вы решили разместить веб-приложение в /srv/webapp/ вместо стандартного /var/www/html/. Создали структуру директорий, скопировали файлы, настроили виртуальный хост Apache. Перезапускаете сервер, открываете браузер... 403 Forbidden. В error_log видите Permission denied, в audit.log множество AVC denial.

Шаг первый: проверьте текущие контексты:

ls -Z /srv/webapp/

Скорее всего, увидите var_t или default_t. Создайте правило для корректной маркировки:

semanage fcontext -a -t httpd_sys_content_t "/srv/webapp(/.*)?"

Примените правило:

restorecon -R -v /srv/webapp/

Проверьте результат:

ls -Z /srv/webapp/

Все файлы должны получить метку httpd_sys_content_t. Если приложению нужна запись в директорию cache:

semanage fcontext -a -t httpd_sys_rw_content_t "/srv/webapp/cache(/.*)?"
restorecon -R -v /srv/webapp/cache/

Для директории логов:

semanage fcontext -a -t httpd_log_t "/srv/webapp/logs(/.*)?"
restorecon -R -v /srv/webapp/logs/

Если приложение отправляет email:

setsebool -P httpd_can_sendmail on

Если использует сетевые соединения к базе данных:

setsebool -P httpd_can_network_connect_db on

Перезапустите Apache:

systemctl restart httpd

Проверьте работу приложения. Если всё ещё есть проблемы, посмотрите свежие записи аудита:

ausearch -m AVC -ts recent -i

Возможно, потребуется дополнительный boolean или специфичный контекст для каких-то файлов.

Отладка в режиме permissive

Иногда удобно временно перевести SELinux в режим permissive для диагностики. В этом режиме блокировки не применяются, но записываются в лог. Проверить текущий режим:

getenforce

Переключить в permissive:

setenforce 0

Воспроизведите проблему, проверьте логи, примените нужные изменения контекстов и boolean. Затем верните enforcing:

setenforce 1

Эта настройка временная и сбросится при перезагрузке. Для постоянного изменения режима отредактируйте /etc/selinux/config:

SELINUX=permissive

Но делать это на продуктивных системах категорически не рекомендуется. Лучше потратить время на правильную настройку, чем жертвовать безопасностью.

Для отладки конкретного домена можно сделать только его permissive, не трогая остальную систему:

semanage permissive -a httpd_t

Вернуть обратно:

semanage permissive -d httpd_t

Этот подход безопаснее полного переключения режима, так как затрагивает только Apache.

Почему отключать SELinux - плохая идея

Многие администраторы при первых проблемах с SELinux идут простым путём: setenforce 0 или SELINUX=disabled в конфиге. Кажется, что это экономит время. На самом деле вы убираете критически важный слой защиты.

SELinux работает на уровне ядра и контролирует действия процессов независимо от прав UNIX. Даже если злоумышленник получит права root через уязвимость в веб-приложении, SELinux ограничит его возможности. Процесс httpd_t не сможет читать /etc/shadow, модифицировать системные файлы, запускать произвольные программы. Атакующий окажется в жёсткой песочнице.

Отключение SELinux открывает путь для lateral movement: взломав веб-сервер, злоумышленник легко перейдёт к атаке базы данных, почтового сервера, других служб. С активным SELinux такое невозможно или крайне затруднено.

Правильная настройка SELinux - это разовая работа при развёртывании приложения. Зато взамен вы получаете многократное повышение безопасности. Современные дистрибутивы предоставляют отличные инструменты для работы с политиками: semanage, restorecon, setsebool, audit2allow. Документация подробная, сообщество активное.

Если вы управляете серверами в облаке или датацентре, где реальная угроза атак, экономия нескольких часов на настройке SELinux может обернуться катастрофическими последствиями при успешной компрометации. Вложите время в изучение - и ваши серверы станут на порядок устойчивее к атакам.

Полезные команды для повседневной работы

Со временем работа с SELinux становится рутиной. Держите под рукой эти команды для быстрого решения типовых задач.

Просмотр контекста файлов:

ls -Z /path/to/file

Просмотр контекста процессов:

ps auxZ | grep httpd

Просмотр всех доступных типов для httpd:

seinfo -thttpd_t -x

Список всех портов и их типов:

semanage port -l

Список всех файловых контекстов:

semanage fcontext -l

Поиск в документации по ключевому слову:

apropos selinux | grep httpd

Справка по модулю httpd в формате man:

man httpd_selinux

Эти страницы содержат полный список типов, boolean, примеры использования и рекомендации разработчиков политик.

Проверка, какие типы может читать/писать процесс:

sesearch -s httpd_t -t httpd_sys_content_t -c file -p read -A

Полная перемаркировка файловой системы (требуется редко, например, после отключения и повторного включения SELinux):

touch /.autorelabel
reboot

При следующей загрузке система пройдёт по всей файловой системе и применит корректные контексты согласно политике. Это может занять значительное время на больших дисках.

Теперь вы знаете, что блокировки Apache со стороны SELinux - не ошибка и не баг, а признак работающей системы безопасности. Правильная маркировка файлов, грамотное использование boolean, добавление нестандартных портов в политику - все эти операции просты после первого раза. Зато взамен вы получаете сервер, защищённый на уровне ядра. Даже компрометация веб-приложения не даст атакующему полный контроль над системой. SELinux - это не враг администратора, а мощный союзник в борьбе за безопасность инфраструктуры.