AppArmor: изоляция сервисов в Linux — АйТи Фреш
Москва, Щёлковское шоссе, д. 92, корп. 7 · Пн–Пт 9:00–19:00 · +7 903 729-62-41
· 14 мин чтения

AppArmor в Linux: песочница для сервисов в 50 строк профиля

AppArmor в Linux: песочница для сервисов в 50 строк профиля

Если на домашнем сервере ещё можно махнуть рукой на LSM-подсистему ядра, то на продакшене такая роскошь недопустима. В нашей практике AppArmor каждый день защищает сервисы клиентов: блокирует последствия RCE в PHP-библиотеке, не пропускает shell-инъекции в webhook-скриптах и не даёт «убежать» за пределы Docker-контейнеров через их volume. Дальше расскажу, как мы пишем и сопровождаем AppArmor-профили, чтобы сервер не превратился в неотлаживаемую крепость.

Mandatory Access Control простыми словами

Классические права Linux (rwx+owner+group) — это DAC, дискреционное управление. Владелец решает, кому можно. MAC устроен иначе: политику назначает система, и даже root не может её обойти. AppArmor — одна из реализаций LSM-хуков, которая говорит ядру: «процесс nginx может читать /etc/nginx/, писать только в /var/log/nginx/, открывать только 80/443 — остальное блокировать». Если злоумышленник пролез через CVE в PHP-FPM, AppArmor не даст ему прочитать /etc/shadow, даже если пользователь www-data получит временный root.

Где AppArmor уже работает и где его надо включать

Кстати, приятный момент: в Ubuntu Server и Debian (версии 11/12) AppArmor уже стоит по умолчанию. Он даже включен для nginx, mysql, cups, tcpdump и ещё пары десятков других пакетов. Хотите убедиться сами? Вот как проверить его состояние:

sudo aa-status
# 34 profiles are loaded.
# 22 profiles are in enforce mode.
#    /usr/sbin/cupsd
#    /usr/sbin/nscd
#    ...
# 12 profiles are in complain mode.
# 8 processes have profiles defined.

А если у вас RHEL-семейство, например, AlmaLinux или Rocky? Там уже свой "сторож" – SELinux. Мы настоятельно не советуем ставить туда AppArmor. Почему? Всё очень просто: две MAC-подсистемы в одном ядре – это рецепт для катастрофы, они просто не уживаются. Так что выбирайте, что вам ближе по духу.

Анатомия профиля

Профиль — текстовый файл в /etc/apparmor.d/. Читается легко, даже если не знаешь синтаксиса. Вот пример для самописного Python-демона invoicer:

# /etc/apparmor.d/opt.invoicer.bin.invoicer
#include <tunables/global>

@{INVOICER_HOME} = /opt/invoicer
@{INVOICER_LOG}  = /var/log/invoicer

