SELinux в AlmaLinux часто воспринимается как источник «необъяснимых» отказов, особенно при развертывании сервисов на виртуальном сервере (VPS/VDS), где у администратора есть соблазн быстро перевести систему в Permissive или отключить SELinux полностью. В реальных внедрениях такой подход обычно приводит к накоплению скрытых рисков: права доступа остаются расплывчатыми, а дальнейшая миграция на более строгую политику становится сложнее.
Ниже описан один практический сценарий: веб-приложение перенесено в нестандартный каталог /srv, при этом часть логики приложения работает через Unix-сокет локального сервиса. При включенном SELinux Enforcing сервисы выглядят исправными по Unix-пермиссиям, но запросы завершаются ошибками, а в журналах встречается «Permission denied». Задача решается без отключения Enforcing: сначала корректируются контексты файлов, затем для оставшихся отказов генерируется локальный policy-модуль по данным /var/log/audit/audit.log.
Сценарий и типовые симптомы
Условия, которые воспроизводятся на практике довольно часто:
- Nginx или Apache работает в SELinux-домене httpd_t (для nginx в AlmaLinux это тоже типично)
- Документ-рут и часть данных приложения находятся в /srv/www/app (не в стандартных /var/www)
- Приложение (PHP-FPM, uWSGI, Gunicorn или иной backend) обращается к локальному сервису через Unix-сокет, например /run/service/service.sock или /var/run/…
- SELinux остается в Enforcing
Проявления:
- Статика отдается с ошибками 403/404, хотя права на файлы корректны
- Backend видит «Permission denied» при записи в каталоги (uploads/cache) внутри /srv
- Подключение к Unix-сокету падает, хотя сокет существует и доступен по обычным правам
Ключевая идея: SELinux отказывает не из-за «плохих» Unix-прав, а из-за несоответствия контекстов (labels) и/или отсутствия разрешающего правила в политике.
Подготовка AlmaLinux: пакеты и проверки
Проверка режима SELinux
Команда:
getenforce
Ожидаемое значение для этого сценария: Enforcing.
Команда:
sestatus
Полезно убедиться, что включен именно targeted (типовой режим для AlmaLinux), и что политика загружена корректно.
Инструменты для анализа audit.log и сборки модуля
Для работы потребуются утилиты из стандартных репозиториев AlmaLinux:
Команда:
dnf install -y policycoreutils-python-utils setools-console setroubleshoot-server setroubleshoot-plugins audit checkpolicy
- policycoreutils-python-utils – semanage, audit2allow, audit2why
- audit – ausearch и базовая инфраструктура auditd
- setroubleshoot – sealert и человекочитаемые подсказки
- checkpolicy – компиляция policy-модулей, если используется audit2allow -M
- setools-console – sesearch/seinfo для проверки, что именно разрешено политикой
Команда:
systemctl enable —now auditd
Если сервер – арендованный VPS/VDS, auditd почти всегда уже включен, но на минимальных образах встречаются исключения.
Для тестового стенда (где удобно воспроизводить AVC и «доводить» модуль без риска) обычно поднимается отдельный виртуальный сервер. AlmaLinux доступен у большинства провайдеров аренды VPS; в качестве примера площадки встречается VPS.house (география размещения, включая Москву, на ход разбора SELinux не влияет).
Как быстро извлечь нужные AVC из /var/log/audit/audit.log
SELinux-отказы фиксируются в audit.log как события типа AVC. Основная задача – отделить релевантные отказы от «шума» и привязать их к конкретной проблеме.
Фильтрация по времени
Команда (последние минуты):
ausearch -m avc -ts recent
Команда (с начала дня):
ausearch -m avc -ts today
Для точного среза полезно сначала перезапустить сервис или повторить запрос к приложению, затем сразу смотреть события «recent».
Фильтрация по процессу (comm)
Если известно, какой процесс упирается в SELinux (nginx, php-fpm, uwsgi и т. п.), удобна фильтрация по имени команды:
Команда:
ausearch -m avc -ts recent -c nginx
Команда:
ausearch -m avc -ts recent -c php-fpm
Чтение ключевых полей AVC
В типичной AVC-записи важны:
- scontext – контекст источника (домен процесса), например system_u:system_r:httpd_t:s0
- tcontext – контекст цели (тип файла/сокета/директории), например system_u:object_r:default_t:s0
- tclass – класс объекта: file, dir, sock_file, unix_stream_socket, tcp_socket и т. д
- denied { … } – конкретное запрещенное действие: read, write, getattr, connectto и т. п
Почему audit2why полезнее «угадывания»
Команда:
ausearch -m avc -ts recent —raw | audit2why
audit2why иногда подсказывает, что проблема решается включением boolean (например, для сетевых подключений веб-сервиса), а не сборкой модуля. Это важная развилка: локальный модуль стоит использовать только там, где boolean и корректные контексты проблему не закрывают.
Шаг 1. Отладка контекста файлов: /srv и корректные типы для httpd
При переносе проекта в /srv чаще всего ломается разметка SELinux-контекстов. По умолчанию файлы в нестандартных путях нередко получают тип default_t или другой «неподходящий» тип, из-за чего домен httpd_t не может ни читать статику, ни писать в каталоги приложения.
Проверка текущего контекста
Команда:
ls -laZ /srv/www/app
Команда (контекст только у директории):
ls -ldZ /srv/www/app
Если у файлов и директорий виден default_t, это почти всегда признак неверного fcontext для веб-контента.
Проверка «ожидаемого» контекста
Команда:
matchpathcon /srv/www/app/public/index.html
Если matchpathcon показывает тип, отличный от фактического, контексты потребуется восстановить или задать правила для нестандартного пути.
Почему chcon – временная мера
chcon меняет контекст здесь и сейчас, но изменения легко потерять после restorecon, обновления пакетов или пересоздания файлов. Для постоянного решения применяется связка semanage fcontext + restorecon.
Назначение контекста для статики и для каталогов записи
Для веб-приложения обычно нужны как минимум два типа:
- httpd_sys_content_t – контент «только чтение» (public, assets)
- httpd_sys_rw_content_t – каталоги, куда требуется запись (uploads, cache, storage)
Команды (пример для /srv/www/app):
semanage fcontext -a -t httpd_sys_content_t «/srv/www/app/public(/.*)?»
semanage fcontext -a -t httpd_sys_rw_content_t «/srv/www/app/storage(/.*)?»
Далее контексты применяются к уже существующим файлам:
Команда:
restorecon -Rv /srv/www/app
Проверка результата:
Команда:
ls -laZ /srv/www/app/public | head
Как посмотреть все локальные изменения fcontext
В production-системах удобно иметь быстрый список того, что было добавлено вручную:
Команда:
semanage fcontext -C -l
Если правило оказалось ошибочным, оно удаляется так:
Команда (пример):
semanage fcontext -d «/srv/www/app/public(/.*)?»
Шаг 2. Когда контексты уже исправлены, но SELinux все равно блокирует: генерация policy-модуля
После приведения контекстов в порядок веб-приложение обычно начинает корректно читать/писать файлы в /srv. Остаются случаи, где политика действительно не разрешает нужное действие. Типичный пример – взаимодействие домена httpd_t с Unix-сокетом стороннего сервиса.
Пример отказа для Unix-сокета (формат AVC)
Ниже приведен пример строки (как образец), на которую стоит ориентироваться при поиске:
avc: denied { connectto } for pid=1234 comm=«php-fpm» path=«/run/service/service.sock» scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:service_var_run_t:s0 tclass=unix_stream_socket
Из записи видно:
- Источник – httpd_t (веб-стек)
- Цель – сокет с типом service_var_run_t (условное имя типа целевого сервиса)
- Запрещенное действие – connectto (подключение к Unix-сокету)
Выбор подхода: что лучше, чем сразу писать модуль
Перед сборкой локального policy-модуля полезно проверить альтернативы:
- SELinux boolean. Если отказ связан с сетевыми подключениями (tcp_socket), часто достаточно включить соответствующий boolean для httpd. Плюс – поддерживаемый механизм; минус – иногда слишком широкое разрешение
- Правильный тип для объекта. Если сокет или каталог создан в нестандартном месте и получил неверный тип, иногда достаточно настроить fcontext для пути и выполнить restorecon. Плюс – без модуля; минус – подходит не для всех типов взаимодействия
- Локальный policy-модуль. Используется, когда действие легитимно, но политика targeted его не разрешает, а исправление контекста не применимо или нарушает смысл маркировки. Плюс – точечное разрешение; минус – модуль нужно сопровождать
В этом сценарии применяется третий вариант: генерация policy-модуля на основе конкретных AVC.
Сбор «сырья» для audit2allow: важно не подмешать лишнее
Одна из типовых ошибок – кормить audit2allow большим фрагментом audit.log, собрав вместе отказы от разных сервисов. В результате модуль получается слишком широким и труднообъяснимым.
Практичный подход:
- Воспроизвести проблему один-два раза (запрос к приложению, вызов функции, подключение к сокету)
- Сразу после этого выбрать события за последние минуты и по возможности отфильтровать по процессу
Команда (пример, сырой формат для audit2allow):
ausearch -m avc -ts recent —raw -c php-fpm
Генерация policy-модуля из audit.log
Команда (создание модуля «local-httpd-socket»):
ausearch -m avc -ts recent —raw -c php-fpm | audit2allow -M local-httpd-socket
Результат – два файла в текущем каталоге:
- local-httpd-socket.te – текстовое описание правил
- local-httpd-socket.pp – скомпилированный модуль для установки
Критически важный шаг: просмотр .te перед установкой
audit2allow генерирует правила механически: «разрешить то, что было запрещено». Это удобно как стартовая точка, но небезопасно как финальная.
Минимальная проверка:
- Есть ли в .te только относящиеся к задаче домены и типы (например, httpd_t и нужный *_var_run_t)?
- Не появились ли разрешения на dir write, file write или доступ к неожиданным типам вроде default_t там, где ожидался узкий доступ к сокету?
- Не попали ли в выборку AVC, связанные с другими действиями (чтение чужих конфигов, доступ к домашним каталогам и т. п.)?
Команда:
cat local-httpd-socket.te
Установка и откат модуля
Команда (установка):
semodule -i local-httpd-socket.pp
Команда (проверка, что модуль загружен):
semodule -l | grep local-httpd-socket
Команда (удаление/откат):
semodule -r local-httpd-socket
Для многих production-окружений (включая аренду VPS и аренду VDS) практично сначала обкатывать модуль на отдельном тестовом инстансе AlmaLinux, чтобы не превращать боевой audit.log в «генератор» лишних разрешений. В качестве примера может использоваться виртуальный сервер AlmaLinux для проверки SELinux, но принцип одинаков для любого провайдера.
Проверка результата: как убедиться, что разрешение стало точечным
Повторный запуск сценария и контроль audit.log
После установки модуля выполняется повтор действия, которое ранее приводило к отказу. Затем проверяются новые AVC:
Команда:
ausearch -m avc -ts recent
Если появляются новые отказы, важно снова разделить их на две категории:
- ошибка контекста (лечится fcontext/restorecon)
- реально недостающее разрешение (возможен модуль, но только после проверки альтернатив)
Снятие «dontaudit» для диагностики (аккуратно)
Часть политик скрывает некоторые отказы через dontaudit. В редких случаях это мешает диагностике. Тогда временно включается отладочный режим:
Команда:
semodule -DB
После диагностики режим обязательно отключается, иначе audit.log начнет быстро расти:
Команда:
semodule -B
Проверка правил через sesearch
Если установлен пакет setools-console, можно проверить наличие конкретного разрешения в итоговой политике:
Команда (пример):
sesearch -A -s httpd_t -c unix_stream_socket | grep connectto
Точный запрос зависит от типа цели (tcontext), который фигурирует в AVC.
Типовые ошибки, из-за которых policy-модуль «починил все и сразу» (и это плохо)
- Сборка модуля из «старого» audit.log. В выборку попадают отказы месячной давности и случайные события, модуль расширяется без понимания причин
- Попытка лечить контекст политикой. Если в отказах фигурирует default_t для веб-контента в /srv, модуль лишь закрепит неправильную маркировку. Правильнее сначала починить fcontext
- Игнорирование boolean. Для сетевых операций веб-стека boolean часто безопаснее и понятнее, чем локальный модуль с десятками правил
- Отсутствие сопровождения. Локальные модули стоит хранить вместе с конфигурацией (репозиторий/Ansible), иначе через год невозможно объяснить, почему в системе разрешено то или иное действие
Короткий чек-лист: что делать, если SELinux мешает сервису в Enforcing
- Подтвердить режим: getenforce должен показывать Enforcing
- Воспроизвести проблему и сразу собрать AVC: ausearch -m avc -ts recent
- Прочитать scontext/tcontext/tclass и понять, это контекст или политика
- Для файлов и каталогов в нестандартных путях сначала настроить semanage fcontext и применить restorecon
- Если контекст корректен, проверить audit2why и возможные boolean
- Если boolean не подходит – сгенерировать модуль точечно по свежим AVC: audit2allow -M, затем обязательно просмотреть .te
- Установить модуль через semodule -i, протестировать и контролировать audit.log
Такой порядок действий позволяет поддерживать SELinux в рабочем состоянии даже на типичных «быстрых» площадках вроде VPS/VDS, где сервисы часто разворачиваются из готовых инструкций. Enforcing при этом остается включенным, а изменения сохраняются управляемыми: контексты – через fcontext, разрешения – через минимальный локальный policy-модуль.