AppArmor на openSUSE для пользовательского демона: complain/enforce, aa-logprof и расследование блокировок

by itisgood

AppArmor в openSUSE часто воспринимается как механизм для системных сервисов, работающих от root. На практике не меньше пользы приносит ограничение «пользовательских демонов» – процессов, которые запускаются от обычной учетной записи (например, через systemd –user), имеют доступ к домашнему каталогу и нередко хранят токены/API-ключи рядом с конфигурацией.

AppArmor на openSUSE: профиль для пользовательского демона и разбор DENIED

Ниже разобран прикладной сценарий: как подготовить и довести до enforce профиль AppArmor для пользовательского демона на openSUSE, используя режим complain, инструмент aa-logprof и системный разбор блокировок (DENIED) по журналам.

Задача и предпосылки: «пользовательский демон» и риск доступа к данным

Под «пользовательским демоном» в данном материале подразумевается длительно работающий процесс от непривилегированного пользователя, который:

  • автозапускается через systemd –user, cron или менеджер сессии
  • читает конфигурацию из ~/.config и пишет данные в ~/.local/share~/.cache~/.local/state
  • имеет сетевые исходящие подключения (API, вебхуки, брокеры сообщений) и/или локальные сокеты в $XDG_RUNTIME_DIR
  • потенциально уязвим (ошибка в парсере, зависимостях, обработчике входных данных)

Даже если демон запущен не от root, его компрометация часто означает доступ к чувствительным пользовательским данным: SSH-ключам, history-файлам, токенам браузера/CLI, приватным конфигурациям. AppArmor позволяет ввести обязательный контроль доступа (MAC) и «подрезать» поверхность атаки – запретить чтение/запись всего лишнего, оставив только то, что действительно нужно для работы.

Отдельно стоит учитывать инфраструктурный контекст: на виртуальных серверах (VPS/VDS) подобные демоны нередко обслуживают мониторинг, синхронизацию, сбор метрик или выполнение фоновых задач. В сценариях арнедованных VPS/VDS типичная ошибка – переносить пользовательские агенты «как есть» между хостами, не ограничивая доступ к файловой системе. AppArmor дает контролируемую и воспроизводимую модель доступа.

Подготовка openSUSE: AppArmor, инструменты и журналы

Проверка, что AppArmor действительно активен

Первый шаг – убедиться, что LSM AppArmor включен ядром и сервис загружен:

  • sudo systemctl status apparmor
  • sudo aa-status
  • cat /sys/kernel/security/lsm (в выводе ожидается apparmor)

Если AppArmor отсутствует в списке LSM или отключен параметрами загрузки ядра, профильная работа не будет иметь смысла. На большинстве образов openSUSE для KVM-VPS это включено по умолчанию; на некоторых типах виртуализации с «чужим» ядром возможности LSM могут быть ограничены.

Установка пакетов

Для рабочего цикла обычно требуются утилиты AppArmor и (желательно) auditd:

sudo zypper in apparmor-utils apparmor-parser audit

В некоторых минимальных образах на VPS/VDS логирование AppArmor уходит в journal, но aa-logprof традиционно удобнее работает с audit-логом. Поэтому auditd часто включается явно:

  • sudo systemctl enable –now auditd
  • sudo systemctl enable –now apparmor (если не включен)

Как AppArmor привязывается к пользовательскому демону: важные нюансы

Профиль привязывается к исполняемому файлу

AppArmor применяет ограничения на уровне процесса, «прикрепляя» профиль к исполняемому файлу в момент execve(). Поэтому пользовательский демон, запущенный через systemd –user, ограничивается так же, как и системный – различие лишь в том, что процесс выполняется под UID обычного пользователя и активно взаимодействует с домашним каталогом.

Ключевой практический вывод: профиль должен соответствовать реальному пути запуска. Если сервис стартует через оболочку/обертку, профиль может «сесть» на обертку, а не на целевую программу.

Скрипты (Python/Node.js) и «профиль на интерпретатор»

Распространенная ловушка – пользовательский демон в виде скрипта:

/usr/bin/python3 /home/alice/bin/daemon.py

