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

Ниже разобран прикладной сценарий: как подготовить и довести до 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):
- Создание каркаса профиля:sudo aa-autodep /usr/local/bin/notifyd
- Проверка, что файл появился (имя строится из пути):ls -l /etc/apparmor.d/usr.local.bin.notifyd
- Загрузка/перезагрузка профиля (один из вариантов):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-сообщение с полями operation, profile, name, requested_mask, denied_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 анализирует логи и предлагает интерактивно обновить профиль под реальные обращения приложения. Это ускоряет путь от «пустого каркаса» до применимого профиля, но требует дисциплины: автоматическое предложение правил не равно безопасной политике.
Базовый цикл выглядит так:
- загрузка каркаса профиля
- перевод профиля в complain
- запуск демона и выполнение типового рабочего сценария (инициализация, сеть, чтение конфигов, запись кэша, ротация логов)
- прогон aa-logprof, выбор минимально необходимых разрешений
- перезагрузка профиля
- повтор до стабилизации
- перевод в 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
Удобно мысленно классифицировать блокировки по «классам»:
- Файлы и каталоги – добавляются правила путей (r, w, rwk и т. п.)
- Сеть – добавляются 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
Для повторяемого процесса на виртуальном сервере удобно следовать короткому списку:
- Проверить активность AppArmor и наличие LSM (aa-status, /sys/kernel/security/lsm)
- Установить apparmor-utils и включить auditd для удобного анализа логов
- Разместить демон по стабильному пути (/usr/local/bin или /opt), избегая привязки к конкретному /home/<user>
- Создать каркас профиля через aa-autodep и загрузить его
- Перевести профиль в complain, прогнать типовую нагрузку
- Использовать aa-logprof для уточнения правил, не принимая предложения «на автомате»
- Переключить в enforce, перезапустить демона и мониторить DENIED через journalctl
При необходимости быстро поднять чистый стенд openSUSE под воспроизведение проблем (особенно когда требуется «аренда VDS» на отдельный срок) обычно выбирается KVM-окружение с доступом к ядру и LSM; как пример каталога услуг аренды VPS для тестов подойдет VPS.house, но провайдер в данном сценарии не принципиален – важна поддержка AppArmor на уровне ядра.
Итоги
AppArmor в openSUSE подходит не только для системных служб, но и для пользовательских демонов, запускаемых через systemd –user. Практически рабочая схема строится на трех опорах: сначала сбор обращений в complain, затем итеративная правка профиля через aa-logprof и ручную верификацию, после чего – переход в enforce с обязательной методикой расследования DENIED по журналам.
Ключ к успеху – не количество разрешений, а их точность: минимальные пути, использование owner, осторожность с exec и сетевыми правилами, а также привычка трактовать DENIED как повод понять поведение демона, а не как просьбу «разрешить всё».
![]()
