ADG Оставить заявку
Блог Информационная безопасность 5 мин чтения

SELinux enforcing на CentOS 7: audit2allow и кастомные политики вместо setenforce 0

Переход на CentOS 7 - SELinux enforcing ломает несколько сервисов. Разбираемся с audit2allow, пишем custom policy modules и объясняем, почему отключать не вариант.

Контекст момента

RHEL 7 / CentOS 7 переводит SELinux enforcing в дефолт, что требует новых знаний от администраторов при переходе с шестёрки

Когда переводили первые серверы на CentOS 7, systemd забрал всё наше внимание. Unit-файлы, journalctl, firewalld - это было ново и бросалось в глаза. SELinux при этом как-то остался на периферии. Он же был и на шестёрке, привыкли. Казалось, ничего принципиально нового.

Оказалось, принципиально новое там было - просто другого рода. Не в самом SELinux, а в том, что на CentOS 7 он enforcing по умолчанию, и Red Hat явно сигнализирует: теперь это не рекомендация, а базовая позиция. А значит, чинить «включи и забудь» не получается - надо разбираться.

Где сломалось

Первый звонок поступил от одного из клиентов на сопровождении через несколько дней после перехода на CentOS 7. Веб-приложение на базе nginx + php-fpm отказывалось читать загруженные пользователями файлы из нестандартной директории. nginx отдавал 502, php-fpm в логах молчал, права на директорию были выставлены правильно.

tail -f /var/log/audit/audit.log показал сразу:

type=AVC msg=audit(1427720481.123:456): avc:  denied  { read } for  pid=12345 comm="php-fpm" name="uploads" dev="sda1" ino=789012 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:var_t:s0 tclass=dir

SELinux заблокировал доступ php-fpm к директории, потому что её контекст - var_t, а httpd-процессы по умолчанию имеют право читать только httpd_sys_content_t и несколько смежных типов. Логично с точки зрения MAC-модели. Неудобно с точки зрения того, что директория создавалась скриптом деплоя без учёта SELinux-контекстов.

Решение в этом случае простое - chcon -R -t httpd_sys_content_t /var/www/uploads или, лучше, прописать через semanage fcontext, чтобы контекст переживал restorecon:

semanage fcontext -a -t httpd_sys_content_t "/var/www/uploads(/.*)?"
restorecon -Rv /var/www/uploads

Но это был простой случай - типовой контекст, документированный в man-странице httpd_selinux. Дальше пошло интереснее.

Когда готового контекста нет

Второй случай оказался сложнее. Java-демон собственной разработки клиента пытался открыть сокет на нестандартном порту и писать в /opt/app/run/. Обе операции SELinux блокировал. Готового типа для такого приложения в системной политике не было.

Тут пришёл черёд audit2allow. Инструмент входит в пакет policycoreutils-python, на CentOS 7 надо доставить отдельно:

yum install policycoreutils-python

Базовый сценарий: берём отказы из audit.log, скармливаем audit2allow, получаем текст правила.

grep AVC /var/log/audit/audit.log | audit2allow -m myapp

Вывод - это .te-файл (Type Enforcement), примерно такой:

module myapp 1.0;

require {
    type httpd_t;
    type var_t;
    class dir { read open };
}

#============= httpd_t ==============
allow httpd_t var_t:dir { read open };

Дальше компилируем в бинарный модуль и загружаем:

audit2allow -M myapp < /var/log/audit/audit.log
semodule -i myapp.pp

semodule -i загружает модуль немедленно, без перезагрузки. Сервис подняли, проверили - работает.

Почему нельзя просто брать вывод audit2allow напрямую

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

Поэтому перед semodule -i мы делаем несколько вещей:

  • Читаем .te-файл руками. Смотрим какие типы задействованы, какие классы и права. Если видим что-то неожиданное вроде execmem или ptrace - это повод задать вопрос, откуда это взялось.
  • Фильтруем audit.log по конкретному процессу. grep "comm=\"myapp\"" вместо grep AVC - так в политику не попадают случайные чужие отказы из лога.
  • Проверяем есть ли стандартный boolean. Перед написанием кастомного модуля смотрим getsebool -a | grep <что-то релевантное>. Иногда нужное уже есть, просто выключено. Например, httpd_can_network_connect_db включает доступ httpd к базе данных по сети - гораздо чище, чем писать правило самому.

Перманентный аудит

Ещё один практический момент: /var/log/audit/audit.log по умолчанию ротируется и может не сохранить отказы с первого запуска сервиса. Для отладки удобно временно поставить auditd на подробный режим, а ещё лучше - на время тестирования запускать ausearch -m avc -ts today вместо прямого grep. ausearch понимает форматы времени и умеет фильтровать по pid, comm, типу события.

Почему setenforce 0 - это не вариант

Каждый раз когда SELinux что-то блокирует, в комментариях или в чатах появляется совет «просто выключи SELinux». В RHEL/CentOS-мире это устойчивая традиция, идущая ещё с шестёрки - там SELinux тоже был включён по умолчанию, но в реальности большинство отключало при первых проблемах.

Мы этого не делаем. Причина не в принципиальности ради принципиальности. На серверах с персональными данными или финансовой информацией SELinux - это отдельный слой контроля доступа, который работает даже если приложение или веб-сервер скомпрометированы. Shellshock в прошлом году хорошо это показал: на системах с SELinux enforcing часть векторов эскалации просто не срабатывала, потому что MAC-политика запрещала нужные действия ещё до того, как до неё добирался exploit.

Написать точечный модуль политики занимает 20-40 минут. Выключить SELinux - 30 секунд. Но второе означает убрать защитный слой со всех процессов сразу, а не решить конкретную проблему конкретного сервиса.

Где мы сейчас

Кастомные политики для нескольких клиентских серверов уже лежат в репозитории как часть Ansible-роли: .te-файлы хранятся вместе с конфигами приложения, компиляция и загрузка модуля - шаг провизионирования. Это значит, что при пересборке сервера политика поднимается автоматически.

Покрыты не все приложения - часть сервисов досталась в наследство с конфигами, писавшимися при выключенном SELinux. Разбираемся по мере того, как серверы мигрируют на CentOS 7. Чем раньше начинаешь считать SELinux нормой, а не помехой - тем меньше работы потом.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.