В таком варианте профиль может потребоваться на /usr/bin/python3, а это означает влияние на любые Python-процессы, запускаемые тем же бинарником. Более управляемые варианты:

  • использовать отдельный интерпретатор в виртуальном окружении (/opt/daemon/venv/bin/python) и профилировать именно его
  • вынести демон в отдельный бинарник/обертку с фиксированным путем (например, /usr/local/bin/daemon), чтобы профиль был адресным
  • использовать exec-правила AppArmor для перехода в отдельный профиль при запуске конкретного скрипта (вариант более сложный и требует аккуратной политики)

Стабильный путь важнее «удобства в home»

Для пользовательских демонов часто хочется хранить исполняемый файл в ~/bin. Для AppArmor это не запрещено, но создает проблемы переносимости:

  • путь включает имя пользователя и привязывает профиль к конкретному home
  • при переносе на другой сервер или при смене каталога домашней директории профиль придется редактировать
  • в multi-user среде профиль «на /home/alice/…» не переиспользуется для других аккаунтов

Типовой компромисс в реальных внедрениях – размещать бинарник в /usr/local/bin или /opt/…, а доступы к пользовательским данным задавать через @{HOME} и ключевое слово owner.

Создание чернового профиля: aa-autodep и базовая загрузка

На старте требуется минимальный профиль, который дальше будет уточняться. Для этого подходит aa-autodep – он создаст каркас профиля и подключит базовые абстракции.

Пример (демон расположен в /usr/local/bin/notifyd):

  1. Создание каркаса профиля:sudo aa-autodep /usr/local/bin/notifyd
  2. Проверка, что файл появился (имя строится из пути):ls -l /etc/apparmor.d/usr.local.bin.notifyd
  3. Загрузка/перезагрузка профиля (один из вариантов):sudo apparmor_parser -r /etc/apparmor.d/usr.local.bin.notifydАльтернатива – перезагрузка сервиса:sudo systemctl reload apparmor

На этом этапе профиль обычно слишком «сухой» и не учитывает фактические обращения к файлам, DNS, сертификатам, сокетам и т. д. Поэтому перед ужесточением требуется этап наблюдения.

Complain и enforce: зачем нужны оба режима и как ими управлять

Что делает complain

Режим complain логирует нарушения политики, но не блокирует действия. Это режим «инструментирования» – собирается реальная картина доступа демона к ресурсам. Важно помнить: в complain защита не включается, поэтому на хостах с недоверенной нагрузкой или открытым внешним интерфейсом риски сохраняются.

Перевод профиля в complain:

sudo aa-complain /usr/local/bin/notifyd

Что делает enforce

Режим enforce применяет политику: запрещенные операции завершаются ошибкой (например, EACCES), и это отражается в логах как DENIED. Перевод в enforce:

sudo aa-enforce /usr/local/bin/notifyd

В боевых сценариях часто используется последовательность: сначала тестовый стенд → complain → уточнение правил → переключение на enforce → мониторинг. Для воспроизведения на отдельном виртуальном сервере подходят любые провайдеры с образами openSUSE; как один из примеров встречается VPS.house – https://vps.house.

Сбор и чтение блокировок (DENIED): journalctl и auditd

Быстрый просмотр DENIED в journald

Даже при включенном auditd сообщения часто дублируются в журнале ядра. Для оперативного поиска удобно использовать фильтры:

  • sudo journalctl -k -g ‘apparmor=«DENIED»’ –since «30 min ago»
  • sudo journalctl -k -g ‘profile=«/usr/local/bin/notifyd»’
  • sudo journalctl -k -g ‘apparmor=«DENIED»’ -f (follow-режим)

Типичный фрагмент записи выглядит как audit-сообщение с полями operationprofilenamerequested_maskdenied_mask. На практике именно эти поля позволяют быстро понять, что именно нужно разрешить (или что демон пытается сделать лишнее).

Auditd и поиск событий

Если auditd включен, сообщения AppArmor попадают в /var/log/audit/audit.log. Для выборки используется ausearch:

  • sudo ausearch -m apparmor –start recent
  • sudo ausearch -m apparmor –start today

