Библиотека — Linux — безопасность: hardening, SELinux/AppArmor, аудит [X]
Шифр
linux-security-hardening
Разделы
linux security
Сверено
2 сентября — в этот день утверждения статьи сверялись с первоисточниками, а не правился текст
Объём
20 116 знаков · 16 разделов · чтения около 20 минут

Linux — безопасность: hardening, SELinux/AppArmor, аудит


Обзор

Модель безопасности Linux строится послойно: DAC (Discretionary Access Control — классические UNIX-права rwx) → capabilities (гранулярные привилегии вместо монолитного root) → MAC (Mandatory Access Control через LSM — SELinux/AppArmor) → seccomp (фильтрация системных вызовов) → audit (аудит событий). Каждый слой компенсирует слабости предыдущего. DAC недостаточен, потому что скомпрометированный root-процесс обходит все ограничения. MAC решает это: даже root подчиняется политике, определённой администратором.

В FreeBSD аналогичная послойная модель: DAC → MAC framework (аналог LSM) → capsicum (аналог seccomp) → securelevel (глобальное ограничение привилегий). Но реализации существенно отличаются.

DAC: классическая модель

DAC — владелец объекта (файла, директории) определяет права доступа. User/group/other × read/write/execute + SUID/SGID/sticky bit. ACL (Access Control Lists, setfacl/getfacl) расширяют модель, позволяя задать права для произвольных пользователей/групп.

Фундаментальная слабость DAC: если процесс получает привилегии root (через эксплойт, misconfiguration, SUID binary), он обходит все DAC-ограничения. Поэтому DAC — необходимый, но недостаточный уровень.

Capabilities: гранулярный root

Традиционно UNIX знает два уровня привилегий: обычный пользователь и root (UID 0). Capabilities (с ядра 2.2) разбивают привилегии root на ~40 дискретных capabilities:

CAP_NET_BIND_SERVICE — привязка к портам <1024. CAP_NET_RAW — raw sockets (ping, tcpdump). CAP_SYS_PTRACE — отладка процессов других пользователей. CAP_DAC_OVERRIDE — обход проверок прав доступа к файлам. CAP_SYS_ADMIN — "мусорная корзина" привилегий: mount, swapon, настройка hostname, ядерные namespace operations и десятки других. По сути — почти root.

Capabilities назначаются на три уровня: file capabilities (через setcap, хранятся в extended attributes — замена SUID), thread capabilities (наследуются/изменяются при exec()), ambient capabilities (с 4.3, позволяют передать capabilities дочерним процессам без file capabilities).

# Назначить capability на файл (вместо SUID root)
setcap cap_net_bind_service+ep /usr/bin/myserver

# Проверить capabilities файла
getcap /usr/bin/myserver

# Запустить процесс с ограниченными capabilities
# systemd: в unit-файле
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_RAW
AmbientCapabilities=CAP_NET_BIND_SERVICE

# Посмотреть capabilities процесса
cat /proc/<PID>/status | grep Cap
getpcaps <PID>

systemd интегрирован с capabilities: каждый сервис может декларировать CapabilityBoundingSet (максимум доступных) и AmbientCapabilities (переданных при exec). Вместе с User=, ProtectHome=, ProtectSystem= это формирует sandbox без SELinux/AppArmor (см. статью 2).

LSM: Linux Security Modules

LSM — фреймворк ядра, предоставляющий hooks (точки перехвата) для модулей безопасности. Hooks размещены во всех критических точках: открытие файла, exec(), создание сокета, изменение прав, ptrace и т.д. LSM hooks выполняются после DAC-проверок и могут дополнительно запретить операцию.

Текущие LSM в ядре (2026, по списку CONFIG_LSM в mainline): AppArmor, SELinux, Smack, TOMOYO, Yama, LoadPin, SafeSetID, Lockdown, Landlock, IPE (Integrity Policy Enforcement), BPF (LSM BPF с ядра 5.7). Major LSMs (SELinux, AppArmor, Smack) — exclusive: только один может быть active одновременно. Minor LSMs (Yama, Lockdown, Landlock, capabilities) могут работать параллельно с major.

SELinux

Разработан NSA, дефолт в RHEL/Fedora/CentOS. Label-based: каждый объект (файл, процесс, порт, сокет) имеет security context (метку) в формате user:role:type:level. Политика определяет, какие типы могут взаимодействовать.

# Контекст файла
ls -Z /var/www/html/index.html
# unconfined_u:object_r:httpd_sys_content_t:s0

# Контекст процесса
ps -eZ | grep httpd
# system_u:system_r:httpd_t:s0  /usr/sbin/httpd

