⚙️ Паровой сервер ROADIT

SELinux для администратора: не враг, а шестерёнка в механизме безопасности

Когда вы в очередной раз видите в логах загадочное «Permission denied», хотя права 777 уже выданы, а владелец файла — ваш пользователь, наступает момент истины. Это не призрак, не проклятие и не сбой в матрице. Это SELinux — точный механизм, который работает как швейцарские часы, только латунные, с паровым приводом и без права на ошибку.

Многие администраторы воспринимают SELinux как врага: «выключить и забыть». Но это всё равно что снять предохранительный клапан с парового котла — пока давление в норме, всё работает, но однажды… В этой статье мы разберёмся, как приручить этот механизм и заставить его работать на вас.



⚙️ Содержание

Что такое SELinux и почему он пугает

SELinux (Security-Enhanced Linux) — это система принудительного контроля доступа (MAC, Mandatory Access Control), разработанная NSA и встроенная в ядро Linux. В отличие от традиционной модели DAC (Discretionary Access Control), где владелец файла решает, кто имеет доступ, SELinux работает как строгий диспетчер на железнодорожной станции: даже если у вас есть билет (права доступа), вы не попадёте на путь без разрешения диспетчера (политики SELinux).


Три режима работы: дроссель безопасности

SELinux может работать в трёх режимах, как паровой двигатель с регулируемым дросселем:

  • Enforcing (принудительный) — полная защита, нарушения блокируются и логируются. Это рабочий режим для продакшена.
  • Permissive (предупреждающий) — нарушения только логируются, но не блокируются. Идеален для отладки.
  • Disabled (отключён) — механизм остановлен. Как котёл без огня.

Проверка текущего режима:

# Проверяем статус SELinux
getenforce
# Подробная информация
sestatus

# Вывод:

# SELinux status:                 enabled
# SELinuxfs mount:                /sys/fs/selinux
# SELinux root directory:         /etc/selinux
# Loaded policy name:             targeted
# Current mode:                   enforcing
# Mode from config file:          enforcing
# Policy MLS status:              enabled
# Policy deny_unknown status:     allowed
# Memory protection checking:     actual (secure)
# Max kernel policy version:      33

Переключение режимов «на лету» (до перезагрузки):

# Включаем режим предупреждений для отладки
setenforce 0

# Возвращаем принудительный режим
setenforce 1

Для постоянного изменения отредактируйте конфигурационный файл:

# /etc/selinux/config
SELINUX=enforcing
# или
SELINUX=permissive
# или
SELINUX=disabled

⚡ Важно

Изменение в конфиге вступает в силу только после перезагрузки. Переключение между enforcing и permissive через setenforce работает мгновенно, но переход из disabled в enabled требует перезагрузки и может занять время из-за перемаркировки файлов.


Контексты безопасности: адресная система SELinux

Каждый файл, процесс и порт в системе с SELinux имеет контекст безопасности — своеобразный паспорт с несколькими полями. Контекст выглядит так:

user:role:type:level

На практике чаще всего используется поле type (тип), которое и определяет правила доступа.

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

# Показываем контексты файлов
ls -Z /var/www/html/

# Вывод:
# system_u:object_r:httpd_sys_content_t:s0 index.html
# system_u:object_r:httpd_sys_script_exec_t:s0 script.cgi

Здесь httpd_sys_content_t — это тип, который говорит SELinux: «это контент для веб-сервера, его можно читать процессу httpd».

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

# Контекст процесса Apache
ps -eZ | grep httpd

# Вывод:
# system_u:system_r:httpd_t:s0 1234 ? 00:00:01 httpd

Здесь httpd_t — тип процесса, который может взаимодействовать только с определёнными типами файлов (например, httpd_sys_content_t).


Изменение контекстов: chcon и restorecon

Иногда нужно изменить контекст файла. Для этого есть два инструмента:

chcon (change context) — меняет контекст вручную, но изменение может быть потеряно при перемаркировке:

# Меняем тип файла на httpd_sys_content_t
chcon -t httpd_sys_content_t /var/www/html/custom.html

# Рекурсивно меняем контекст директории
chcon -R -t httpd_sys_content_t /var/www/html/uploads

restorecon (restore context) — восстанавливает контекст согласно политике — это «сброс к заводским настройкам»:

# Восстанавливаем контекст файла
restorecon -v /var/www/html/custom.html

# Рекурсивно восстанавливаем всю директорию
restorecon -Rv /var/www/html/

# Вывод:
# Relabeled /var/www/html/custom.html from httpd_user_content_t to httpd_sys_content_t

Золотое правило: Если вы скопировали файл в директорию с особым контекстом, всегда запускайте restorecon для этой директории.


Булевы переменные: переключатели на панели управления

Булевы переменные SELinux — это флаги, которые включают или отключают определённые аспекты политики. Представьте их как рычаги на панели управления паровым механизмом: каждый рычаг отвечает за конкретную функцию.

Просмотр всех булевых переменных:

# Все булевы переменные
getsebool -a