На некоторых системах тип события может отображаться не как «apparmor», а как запись audit с числовым типом (например, type=1400). В таком случае полезно ориентироваться на строковый маркер apparmor=«DENIED» через grep или фильтры journald.

aa-logprof: рабочий цикл «подбор правил по факту обращений»

Зачем aa-logprof и как он вписывается в сценарий

aa-logprof анализирует логи и предлагает интерактивно обновить профиль под реальные обращения приложения. Это ускоряет путь от «пустого каркаса» до применимого профиля, но требует дисциплины: автоматическое предложение правил не равно безопасной политике.

Базовый цикл выглядит так:

  1. загрузка каркаса профиля
  2. перевод профиля в complain
  3. запуск демона и выполнение типового рабочего сценария (инициализация, сеть, чтение конфигов, запись кэша, ротация логов)
  4. прогон aa-logprof, выбор минимально необходимых разрешений
  5. перезагрузка профиля
  6. повтор до стабилизации
  7. перевод в enforce и контроль после переключения

Запуск aa-logprof и выбор лог-файла

По умолчанию aa-logprof ищет события в стандартных логах (в зависимости от дистрибутива и настроек). Если auditd включен, часто достаточно запуска без параметров:

sudo aa-logprof

Если события лежат в конкретном файле, полезна явная привязка:

sudo aa-logprof -f /var/log/audit/audit.log

При большом потоке событий на сервере (характерно для VPS/VDS с несколькими сервисами) повышает точность предварительная фильтрация по времени (сначала поиск нужных событий через journald/ausearch, затем работа с профилем).

Что выбирать в диалоге aa-logprof: практические правила

Наиболее важные решения в ходе интерактивного профилирования:

  • Разрешать только то, что необходимо для функционала. Если демон пытается читать ~/.ssh или ~/.gnupg, чаще это повод расследовать поведение демона, а не «разрешить, чтобы работало»
  • Предпочитать узкие пути и каталоги. Вместо @{HOME}/** rw лучше явно указать @{HOME}/.config/app/** r и @{HOME}/.local/share/app/** rw
  • Использовать owner там, где это уместно. В пользовательских профилях ключевое слово owner снижает риск доступа к чужим файлам при совпадении путей или при нестандартных монтированиях
  • Подключать абстракции осмысленно. Например, обращения к DNS и NSS почти всегда закрываются <abstractions/nameservice>, а TLS – абстракциями сертификатов/openssl. Это чище, чем разбрасывать десятки точечных разрешений на системные файлы

Переменные @{HOME} и XDG-пути

В openSUSE в составе AppArmor обычно доступны tunables, включая определение домашних каталогов. После подключения:

#include <tunables/global>

становятся доступны переменные вроде @{HOME}. Для пользовательских демонов полезно привязаться и к XDG-структуре:

  • @{HOME}/.config/<app>/ – только чтение конфигурации
  • @{HOME}/.local/share/<app>/ – состояние/базы данных (чтение/запись)
  • @{HOME}/.cache/<app>/ – кэш (чтение/запись)
  • @{HOME}/.local/state/<app>/ – логи/состояние (чтение/запись)

Для runtime-сокетов системным аналогом является /run/user/UID/. Путь UID заранее неизвестен, поэтому часто используется шаблон:

/run/user/[0-9]*/app.sock rw,

Практический пример: профиль AppArmor для демона systemd –user

Условная служба демона

Для понимания контекста полезен минимальный unit-файл (размещается в ~/.config/systemd/user/notifyd.service):

[Unit]
Description=notifyd user daemon

[Service]
ExecStart=/usr/local/bin/notifyd –config %h/.config/notifyd/config.yml
Restart=on-failure

[Install]
WantedBy=default.target

После этого демон запускается в пользовательском контуре systemd (возможность автозапуска после logout зависит от настроек lingering и политики хоста):

systemctl –user enable –now notifyd.service

AppArmor не зависит от того, стартовал процесс из системного или пользовательского экземпляра systemd – профиль применяется на уровне ядра при запуске исполняемого файла.

Пример профиля (упрощенный, для демонстрации подхода)