Правило политики (упрощённо): allow httpd_t httpd_sys_content_t:file { read open getattr }; — процесс типа httpd_t может читать файлы типа httpd_sys_content_t.

Targeted policy (дефолт в RHEL) — конфайнит только определённые сервисы, остальные работают в unconfined_t. MLS/MCS — многоуровневая секретность (используется в government/military setups) и Multi-Category Security (изоляция контейнеров).

Режимы: Enforcing (политика применяется), Permissive (нарушения логируются, но не блокируются), Disabled (SELinux полностью отключён — не рекомендуется, переключение обратно требует relabeling всей ФС). Начиная с RHEL 9 третий режим через конфиг уже не работает как раньше: SELINUX=disabled в /etc/selinux/config оставляет подсистему включённой, но без загруженной политики, а для настоящего отключения нужен параметр ядра selinux=0.

getenforce                        # текущий режим
setenforce 0                      # переключить в Permissive (до перезагрузки)
# Постоянное изменение: /etc/selinux/config → SELINUX=enforcing

# Troubleshooting — главная причина «SELinux всё ломает»
ausearch -m avc -ts recent        # последние denial'ы из audit.log
audit2why < /var/log/audit/audit.log  # объяснить причину denial
audit2allow -a -M mypolicy        # сгенерировать policy module из denial'ов
semodule -i mypolicy.pp           # установить policy module

# Исправление контекстов файлов (частая проблема)
restorecon -Rv /var/www/html/     # восстановить дефолтные контексты
chcon -t httpd_sys_content_t file # изменить контекст вручную (не переживёт relabel)
semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"  # постоянное правило
restorecon -Rv /srv/www/

AppArmor

Дефолт в Ubuntu и Debian; SUSE от него ушла — Tumbleweed переключился на SELinux со снапшота 20250211 (февраль 2025), а SLE 16 (ноябрь 2025) и построенный на нём openSUSE Leap 16 ставятся с SELinux в enforcing, AppArmor в них остаётся опцией для ручного включения. Path-based: профили определяют, к каким путям файловой системы и с какими правами может обращаться программа.