/opt/invoicer/bin/invoicer {
  #include <abstractions/base>
  #include <abstractions/python>
  #include <abstractions/nameservice>

  capability net_bind_service,

  network tcp,
  network udp,

  @{INVOICER_HOME}/**          r,
  @{INVOICER_HOME}/bin/**      mrix,
  @{INVOICER_HOME}/var/**      rwk,
  @{INVOICER_LOG}/**           rw,

  /etc/invoicer/config.yaml    r,
  /etc/ssl/certs/**            r,

  /proc/*/stat                 r,
  /proc/*/status               r,
  /sys/kernel/mm/transparent_hugepage/enabled r,

  deny @{HOME}/**              rwkx,
  deny /etc/shadow             r,
  deny /root/**                rwkx,
}

Суть: демону разрешено делать только то, что явно перечислено. Всё остальное — запрет. Правила deny здесь декоративные (итак неявно запрещено), но я их пишу специально — чтобы в audit-логе видеть попытки, даже если кто-то потом случайно расширит профиль.

aa-genprof: автогенерация под реальную нагрузку

Писать профиль с нуля — путь долгий. Я делаю так: запускаю приложение в learning-режиме, выполняю типовые сценарии (старт, обработка запроса, бэкап), и утилита aa-genprof сама строит правила.

sudo apt install apparmor-utils
sudo aa-genprof /opt/invoicer/bin/invoicer
# Параллельно в другом окне — запускаем демон и прогоняем тесты
# После каждой операции — возвращаемся в aa-genprof и нажимаем S (scan)
# Утилита предлагает варианты: Abstraction? Include? Glob? Allow/Deny?
# Когда нагрузочный прогон пройден — F (Finish), профиль сохраняется

У нас есть железное правило: перед тем как перевести профиль в режим `enforce`, мы держим его в `complain mode` как минимум неделю. Зачем так долго? За это время успевают спокойно отработать все редкие сценарии: ротация логов, еженедельные cron-задачи или, скажем, разовые операции из админки. Всё, что системе действительно нужно, она "покажет", а мы потом просто добавляем соответствующие правила.

РежимЧто делаетКогда использовать
enforceБлокирует запрещённое, пишет в логПродакшен после обкатки
complainРазрешает всё, пишет в логОбучение, отладка, недельный прогон
disableПрофиль выгружен из ядраДиагностика конкретной проблемы
auditEnforce + логирование разрешённых операцийФорензика, расследование

Реальный кейс: взлом Bitrix и последствия

В ноябре 2024 года к нам прибежал клиент — оптовая компания в Подольске, на их Битриксе 24.0 нашли свежий webshell. Атакующий сидел уже четыре дня, прочитал базу, успел залить nested-эксплойт в backup-папку. Что его остановило — у нас на этом сервере был AppArmor-профиль для PHP-FPM, который запрещал запись куда-либо, кроме /var/www/site/upload/ и /tmp. Попытки сделать exec shell-скрипта из /tmp аккуратно ломали закладки злоумышленника — в audit.log мы позже разобрали 47 таких попыток.

Без AppArmor ущерб был бы в разы больше: читать почту из /var/spool/mail, подложить SSH-ключ в /root/.ssh/authorized_keys, установить крон — все эти сценарии упёрлись в MAC. Выложить пост-инцидентный разбор клиент не разрешил, но в ключевом месте там одна строчка: «Ущерб: утечка данных клиентской базы (1220 записей), простой сайта 4 часа. Стоимость реагирования — 92 000 руб». Без AppArmor было бы тысяч 400 и две недели работ.

Сравнение ущерба с AppArmor и без него в случае взлома Bitrix
AppArmor значительно снижает ущерб от взлома, ограничивая утечку данных и простой сайта.

AppArmor + systemd: изящная интеграция

systemd умеет требовать AppArmor-профиль для юнита — и это отличная страховка: если кто-то случайно выгрузил профиль, а потом сервис снова запустили, systemd не даст этому случиться.

# /etc/systemd/system/invoicer.service
[Service]
ExecStart=/opt/invoicer/bin/invoicer
AppArmorProfile=opt.invoicer.bin.invoicer
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true

Проще говоря: нет профиля — нет старта. systemd просто откажется запускать сервис. Это как раз то, что нужно для параноидального продакшена, верно?

AppArmor в Docker и LXC

Docker по умолчанию применяет профиль docker-default. Если нужен свой — генерируете, загружаете и указываете в запуске:

sudo apparmor_parser -r -W /etc/apparmor.d/docker-nginx
docker run --security-opt apparmor=docker-nginx -p 80:80 nginx:stable

Для LXC правило ещё проще — одна строчка в конфиге контейнера /var/lib/lxc/webhost/config:

lxc.apparmor.profile = lxc-container-restricted

Отладка: разбор audit-логов

Итак, профиль в `enforce`, но вдруг что-то перестало работать? Куда смотреть в первую очередь? Конечно, сюда:

sudo dmesg | grep DENIED | tail
sudo journalctl -k | grep apparmor="DENIED"
# или парсим аккуратно:
sudo aa-logprof

Утилита aa-logprof предложит добавить правила для каждого зафиксированного DENY — остаётся только подтвердить или отклонить. Я всегда делаю так: в staging-среде прогнали тест-набор, просмотрели денаи, сформировали финальный профиль, выкатили в прод.

Чек-лист внедрения AppArmor на новом сервере

FAQ — AppArmor на практике

AppArmor или SELinux — что выбрать?
AppArmor использует пути файлов, SELinux — метки inode. AppArmor проще читать и править, его профили похожи на fstab. SELinux мощнее, но входной порог выше. В Ubuntu и Debian по умолчанию AppArmor, в RHEL/CentOS — SELinux.
Насколько AppArmor замедляет систему?
На типичных веб-нагрузках разница меньше 2% — в пределах погрешности. На I/O-интенсивных приложениях вроде Ceph OSD лучше замерить до и после.
Как быстро потушить AppArmor при инциденте?
Правильно: перевести конкретный профиль в complain mode через aa-complain. Не трогать всю подсистему.
AppArmor работает с Docker и LXC?
Да. Docker идёт со встроенным профилем docker-default, LXC — с lxc-container-default. Оба можно переопределить.
Что делать, если приложение пишет в непредсказуемые пути?
Использовать tunables и glob-шаблоны. Объявляете @{MYAPP_DATA}=/srv/myapp и в профиле пишете @{MYAPP_DATA}/** rw.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

📄
Скачайте подробный разбор в PDF Кейсы, статистика, типовые ошибки и чек-лист самопроверки — 12 страниц
Скачать PDF

Подпишитесь на рассылку ITfresh

И, к слову, не забывайте: раз в неделю мы выпускаем по-настоящему практические гайды. Они будут полезны и руководителю IT, и сисадмину. Что там? Всё, что пригодится в работе: безопасность, 1С, безболезненные миграции, надёжные резервные копии и, конечно, куча лайфхаков из наших реальных проектов.

Письмо придёт в течение минутыНе нашли его во «Входящих» — загляните в папку «Спам» или «Промоакции» и нажмите «Не спам». Так все следующие выпуски будут приходить прямо в основную почту.
Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.