# Фильтруем по интересующей теме (например, httpd)
getsebool -a | grep httpd

# Вывод:
# httpd_can_network_connect --> off
# httpd_can_sendmail --> off
# httpd_enable_cgi --> on
# httpd_read_user_content --> off

Поиск конкретной переменной:

# Ищем булевы, связанные с FTP
getsebool -a | grep ftp

# Вывод:
# allow_ftpd_anon_write --> off
# allow_ftpd_full_access --> off
# ftpd_enable_home_dirs --> on

Включение булевой переменной:

# Временно (до перезагрузки)
setsebool httpd_can_network_connect on

# Постоянно (сохраняется после перезагрузки)
setsebool -P httpd_can_network_connect on

# Выключение
setsebool -P httpd_can_network_connect off

Практические сценарии с булевыми переменными

Сценарий 1: Веб-сервер должен соединяться с базой данных

Если Apache или Nginx не могут подключиться к MySQL/PostgreSQL по сети:

# Разрешаем httpd инициировать сетевые соединения
setsebool -P httpd_can_network_connect on

# Для соединения с базой данных
setsebool -P httpd_can_network_connect_db on

Сценарий 2: FTP-сервер с доступом к домашним директориям

# Разрешаем FTP доступ к home директориям
setsebool -P ftpd_enable_home_dirs on

# Если нужен полный доступ (опасно!)
setsebool -P allow_ftpd_full_access on

Сценарий 3: Nginx читает пользовательский контент

# Разрешаем веб-серверу читать пользовательские файлы
setsebool -P httpd_read_user_content on

Диагностика нарушений: читаем журналы как детектив

Когда SELinux блокирует доступ, он оставляет следы в логах. Ваша задача — научиться их читать.

audit.log: чёрный ящик SELinux

Все нарушения записываются в /var/log/audit/audit.log:

# Смотрим последние нарушения
tail -f /var/log/audit/audit.log

# Фильтруем только сообщения SELinux
grep "AVC" /var/log/audit/audit.log | tail -20

# Вывод типичного нарушения:
# type=AVC msg=audit(1704123456.789:123): avc:  denied  { read } for  pid=1234 comm="httpd" name="index.html" dev="sda1" ino=56789 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0

Разберём это сообщение:

  • denied { read } — запрещено чтение
  • comm=”httpd” — процесс httpd
  • name=”index.html” — файл index.html
  • scontext=…:httpd_t:s0 — контекст источника (процесса)
  • tcontext=…:user_home_t:s0 — контекст цели (файла в домашней директории)
  • tclass=file — класс объекта: файл

Проблема: httpd пытается прочитать файл из домашней директории (user_home_t), что запрещено политикой.

Решение: Переместить файл в правильную директорию с контекстом httpd_sys_content_t или изменить булеву переменную.

ausearch: поиск по журналу

Утилита ausearch позволяет удобно искать нарушения:

# Ищем нарушения за последние 2 часа
ausearch -ts recent

# Ищем нарушения для конкретного процесса
ausearch -m AVC -c httpd

# Ищем нарушения за конкретный период
ausearch -ts today -te now

# Экспорт в читаемый формат
ausearch -m AVC --start today | audit2why

audit2why и audit2allow: переводим с языка SELinux

Эти утилиты помогают понять причину нарушения и создать правило:

# Объясняем причину нарушения
cat /var/log/audit/audit.log | audit2why

# Вывод:
# type=AVC msg=audit(1704123456.789:123): avc:  denied  { read } for ...
#       Allow httpd to read user_home_t files.
#       Boolean allow_httpd_read_user_content is currently off.
#       Try: setsebool -P allow_httpd_read_user_content on

# Генерируем модуль политики для разрешения
cat /var/log/audit/audit.log | audit2allow -M mypol

# Создаётся два файла:
# mypol.te  — исходный код правила
# mypol.pp  — скомпилированный модуль

# Устанавливаем модуль
semodule -i mypol.pp

⚡ Важно

Используйте audit2allow с осторожностью! Слепое применение сгенерированных правил может открыть дыры в безопасности. Всегда анализируйте, что именно вы разрешаете.


Практические сценарии: от теории к работе механизмов

Сценарий 1: Развёртывание веб-приложения

Вы скопировали файлы сайта в /var/www/html, но Apache выдаёт 403 Forbidden.

Диагностика:

# Проверяем контексты
ls -Z /var/www/html/

# Видим:
# unconfined_u:object_r:default_t:s0 index.html

# Проверяем логи
tail /var/log/audit/audit.log | grep httpd

Решение:

# Восстанавливаем контексты
restorecon -Rv /var/www/html/

# Или вручную устанавливаем правильный тип
chcon -R -t httpd_sys_content_t /var/www/html/

# Если приложение должно писать в директорию (загрузки)
chcon -R -t httpd_sys_rw_content_t /var/www/html/uploads

# Для скриптов
chcon -t httpd_sys_script_exec_t /var/www/html/cgi-bin/script.sh

Сценарий 2: Перенос домашней директории веб-приложения