# /etc/apparmor.d/usr.sbin.nginx
/usr/sbin/nginx {
    # Сеть
    network inet stream,
    network inet6 stream,

    # Файлы
    /etc/nginx/** r,
    /var/log/nginx/** rw,
    /var/www/** r,
    /run/nginx.pid rw,

    # Capabilities
    capability net_bind_service,
    capability setuid,
    capability setgid,

    # Запретить всё остальное (implicit deny)
}

Режимы: Enforce (блокировка + лог), Complain (только лог — аналог SELinux Permissive). aa-genprof запускает приложение в complain-mode и интерактивно генерирует профиль на основе наблюдаемого поведения.

aa-status                          # все загруженные профили и их режимы
aa-enforce /etc/apparmor.d/usr.sbin.nginx
aa-complain /etc/apparmor.d/usr.sbin.nginx
aa-logprof                         # обновить профиль по логам нарушений

SELinux vs AppArmor: архитектурный trade-off

SELinux — label-based (метки на inode): переименование файла не меняет его тип, hard link сохраняет контекст. Гранулярнее (type enforcement, role-based, MLS/MCS), но значительно сложнее в настройке и отладке. Типичная реакция администратора — setenforce 0 (отключить).

AppArmor — path-based: профили привязаны к путям. Проще писать и понимать, но если файл доступен по другому пути (hard link, bind mount), профиль может быть обойдён. Частично компенсируют это ядерные ограничения fs.protected_hardlinks и fs.protected_symlinks: они начинали жизнь в Yama, но давно переехали в общий код VFS, и systemd включает их своим sysctl.d/50-default.conf — сам Yama сегодня отвечает только за ptrace_scope.

Для контейнеров SELinux предоставляет MCS-изоляцию: каждый контейнер получает уникальную категорию (s0:c1,c2), что предотвращает доступ между контейнерами даже при escaping namespace isolation. Docker и Podman автоматически назначают MCS-метки.

seccomp: фильтрация системных вызовов

seccomp-bpf (с ядра 3.5) позволяет процессу установить BPF-фильтр, определяющий, какие системные вызовы разрешены. Фильтр — одностороннее ограничение: установленный, он не может быть снят или ослаблен.

seccomp дополняет LSM: LSM контролирует доступ к объектам ядра (файлы, сокеты), seccomp контролирует интерфейс между userspace и ядром (syscalls). Ограничение seccomp: BPF-фильтры не могут разыменовывать указатели, поэтому seccomp не может проверить содержимое аргументов-строк (путь файла в open()).

Docker применяет default seccomp-профиль, блокирующий ~44 опасных syscall'а (из ~300+): mount, reboot, clock_settime, init_module, ptrace и др. Для большинства приложений это прозрачно. Podman использует аналогичный дефолтный профиль.

systemd предоставляет SystemCallFilter= в unit-файлах: SystemCallFilter=@system-service (предопределённые группы syscall'ов).

Landlock: application-level sandboxing

Landlock (с ядра 5.13, стабилен с 5.19) — LSM, позволяющий непривилегированному процессу ограничить свой собственный доступ к файловой системе и сети. В отличие от SELinux/AppArmor (требуют root для загрузки политики), Landlock позволяет приложению самостоятельно sandbox'ить себя.

Концептуально Landlock ближе к FreeBSD Capsicum и OpenBSD pledge(): программа декларирует, что ей нужно (чтение /etc/config, запись в /tmp), а всё остальное запрещается ядром. С ядра 6.7 Landlock поддерживает и сетевые ограничения (bind/connect для TCP).

Возможности Landlock нумеруются собственной версией ABI, и ориентироваться нужно на неё, а не на версию ядра: ABI 6 (ядро 6.12) добавил изоляцию абстрактных unix-сокетов и сигналов, ABI 7 (6.15) — запись отказов в audit, ABI 8 (7.0) — флаг LANDLOCK_RESTRICT_SELF_TSYNC, ABI 10 (7.2) — UDP. Флаг TSYNC важнее остального: до него ruleset применялся только к вызывающему потоку и его потомкам, так что многопоточной программе приходилось ограничивать каждый поток отдельно.

auditd: аудит событий

Linux Audit Framework — подсистема ядра для записи security-relevant событий. auditd — демон, принимающий события от ядра и записывающий в /var/log/audit/audit.log.

# Правила аудита (/etc/audit/rules.d/)
# Мониторинг изменений в /etc/passwd
-w /etc/passwd -p wa -k identity

# Мониторинг exec() с UID 0
-a always,exit -F arch=b64 -S execve -F euid=0 -k root-commands

# Мониторинг загрузки модулей ядра
-a always,exit -F arch=b64 -S init_module,finit_module -k module-load

# Поиск по логам
ausearch -k identity                      # по ключу
ausearch -m USER_LOGIN --success no       # неудачные логины
aureport --auth                           # сводка аутентификации
aureport --summary                        # общая сводка

auditd критичен для compliance (PCI DSS, HIPAA, SOX). На серверах с высокой нагрузкой audit может генерировать значительный I/O; аудит-правила должны быть targeted, не blanket.

SSH hardening

SSH — основной вектор remote-доступа и частый target атак. Ключевые настройки (/etc/ssh/sshd_config):

PermitRootLogin no                    # запретить root login (использовать sudo)
PasswordAuthentication no             # только ключи
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
MaxAuthTries 3
AllowUsers deploy admin               # whitelist пользователей
LoginGraceTime 30
ClientAliveInterval 300               # timeout неактивных сессий
ClientAliveCountMax 2

fail2ban / sshguard — анализ логов и блокировка IP после N неудачных попыток. fail2ban работает через iptables/nftables jails; sshguard — более лёгкая альтернатива. На серверах с публичным SSH fail2ban — минимально необходимая мера.

Практика: уровни hardening

# 1. Проверка текущего состояния безопасности
getenforce                               # SELinux
aa-status                                # AppArmor
sysctl user.max_user_namespaces          # user namespaces, вентиль mainline
sysctl kernel.unprivileged_userns_clone  # downstream-патч Debian/Ubuntu
sysctl kernel.apparmor_restrict_unprivileged_userns  # Ubuntu 24.04+, по умолчанию 1
sysctl kernel.yama.ptrace_scope          # Yama ptrace restriction
cat /proc/sys/kernel/dmesg_restrict      # ограничение dmesg для non-root

# 2. Базовый hardening через sysctl
kernel.dmesg_restrict = 1                # скрыть kernel messages от non-root
kernel.kptr_restrict = 2                 # скрыть указатели ядра из /proc
kernel.yama.ptrace_scope = 1             # ptrace только для дочерних процессов
# Значения all, default и <iface> ядро комбинирует по-разному: rp_filter — как
# max(all, <iface>), redirects на узле без форвардинга — как OR, а default действует
# только на созданные позже интерфейсы; systemd вдобавок раздаёт rp_filter = 2 (loose)
net.ipv4.conf.all.rp_filter = 1          # reverse path filtering
net.ipv4.conf.*.rp_filter = 1            # glob понимает systemd-sysctl, но не procps
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.*.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.*.send_redirects = 0
kernel.unprivileged_bpf_disabled = 1     # запретить BPF для non-root
kernel.io_uring_disabled = 2             # отключить io_uring (attack surface)

# 3. Lockdown LSM (с ядра 5.4)
# integrity mode: запретить загрузку неподписанных модулей, /dev/mem доступ
# confidentiality mode: + скрыть ядерные данные из /proc
# Включается через kernel cmdline: lockdown=integrity

# 4. Проверка SUID-бинарей (минимизировать attack surface)
find / -perm -4000 -type f 2>/dev/null
find / -perm -2000 -type f 2>/dev/null

Нюансы и trade-off'ы

«Просто отключите SELinux» — антипаттерн. SELinux denial в 90% случаев указывает на реальную проблему: неправильный файловый контекст (решается restorecon), нестандартный порт (semanage port -a), или действительно нежелательное поведение приложения. audit2why объясняет причину человеческим языком. Отключение SELinux убирает критический слой защиты, особенно важный для контейнеров (MCS isolation).

AppArmor path-based ≠ слабый. Path-based подход имеет реальную слабость (hard links, bind mounts), но для большинства серверных сценариев (confined daemons) этого достаточно. Docker на Ubuntu использует AppArmor-профили, и они эффективно ограничивают контейнеры.

Capabilities vs полный root. На практике многие приложения запрашивают CAP_SYS_ADMIN, который даёт почти полный root. Это «мусорная capability» — разделение неполное. Работа по разделению CAP_SYS_ADMIN на более гранулярные capabilities продолжается, но медленно из-за backward compatibility.

seccomp overhead. Каждый системный вызов проходит через BPF-фильтр. Overhead зависит от сложности фильтра: для дефолтного Docker-профиля (~44 rules) — менее 1%. Для сложных кастомных профилей может быть заметнее. В contrast, LSM hooks имеют фиксированный overhead (вызов функции на каждом hook point), который может достигать единиц процентов для SELinux с полной политикой.

io_uring как attack surface. io_uring — высокопроизводительный I/O-интерфейс ядра, добавленный в 5.1. Из-за сложности реализации он стал постоянным источником уязвимостей (десятки CVE). Google и другие крупные пользователи рекомендуют отключать io_uring на серверах, где он не нужен: kernel.io_uring_disabled=2.

LSM BPF (с ядра 5.7) — новое направление: eBPF-программы, подключаемые к LSM hooks, позволяющие реализовать кастомные MAC-политики без написания kernel module. Более гибко, чем SELinux/AppArmor, с меньшим overhead (~15% в worst case vs ~110% для AppArmor в некоторых бенчмарках). В production используется в Cilium, Android, ChromeOS.

Сравнение с FreeBSD

MAC Framework. FreeBSD имеет собственный MAC framework (аналог LSM), с модулями: mac_bsdextended (файловый firewall на правилах), mac_portacl (привязка к портам без root), mac_seeotheruids (скрытие процессов других пользователей). Менее развит, чем SELinux/AppArmor — нет эквивалента targeted policy для каждого сервиса. Реальное использование MAC в FreeBSD значительно реже, чем SELinux в RHEL.

Securelevel. kern.securelevel — глобальный уровень безопасности, который можно только повышать: на уровне 1 запрещается загрузка модулей и запись в /dev/mem, на уровне 2 — изменение правил firewall и запись в блочные устройства. Простой и эффективный механизм для production-серверов. В Linux аналог — Lockdown LSM (integrity/confidentiality modes), появившийся значительно позже (ядро 5.4).

Capsicum. Capability-based sandbox framework — процесс входит в capability mode и теряет доступ к глобальному namespace (файловые пути, сетевые адреса). Доступ только через явно переданные file descriptors с ограниченными правами. Концептуально чище, чем seccomp (который фильтрует syscalls, а не объекты). В Linux аналогом является Landlock + seccomp, но capsicum элегантнее архитектурно.

chflags. FreeBSD файловые флаги (schg — system immutable, sunlnk — undeletable) — в сочетании с securelevel делают файлы неизменяемыми даже для root. В Linux аналог — chattr +i (immutable), но без securelevel root может снять этот флаг.

blacklistd. FreeBSD-аналог fail2ban, встроенный в base system. Интегрирован с sshd, ftpd, smtpd — при N неудачных попыток IP автоматически блокируется через PF. Проще и надёжнее, чем парсинг логов fail2ban.

Источники


Связанные заметки

Из библиотеки института уносят только знания. Читателей здесь не считают: посещение не регистрируется, отметок в издании не остаётся, счётчиков нет ни у одного издания фонда.

Издание пополняется и пересверяется. Текст доступен на условиях CC BY-SA 4.0 — унесённое можно передавать дальше на тех же условиях.