SELinux на AlmaLinux без отключения Enforcing: генерация policy-модуля по audit.log и отладка контекста файлов

SELinux на AlmaLinux без отключения Enforcing: генерация policy-модуля по audit.log и отладка контекста файлов

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

Читать статью  Оптимальный выбор видеокарты для AMD FX 4350

Команда (с начала дня):
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-сокетом стороннего сервиса.

Читать статью  Как выбрать идеальный сервер Lineage 2 Essence

Пример отказа для 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, собрав вместе отказы от разных сервисов. В результате модуль получается слишком широким и труднообъяснимым.

Практичный подход:

  1. Воспроизвести проблему один-два раза (запрос к приложению, вызов функции, подключение к сокету)
  2. Сразу после этого выбрать события за последние минуты и по возможности отфильтровать по процессу

Команда (пример, сырой формат для 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

Читать статью  Идеальная видеокарта для процессора AMD Ryzen 5000

Команда (проверка, что модуль загружен):
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

  1. Подтвердить режим: getenforce должен показывать Enforcing
  2. Воспроизвести проблему и сразу собрать AVC: ausearch -m avc -ts recent
  3. Прочитать scontext/tcontext/tclass и понять, это контекст или политика
  4. Для файлов и каталогов в нестандартных путях сначала настроить semanage fcontext и применить restorecon
  5. Если контекст корректен, проверить audit2why и возможные boolean
  6. Если boolean не подходит – сгенерировать модуль точечно по свежим AVC: audit2allow -M, затем обязательно просмотреть .te
  7. Установить модуль через semodule -i, протестировать и контролировать audit.log

Такой порядок действий позволяет поддерживать SELinux в рабочем состоянии даже на типичных «быстрых» площадках вроде VPS/VDS, где сервисы часто разворачиваются из готовых инструкций. Enforcing при этом остается включенным, а изменения сохраняются управляемыми: контексты – через fcontext, разрешения – через минимальный локальный policy-модуль.