Разработчик хочет хранить файлы приложения в /home/app/www вместо /var/www/html.

Проблема: SELinux не разрешит httpd читать файлы из home директории.

Решение 1 (правильное): Использовать стандартную директорию.

Решение 2 (через контексты):

# Создаём директорию
mkdir -p /home/app/www

# Устанавливаем контекст
chcon -R -t httpd_sys_content_t /home/app/www

# Делаем постоянное правило (fcontext)
semanage fcontext -a -t httpd_sys_content_t "/home/app/www(/.*)?"

# Применяем
restorecon -Rv /home/app/www

Решение 3 (через булеву переменную):

# Разрешаем httpd читать пользовательский контент
setsebool -P httpd_read_user_content on

Сценарий 3: MySQL и сетевые соединения

Приложение на PHP не может подключиться к MySQL.

Диагностика:

# Проверяем логи
ausearch -m AVC -c httpd | audit2why

# Вывод говорит: httpd пытается соединиться с портом 3306, но не может

Решение:

# Разрешаем httpd сетевые соединения
setsebool -P httpd_can_network_connect on

# Или более специфично — только к БД
setsebool -P httpd_can_network_connect_db on

Сценарий 4: Docker и SELinux

Docker может конфликтовать с SELinux при монтировании томов.

Проблема: Контейнер не может прочитать данные из хоста.

Решение:

# При запуске docker используйте суффикс :z или :Z
docker run -v /host/data:/container/data:z nginx

# :z — общий контент (может использоваться несколькими контейнерами)
# :Z — приватный контент (только для этого контейнера)

# Или отключите SELinux для конкретного контейнера (не рекомендуется)
docker run --security-opt label:disable nginx

Создание собственных модулей политики: для продвинутых механиков

Иногда стандартных булевых переменных и контекстов недостаточно. В этом случае можно создать собственный модуль политики.

Пример: кастомное приложение

Допустим, у вас есть приложение myapp, которое должно:

  • Читать файлы из /opt/myapp/data
  • Писать логи в /var/log/myapp
  • Слушать порт 8888

Шаг 1: Создаём контексты

# Определяем контексты для файлов
semanage fcontext -a -t myapp_data_t "/opt/myapp/data(/.*)?"
semanage fcontext -a -t myapp_log_t "/var/log/myapp(/.*)?"

# Применяем
restorecon -Rv /opt/myapp/data
restorecon -Rv /var/log/myapp

Шаг 2: Создаём модуль политики

# Создаём файл с правилами
cat > myapp.te << 'EOF'
policy_module(myapp, 1.0.0)

# Типы
type myapp_t;
type myapp_exec_t;
type myapp_data_t;
type myapp_log_t;

# Инициализация домена
init_daemon_domain(myapp_t, myapp_exec_t)

# Разрешаем читать данные
allow myapp_t myapp_data_t:file { read open getattr };
allow myapp_t myapp_data_t:dir { read open search };

# Разрешаем писать логи
allow myapp_t myapp_log_t:file { create write append open getattr };
allow myapp_t myapp_log_t:dir { add_name write search };

# Сетевой доступ
allow myapp_t self:tcp_socket { create bind listen accept };
EOF

# Компилируем
checkmodule -M -m -o myapp.mod myapp.te

# Создаём пакет
semodule_package -o myapp.pp -m myapp.mod

# Устанавливаем
semodule -i myapp.pp

Шаг 3: Добавляем порт

# Разрешаем приложению слушать порт 8888
semanage port -a -t myapp_port_t -p tcp 8888

Отладка в режиме Permissive: безопасный полигон

Прежде чем разворачивать приложение в продакшене с SELinux в режиме Enforcing, протестируйте его в Permissive.

# Включаем режим предупреждений
setenforce 0

# Запускаем приложение, выполняем все сценарии использования
# SELinux будет логировать нарушения, но не блокировать их

# Анализируем логи
ausearch -m AVC -ts today | audit2why

# Создаём необходимые правила
ausearch -m AVC -ts today | audit2allow -M myapp_policy
semodule -i myapp_policy.pp

# Возвращаем принудительный режим
setenforce 1

Этот подход позволяет «обучить» SELinux работе с вашим приложением без простоев.

Чек-лист администратора по SELinux

  • Никогда не отключайте SELinux полностью без крайней необходимости. Используйте Permissive для отладки.
  • Всегда используйте restorecon после копирования файлов в системные директории.
  • Проверяйте логи /var/log/audit/audit.log при любых странных «Permission denied».
  • Используйте audit2why перед применением audit2allow.
  • Документируйте изменения в политике — через полгода вы не вспомните, зачем создали то правило.
  • Тестируйте в Permissive перед развёртыванием в Enforcing.
  • Используйте стандартные директории (/var/www, /var/lib) — для них уже настроены контексты.

Заключение: SELinux — ваш союзник

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

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



⚙️ Машинное отделение ROADIT благодарит за прочтение.
Больше команд, шпаргалок и обзоров — на roadit.ru и в нашем Телеграф-канале.
📋 Все команды


Оставьте комментарий