Помню свой первый случай с 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 - это не враг администратора, а мощный союзник в борьбе за безопасность инфраструктуры.