Файл профиля для /usr/local/bin/notifyd обычно будет называться /etc/apparmor.d/usr.local.bin.notifyd.

Важно: пример ниже иллюстративный. Для реального демона набор разрешений уточняется по логам и функциональным требованиям.

#include <tunables/global>

profile /usr/local/bin/notifyd flags=(attach_disconnected, mediate_deleted) {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/openssl>
#include <abstractions/user-tmp>

# Сеть – только исходящие TCP (пример для HTTP API)
network inet stream,
network inet6 stream,

# Конфигурация – только чтение
owner @{HOME}/.config/notifyd/** r,

# Состояние/кэш – чтение и запись
owner @{HOME}/.local/share/notifyd/** rwk,
owner @{HOME}/.cache/notifyd/** rwk,
owner @{HOME}/.local/state/notifyd/** rwk,

# Runtime-сокет в XDG_RUNTIME_DIR
owner /run/user/[0-9]*/notifyd.sock rw,

# Системные файлы, которые часто читаются библиотеками
/etc/hosts r,
/etc/resolv.conf r,
/etc/nsswitch.conf r,
/etc/ssl/certs/** r,
/etc/pki/** r,

# Явный запрет на чувствительные каталоги в home (пример усиления)
deny @{HOME}/.ssh/** rwklx,
deny @{HOME}/.gnupg/** rwklx,
}

После правки профиль перезагружается:

sudo apparmor_parser -r /etc/apparmor.d/usr.local.bin.notifyd

Расследование блокировок в enforce: методика «от симптома к правилу»

Шаг 1. Проверка, что процесс действительно находится под AppArmor

Самая частая причина «профиль написан, но ничего не меняется» – профиль не загружен или демон запущен другим путем. Для проверки используются:

  • sudo aa-status – список профилей и режим (enforce/complain)
  • sudo cat /proc/<PID>/attr/current – текущий контекст процесса (PID берется через pgrep или systemctl –user status)

Если в /proc/<PID>/attr/current отображается unconfined, значит профиль не привязался к процессу (не тот путь запуска, профиль не загружен, либо AppArmor не активен).

Шаг 2. Чтение DENIED и расшифровка полей

Типовой фрагмент DENIED содержит:

  • operation – что делал процесс (open, getattr, connect, exec, file_lock и т. п.)
  • profile – какой профиль сработал
  • name – путь к ресурсу (файл/сокет)
  • requested_mask/denied_mask – какие права требовались (r/w/m/k и т. д.)

Расшифровка прав по месту зависит от операции. Для файлов в первом приближении:

  • r – чтение
  • w – запись
  • m – memory map (часто возникает при загрузке библиотек)
  • k – блокировки файлов (lock)
  • x – выполнение (exec)

Шаг 3. Выбор «что делать»: исправлять профиль или поведение демона

DENIED – не всегда сигнал «нужно разрешить». В практических расследованиях полезно разделять причины:

  • ожидаемое легитимное обращение (сертификаты, DNS, чтение конфигурации, запись state-файла) – добавляется точечное разрешение или подключается абстракция
  • побочный эффект зависимостей (попытка читать лишние пути, пробовать несколько вариантов конфигурации) – разрешение добавляется только после понимания, почему это нужно
  • подозрительное обращение (SSH-ключи, чужие домашние каталоги, системные shadow-файлы) – повод проверять конфигурацию демона и цепочку вызовов, а не расширять права

Шаг 4. Привязка DENIED к типу правила AppArmor

Удобно мысленно классифицировать блокировки по «классам»:

  • Файлы и каталоги – добавляются правила путей (rwrwk и т. п.)
  • Сеть – добавляются network inet/inet6 stream/dgram (или сеть полностью запрещается, если демон не должен ходить наружу)
  • Запуск внешних команд – добавляются exec-правила. Если демон вызывает curl/sh, требуется отдельное решение: либо запретить, либо разрешить через ix/px/cx (и понимать последствия)
  • Сокеты в /run/user – добавляются правила на конкретные файлы сокетов/каталоги
  • Capability – для пользовательского демона чаще признак лишних амбиций; разрешать capability стоит только при ясной необходимости

Шаг 5. Возврат к aa-logprof или ручная правка

Для серийных «файловых» DENIED удобнее aa-logprof. Для сложных случаев (например, exec-переходы, слишком широкие glob-шаблоны, подозрительные обращения) чаще требуется ручная правка профиля с последующей перезагрузкой.

Частые причины «сломалось после enforce» и как их диагностировать

1. Демон пишет туда, где не ожидалось

В пользовательских сценариях демоны иногда пишут временные файлы в неожиданные места: /tmp, текущий каталог, рядом с бинарником, или в «исторически привычные» ~/.notifyd/ вместо XDG-путей. В enforce это проявляется как DENIED на запись.

Тактика решения:

  • предпочесть перенос состояния в ~/.local/share или ~/.local/state и ограничить доступ именно туда
  • если перенос невозможен – добавить точечное правило на конкретный путь (например, owner /tmp/notifyd-*.tmp rw), избегая широких шаблонов

2. Имя профиля не совпадает с реальным путем ExecStart

Если unit-файл запускает демона через симлинк или обертку, профиль может не примениться. Практически полезно:

  • использовать в ExecStart абсолютный «канонический» путь (результат readlink -f)
  • проверять /proc/<PID>/attr/current после старта
  • следить за полем profile= в DENIED – оно показывает, какой профиль вообще участвует в решении

3. Подключение к сети есть, а DNS не работает

Даже при разрешенной сети демон может «падать» на резолвинге имен: библиотеки читают /etc/resolv.conf/etc/nsswitch.conf, могут обращаться к /run-сокетам resolver’а. В таких случаях полезно подключить <abstractions/nameservice> и разрешить чтение базовых конфигов, а затем уточнить по логам.

4. TLS/HTTPS и цепочки сертификатов

Для исходящих HTTPS-запросов почти всегда требуется чтение системного хранилища сертификатов (/etc/ssl/etc/pki). Без этого наблюдаются DENIED на чтение сертификатов и ошибки вида «certificate verify failed» на уровне приложения.

5. Демон «внезапно» пытается исполнять /bin/sh

Это отдельный класс рисков: вызов оболочки резко расширяет поведение демона и усложняет политику. Если внешний exec действительно нужен, стоит предпочесть:

  • вызов конкретной утилиты вместо оболочки
  • минимальные exec-разрешения (без перехода в unconfined)
  • по возможности – отдельный сервис/процесс с собственным профилем

Короткий чек-лист внедрения на VPS/VDS

Для повторяемого процесса на виртуальном сервере удобно следовать короткому списку:

  1. Проверить активность AppArmor и наличие LSM (aa-status/sys/kernel/security/lsm)
  2. Установить apparmor-utils и включить auditd для удобного анализа логов
  3. Разместить демон по стабильному пути (/usr/local/bin или /opt), избегая привязки к конкретному /home/<user>
  4. Создать каркас профиля через aa-autodep и загрузить его
  5. Перевести профиль в complain, прогнать типовую нагрузку
  6. Использовать aa-logprof для уточнения правил, не принимая предложения «на автомате»
  7. Переключить в enforce, перезапустить демона и мониторить DENIED через journalctl

При необходимости быстро поднять чистый стенд openSUSE под воспроизведение проблем (особенно когда требуется «аренда VDS» на отдельный срок) обычно выбирается KVM-окружение с доступом к ядру и LSM; как пример каталога услуг аренды VPS для тестов подойдет VPS.house, но провайдер в данном сценарии не принципиален – важна поддержка AppArmor на уровне ядра.

Итоги

AppArmor в openSUSE подходит не только для системных служб, но и для пользовательских демонов, запускаемых через systemd –user. Практически рабочая схема строится на трех опорах: сначала сбор обращений в complain, затем итеративная правка профиля через aa-logprof и ручную верификацию, после чего – переход в enforce с обязательной методикой расследования DENIED по журналам.

Ключ к успеху – не количество разрешений, а их точность: минимальные пути, использование owner, осторожность с exec и сетевыми правилами, а также привычка трактовать DENIED как повод понять поведение демона, а не как просьбу «разрешить всё».

Loading

You may also like

Leave a Comment