Debian 13: AppArmor блокує ваш сервіс — дозвольте лише необхідне, не вимикаючи безпеку

Було корисно?

Ваш сервіс прекрасно стартує в стейджингу. Потім Debian 13 у продакшні перетворює його на цеглину: «Permission denied», відсутні файли, які точно існують, сокети, що відмовляються зв’язуватися,
загадкові помилки, які зникають миттєво, щойно ви вимкнете AppArmor. Вітаю — ви зустріли контроль безпеки, який виконує свою роботу — просто ще не виконує вашу.

Ось робоча інструкція, якою користуюся, коли AppArmor «закриває» демон: визначити точну відмову, зіставити її з профілем, що застосувався, додати найвужче можливе правило і довести виправлення.
Вимикати AppArmor — це не «тимчасово». Це шлях, яким тимчасове стає постійним.

Що таке AppArmor (і чому в Debian 13 він здається суворішим)

AppArmor — це модуль безпеки ядра Linux, який обмежує програми за допомогою профілів. Профіль визначає, що програма може читати, записувати, виконувати та які привілеї вона може використовувати.
Якщо програма намагається зробити щось невідповідне, ядро відмовляє і записує відмову в журнал. Все просто. Ніякої магії, ніякого «AI-безпеки», лише політика й її застосування.

Якщо ви з родини «ми запускаємо все як root у контейнері», AppArmor стане для вас пробудженням. Контейнери — не заміна прав. Навіть root у контейнері потребує дозволу від ядра.
AppArmor — частина того «так/ні».

На Debian 13 ви найчастіше натрапите на AppArmor коли:

  • Ви розгортаєте кастомний сервіс, який торкається файлів поза стандартними локаціями.
  • Додаєте новий плагін, ціль для бекапу або шлях до TLS-ключа під /srv або /mnt.
  • Переключаєте демон з TCP на Unix-сокети (або навпаки).
  • Переміщуєте логи в новий каталог, бо «ми любимо порядок». (Знамениті останні слова.)
  • Загострюєте systemd-hарденінг і ненавмисно змінюєте середовище процесу, якого профіль не очікував.

Ментальна модель: AppArmor не «блокує ваш сервіс». Ваш сервіс просить доступу, який йому ніколи не надавали. Ваше завдання — надати мінімально потрібний доступ і перевірити,
що ви не розширили зону ураження.

Одна цитата, яку варто мати на видному місці — бо це операції в одній фразі:
Надія — це не стратегія. (атрибуція: перефразована ідея, часто пов’язана з ген. Горданом Р. Салліваном; широко використовується в інженерії та операціях)

Швидкий план діагностики

Коли сервіс падає й ви підозрюєте AppArmor, не «пробуйте все підряд». Виконайте ці три перевірки по черзі. Це швидше і залишає докази.

1) Підтвердьте, що AppArmor активний і працює в режимі примусового застосування

Якщо AppArmor вимкнений глобально, ви шукаєте іншу проблему. Якщо він увімкнений, але профіль у complain-режимі, ви отримуватимете логи без застосування — теж інша ситуація.

2) Визначте подію відмови та ім’я профілю

Рядок у журналі відмови повідомляє profile=, цільовий шлях, операцію й запитані дозволи. Цей рядок — ваш запит на зміну. Немає рядка відмови — немає зміни політики.

3) Вирішіть: виправити профіль, змінити поведінку сервісу або змінити розташування файлів

Якщо сервіс намагається читати секрет із загальнодоступного каталогу, це не «помилка AppArmor». Це проблема макету. Якщо сервіс потребує доступу — додайте вузьке правило.
Якщо доступ не повинен бути потрібен — виправте сервіс.

Правило великого пальця: одна відмова зазвичай означає одне відсутнє правило. Десять відмов у десяти різних шляхах часто означає, що виконуваний файл не той, що ви думаєте, або профіль невірний.

Практичні завдання: команди, виводи, рішення (12+)

Все нижче можна виконати на хості Debian з інстальованим AppArmor. Замініть імена сервісів і шляхи, але зберігайте метод. Саме метод важливий.

Завдання 1: Перевірте, чи AppArmor увімкнений у ядрі

cr0x@server:~$ cat /sys/module/apparmor/parameters/enabled
Y

Що це означає: Y означає, що LSM AppArmor увімкнено в ядрі.
Рішення: Якщо там N або файл відсутній — зупиніться. На цьому завантаженні AppArmor вас не блокує.

Завдання 2: Перевірте здоров’я служби AppArmor

cr0x@server:~$ systemctl status apparmor
● apparmor.service - Load AppArmor profiles
     Loaded: loaded (/lib/systemd/system/apparmor.service; enabled; preset: enabled)
     Active: active (exited) since Tue 2025-12-28 08:11:29 UTC; 2h 4min ago
       Docs: man:apparmor(7)
    Process: 512 ExecStart=/lib/apparmor/apparmor.systemd reload (code=exited, status=0/SUCCESS)

Що це означає: Профілі завантажені успішно.
Рішення: Якщо це failed — спочатку виправте це; інакше ваші редагування профілю не застосуються.

Завдання 3: Перелічіть завантажені профілі й їхній режим

cr0x@server:~$ sudo aa-status
apparmor module is loaded.
46 profiles are loaded.
44 profiles are in enforce mode.
2 profiles are in complain mode.
0 profiles are in kill mode.

Що це означає: У вас справжнє примусове застосування.
Рішення: Якщо відповідний профіль у complain-режимі, ваш сервіс може працювати, але логувати відмови; вирішуйте, чи переходити в enforce після виправлень.

Завдання 4: Підтвердіть, чи процес сервісу обмежений

cr0x@server:~$ systemctl show -p MainPID myservice.service
MainPID=1842
cr0x@server:~$ sudo cat /proc/1842/attr/current
/usr/bin/myservice (enforce)

Що це означає: Процес обмежений профілем /usr/bin/myservice у режимі enforce.
Рішення: Редагуйте профіль, що відповідає тому, що ви бачите в attr/current, а не те, що ви вважаєте правильним.

Завдання 5: Витягніть останні відмови AppArmor з journald

cr0x@server:~$ sudo journalctl -k --since "30 min ago" | grep -i apparmor | tail -n 5
Dec 28 10:01:22 server kernel: audit: type=1400 audit(1766916082.123:311): apparmor="DENIED" operation="open" class="file" profile="/usr/bin/myservice" name="/etc/myservice/secret.key" pid=1842 comm="myservice" requested_mask="r" denied_mask="r" fsuid=998 ouid=0

Що це означає: Ваш сервіс спробував відкрити файл на читання й отримав відмову.
Рішення: Дозвольте читання саме цього файлу (або шаблону директорії), або перемістіть секрет у шлях, який вже дозволено політикою. Не гадіть — використайте рядок відмови.

Завдання 6: Використайте лог аудиту, якщо journald захаращений або журнали повернуті

cr0x@server:~$ sudo grep -i "apparmor=\"DENIED\"" /var/log/audit/audit.log | tail -n 3
type=AVC msg=audit(1766916082.123:311): apparmor="DENIED" operation="open" class="file" profile="/usr/bin/myservice" name="/etc/myservice/secret.key" pid=1842 comm="myservice" requested_mask="r" denied_mask="r"

Що це означає: Та сама відмова, з підсистеми аудиту.
Рішення: Якщо у вас не встановлено auditd, розгляньте його для серверів, де потрібне стійке логування безпеки; journald може бути достатнім, але не завжди зберігається довго.

Завдання 7: Знайдіть файл профілю на диску

cr0x@server:~$ sudo grep -R "profile /usr/bin/myservice" -n /etc/apparmor.d | head -n 2
/etc/apparmor.d/usr.bin.myservice:12:profile /usr/bin/myservice flags=(attach_disconnected) {

Що це означає: Профіль знаходиться в /etc/apparmor.d/usr.bin.myservice.
Рішення: Редагуйте цей файл (або локальний include), не вигадуючи нову назву профілю.

Завдання 8: Згенеруйте правила інтерактивно за допомогою aa-logprof

cr0x@server:~$ sudo aa-logprof
Reading log entries from /var/log/audit/audit.log.
Updating AppArmor profiles in /etc/apparmor.d.
Profile:  /usr/bin/myservice
Execute:  /usr/bin/myservice
Severity: unknown

[(A)llow]/(D)eny/(I)gnore/(N)ew/(G)lob/(Q)uit

Що це означає: AppArmor розпарсив вашу відмову і пропонує зміну політики.
Рішення: Віддавайте перевагу Allow лише коли доступ легітимний і очікуваний. Якщо не впевнені — виберіть Ignore, відтворіть проблему з додатковим логуванням і поверніться пізніше.

Завдання 9: Перезавантажте один профіль після редагування

cr0x@server:~$ sudo apparmor_parser -r /etc/apparmor.d/usr.bin.myservice

Що це означає: Відсутність виводу — нормальний результат при успіху.
Рішення: Якщо бачите помилки парсингу — виправте синтаксис перед рестартом сервісу. Неправильний профіль може залишити завантажену стару політику.

Завдання 10: Підтвердіть, що нове правило завантажено (а не просто відредаговано)

cr0x@server:~$ sudo aa-status | grep -n "/usr/bin/myservice"
21:   /usr/bin/myservice

Що це означає: Профіль завантажено. Це не доводить, що нове правило діє, але підтверджує, що профіль існує та активний.
Рішення: Тепер перезапустіть сервіс і перевірте, що відмова зупинилася.

Завдання 11: Перезапустіть сервіс і спостерігайте журнали в режимі реального часу

cr0x@server:~$ sudo systemctl restart myservice.service
cr0x@server:~$ sudo journalctl -u myservice.service -n 50 --no-pager
Dec 28 10:06:41 server myservice[2011]: Loaded TLS key from /etc/myservice/secret.key
Dec 28 10:06:41 server myservice[2011]: Listening on /run/myservice.sock
Dec 28 10:06:41 server systemd[1]: Started myservice.service - My Service.

Що це означає: Сервіс тепер читає ключ і зв’язує свій сокет.
Рішення: Залиште правило. Якщо все ще падає — поверніться до рядка відмови в ядрі/аудіті — журнал сервісу не є авторитетним щодо AppArmor.

Завдання 12: Доведіть, що ви не ввели потік нових відмов

cr0x@server:~$ sudo journalctl -k --since "5 min ago" | grep -i "apparmor=\"DENIED\"" | tail -n 10

Що це означає: Ідеально: відсутність виводу.
Рішення: Якщо з’являються нові відмови, ви виправили один шлях, але сервіс має ще потреби. Ітеруйте з тією самою дисципліною: одна відмова — одне мінімальне правило.

Завдання 13: Тимчасово перевести профіль у complain-режим (для діагностики)

cr0x@server:~$ sudo aa-complain /usr/bin/myservice
Setting /usr/bin/myservice to complain mode.

Що це означає: Профіль логуватиме, але не застосовуватиме обмеження. Це допомагає зрушити справу з мертвої точки, збираючи докази.
Рішення: Використовуйте complain як діагностичний крок, а не як довготривале рішення. Поставте нагадування повернути enforce після оновлення профілю.

Завдання 14: Повернути профіль у enforce-режим

cr0x@server:~$ sudo aa-enforce /usr/bin/myservice
Setting /usr/bin/myservice to enforce mode.

Що це означає: Примусове застосування назад увімкнено.
Рішення: Якщо сервіс знову ламається — ви пропустили правило Allow. Це очікувано — збирайте нові відмови й підтягуйте правила.

Завдання 15: Знайдіть проблеми «файл відсутній», які насправді — обмеження

cr0x@server:~$ sudo strace -f -e trace=file -p 1842 2>&1 | head -n 8
openat(AT_FDCWD, "/etc/myservice/secret.key", O_RDONLY|O_CLOEXEC) = -1 EACCES (Permission denied)
openat(AT_FDCWD, "/etc/ssl/certs/ca-certificates.crt", O_RDONLY|O_CLOEXEC) = 3

Що це означає: Ядро відмовляє в доступі до файлу. Це відповідає відмовам AppArmor.
Рішення: Коли розробники наполягають «файл існує», покажіть їм системний виклик із кодом EACCES і рядок аудиту. Це швидко закінчить суперечку.

Завдання 16: Перевірте, що шлях виконання в юніті відповідає очікуваному профілю

cr0x@server:~$ systemctl cat myservice.service
# /etc/systemd/system/myservice.service
[Service]
ExecStart=/usr/local/bin/myservice --config /etc/myservice/config.yaml

Що це означає: Бінар знаходиться в /usr/local/bin, а не в /usr/bin.
Рішення: Якщо завантажений профіль для /usr/bin/myservice, але сервіс запускає /usr/local/bin/myservice, ви редагуєте невірний профіль. Виправте відповідність профілю або шлях.

Як читати відмови як професіонал

Рядок відмови AppArmor виглядає загрозливо, поки ви не навчитеся, на які поля дивитись. Ось на чому фокусуватися:

  • profile= політика, яка прийняла рішення. Це файл, який ви будете редагувати.
  • operation= що процес намагався зробити: open, create, unlink, connect, bind, ptrace, mount тощо.
  • name= цільовий шлях (для файлових операцій) або ім’я об’єкта.
  • requested_mask= що процес запросив, наприклад r (читання), w (запис), k (блокування), m (mmap), x (виконання).
  • denied_mask= що було відхилено (зазвичай співпадає з requested_mask при чистій відмові).
  • comm= ім’я команди; корисно, коли є обгортки або shell-скрипти.
  • pid= дозволяє зв’язати з systemd, strace і деревом процесів.

Відмова — це не пропозиція. Це точний доказ того, що було заблоковано.
Ваш процес може падати з багатьох причин; змінюйте AppArmor лише тоді, коли ядро каже, що AppArmor відмовив.

Жарт №1: Якщо ви «виправили» AppArmor, вимкнувши його, ви не виправили AppArmor — ви виправили свою совість, прибравши його.

Типи відмов, які найчастіше зустрічаються в реальних сервісах

Читання файлів: конфіги, сертифікати, креденціали, файли плагінів. Зазвичай найпростіше виправляється вузьким правилом r.

Запис файлів: логи, pid-файли, кеші, бази даних. Саме тут люди стають недбалими. Не дозволяйте запис у широкі директорії «щоб просто працювало».

Unix-сокети: биндинг у /run, з’єднання з Docker/containerd сокетами, спілкування з системними сервісами. AppArmor може обмежувати і створення, і операції connect.

Сигнали і ptrace: health-чекери, watchdog-и, дебагери. Часто з’являються під час інцидентів, коли хтось підключає інструменти до обмеженого процесу.

Дозвольте лише необхідне: редагування профілів без «дозволити все»

Правильна зміна профілю — нудна. Вона надає одну можливість, один шлях, одну операцію і нічого зайвого. Неправильна зміна — це «/** rw», що по суті
купує сейф і залишає його відчиненим, бо клавіатура набридла.

Віддавайте перевагу вузьким файловим правилам замість шаблонів директорій

Якщо сервіс потребує одного секретного ключа:

cr0x@server:~$ sudo sed -n '1,80p' /etc/apparmor.d/usr.bin.myservice
#include <tunables/global>

profile /usr/bin/myservice flags=(attach_disconnected) {
  #include <abstractions/base>
  /usr/bin/myservice mr,

  /etc/myservice/secret.key r,
}

Що це означає: Профіль дозволяє читання одного файлу. Це легко зрозуміти під час аудиту і легко відкотити.
Рішення: Обирайте найменший відповідний шлях. Використовуйте директорійні глоби лише коли сервіс дійсно потребує кількох ротаційних файлів.

Робити запис умисно: логи, стан, кеш

Типовий демон потребує:

  • Конфіги лише для читання під /etc
  • Писаний стан під /var/lib
  • Писані логи під /var/log (або stdout у journald)
  • Рuntime-сокет або pid під /run

Узгодьте файлову структуру з цими очікуваннями. Якщо наполягаєте на записі стану в /etc або /opt, ви будете вічно боротися з AppArmor.
І ви це заслужите.

Використовуйте абстракції, але не ховайтеся за ними

Абстракції AppArmor (наприклад, abstractions/base) корисні. Вони також полегшують випадкове надання більше прав, ніж потрібно.
Моє правило: включайте абстракції, коли вони чітко відповідають ролі сервісу, а потім додавайте явні правила для унікальних частин.

Не плутайте «працює» з «безпечним»

Ви завжди можете змусити все працювати, додавши широкі правила. Ви тут, бо керуєте продакшн-системами, а у продакшні є вороги: помилки, атакувальники і майбутній ви.
AppArmor допомагає проти всіх трьох, але тільки якщо ви його не саботуєте.

Жарт №2: «Я тимчасово перевів у complain-режим» — це версія безпеки фрази «я просто потримаю цей оголений дріт на секунду».

Systemd, сервіси та який профіль насправді застосовується

Більшість інцидентів AppArmor у Debian-організаціях не через «AppArmor занадто суворий». Вони через «ми відредагували не той профіль» або «systemd запускає інший бінар, ніж ми думаємо».
Systemd додає обгортки: файли оточення, pre-start скрипти, динамічних користувачів, runtime-каталоги.

Знайте ланцюжок виконання

AppArmor зазвичай прив’язується за шляхом виконуваного файлу. Якщо ваша одиниця запускає shell-обгортку, яка потім exec-ує реальний бінар, ви можете обмежувати shell, а не демон. Або нічого.
Доказ завжди у /proc/<pid>/attr/current.

Обережно з /usr/local vs /usr/bin

Пакети Debian зазвичай кладуть керовані бінарі в /usr/bin. Ручні розгортання люблять /usr/local/bin.
Якщо профіль названий для одного шляху, а ви виконуєте інший — ви гнатиметеся за примарами.

Systemd-hardening може змінити симптоми

Опції як ProtectSystem=, ReadOnlyPaths= і PrivateTmp= також можуть викликати «permission denied», але це не AppArmor-відмови.
Тому швидка діагностика починається з рядка відмови в ядрі. Потрібен правильний винуватець, перш ніж братися за ремонт.

Три корпоративні історії з практики

Інцидент №1: Неправильне припущення («Це просто проблема прав файлової системи»)

Середня компанія розгорнула оновлення Debian по флоту, що працював із внутрішнім API-шлюзом. В одному регіоні почалися випадкові 503.
На чергу вийшли звичні дії під тиском: шукали очевидне.

Логи шлюзу казали, що він не може прочитати свій TLS-ключ. Файл існував, права виглядали правильно, і швидкий sudo -u gateway cat /path/to/key працював.
Хтось вирішив, що це гонитва станів у деплойменті, і перезапустив конфіг менеджмент.
Проблема залишилась. Звісно, додали ще повторів.

Зрештою спокійніший інженер перевірив журнал ядра і знайшов відмови AppArmor для operation="open" на цьому шляху до ключа.
Ключ перемістили в нову директорію, що відповідала їхнім правилам організації — але не профілю AppArmor.

Миттєвий «ага» був неприємним: всі UNIX-права були коректні. AppArmor відмовив у доступі і йому все одно, що ручний cat працював,
бо ручний тест робили поза обмеженим контекстом.

Виправленням стало одне правило, що дозволило читання нового шляху ключа. Тривале виправлення — культурне: ніколи не сприймайте «permission denied» як діагноз лише файлової системи на системі з MAC.
Спочатку перевіряйте рядок відмови.

Інцидент №2: Оптимізація, що повернулася бумерангом (консолідація логів)

Інша команда хотіла «оптимізувати» доставку логів. Замість journald вони перенаправили кілька сервісів на запис JSON-логів у спільну директорію на швидкому диску,
а потім зчитували їх агентом. Виглядало акуратно: одне місце для всіх логів, однакове обертання, задоволені дашборди.

На Debian 13 кілька сервісів почали падати під час старту з загальними IO-помилками. Інженери шукали продуктивність диска, потім SELinux (хоча його не запускали),
потім systemd sandboxing. Помилки були непостійні, бо лише певні кодові шляхи писали логи на ранніх етапах старту.

Корінна причина — профілі AppArmor дозволяли запис у стандартні шляхи логів кожного сервісу, але не в нову спільну директорію.
«Оптимізація» мовчки змінила ціль запису для кількох демонів. AppArmor зробив саме те, що має робити: не дозволив обмеженому сервісу писати куди заманеться.

Наївне вирішення — додати широкий доступ на запис у спільну директорію для кожного сервісу. Це б зробило консолідацію логів працездатною — і одночасно створило б поверхню для підтасування даних між сервісами.
Один скомпрометований сервіс міг би перезаписати логи іншого — чудовий подарунок для атакувальника, який любить ховати сліди.

Правильне виправлення — уникати спільних записуваних директорій між несуміжними демонами, або використовувати піддиректорії для кожного сервісу з вузькими allow-правилами і контролем власності.
Команда в підсумку використовувала journald для більшості сервісів і лише вивантажувала JSON локально там, де це було потрібно для спеціальних інструментів.
Менше порядку на диску. Набагато чистіша ризик-позиція.

Інцидент №3: Нудна практика, яка врятувала день (збір відмов перед змінами)

Фінтех-команда запускала платіжний процесор з суворим процедурним контролем. Їх підхід був болісно непримітний: перед увімкненням примусового режиму для сервісу
вони запускали його в complain-режимі в продакшні на день, збирали відмови, переглядали їх і лише потім переключали в enforce.

Під час рутинного оновлення залежностей сервіс почав звертатися до нового шляху CA-бандлу через зміни в TLS-стеку.
У complain-режимі відмови логувалися негайно. Нічого не ламалось, і клієнти не відчули проблеми.

Вони додали вузьке правило читання для цього місця CA-бандлу, перезавантажили профіль і програли смоук-тести.
Лише потім включили enforce. Коли зміна дісталася до всього флоту — це пройшло непомітно.

Практика не була хитра. Вона не вимагала героїзму. Вона вимагала дисципліни: ставитись до політики як до артефакту продакшну, тестувати її як код і розгортати як код.
Результат — передбачуваність, що саме вам потрібна о 3 ранку.

Поширені помилки: симптом → причина → виправлення

1) Сервіс каже «файл не знайдено», але файл існує

Симптом: У додатку лог ENOENT або «missing file», але ви бачите файл на диску.

Причина: AppArmor відмовляє у відкритті, і додаток невірно відображає помилку (або ви дивитесь не той шлях через symlink/chroot).

Виправлення: Підтвердіть за допомогою journalctl -k або /var/log/audit/audit.log; дозвольте точний шлях або виправте сервіс, щоб читати з дозволеного місця.

2) Ви відредагували профіль і нічого не змінилося

Симптом: Додаєте правила, перезапускаєте сервіс, але відмови залишаються.

Причина: Редагований файл не є завантаженим профілем, або профіль не було перезавантажено, або процес виконує інший бінарний шлях.

Виправлення: Перевірте /proc/<pid>/attr/current. Перезавантажте з apparmor_parser -r. Перевірте шлях бінарника в systemd ExecStart.

3) «Виправлення» — вимкнути AppArmor, і інцидент закінчився (поки що)

Симптом: Вимкнення AppArmor усе полагодило.

Причина: Політика відсутня для легітимних доступів. Вимкнення примусового режиму знімає всі межі.

Виправлення: Використовуйте complain-режим тимчасово, зберіть відмови, оновіть профіль і поверніться в enforce.

4) Сервіс не може прив’язати свій Unix-сокет під /run

Симптом: Старт падає при створенні або прив’язці /run/myservice.sock.

Причина: Профіль не дозволяє створення цього шляху сокета, або runtime-директорія systemd відрізняється від очікуваної.

Виправлення: Додайте правила для /run/myservice.sock з правильними дозволами; переконайтесь, що RuntimeDirectory= відповідає шляху, який ви дозволяєте.

5) Після додавання шаблону сервіс працює, але рев’ю безпеки блокує реліз

Симптом: Рев’юер відхиляє профіль через широкі правила як /etc/** r або /** rw.

Причина: Надмірно широка політика написана під тиском.

Виправлення: Замініть на явні файлові правила, правила на піддиректорії для власних шляхів і мінімальні можливості. Відтворіть відмови, щоб знайти конкретні потреби.

6) DNS, вихідний HTTP або підключення до БД падають, але файлових відмов немає

Симптом: Мережеві виклики таймаутять або відмовляються; логи додатку нечіткі.

Причина: AppArmor може обмежувати мережеві операції залежно від профілю та функцій; або причина в systemd sandboxing/файрволі.

Виправлення: Шукайте відмови з operation="connect" або пов’язані з network. Якщо AppArmor-відмов немає — перевірте systemd hardening і правила файрволу.

7) Ви бачите відмови для імені процесу, якого не впізнаєте

Симптом: Відмови згадують comm="(щось)", що не ваш сервіс.

Причина: Ваш сервіс виконує допоміжні бінарі (curl, openssl, shell-скрипти), і профіль обмежує виконання.

Виправлення: Вирішіть, чи повинен сервіс викликати допоміжні програми. Якщо так — дозвольте конкретні бінарі з коректними правами на виконання; якщо ні — приберіть таку поведінку з сервісу.

Чеклісти / покроковий план

Чекліст A: Коли продакшн впав і потрібен швидкий сигнал

  1. Перевірте обмеження: /proc/<pid>/attr/current. Якщо там unconfined, припиніть звинувачувати AppArmor.
  2. Витягніть відмови ядра: journalctl -k з фільтром apparmor="DENIED".
  3. Зіставте час з падінням сервісу в journalctl -u.
  4. Вирішіть найменше легітимне правило Allow. Якщо не можете це обґрунтувати — не додавайте.
  5. Перезавантажте профіль, рестартуйте сервіс, підтвердіть, що відмови зникли.
  6. Встановіть таймер, щоб відкотити будь-який complain-режим, що використовувався під час інциденту.

Чекліст B: Безпечний цикл розробки політик (той, що збереже вам роботу)

  1. Переведіть профіль сервісу в complain-режим на канарному хості.
  2. Пропрацюйте сервіс: старт, steady-state, бекапи, обертання, оновлення, відмовостійкість.
  3. Запустіть aa-logprof і перегляньте кожне правило, як зміну фаєрволу.
  4. Віддавайте перевагу явним файловим шляхам; уникайте спільних записуваних директорій.
  5. Перезавантажте профіль, перемкніться в enforce на канарі, повторіть тести.
  6. Розгортайте на невеликій підгрупі, моніторте відмови, потім розширюйте.
  7. Зберігайте зміни профілів у звичайній системі змін (Git, CI, рев’ю). Сприймайте їх як код.

Чекліст C: Розумна структура каталогів, що зменшує тертя з AppArmor

  • Конфіги: /etc/myservice/ (тільки для читання під час виконання, окрім явних випадків)
  • Секрети: /etc/myservice/ або /var/lib/myservice/ (читання лише, сувора власність)
  • Стан: /var/lib/myservice/ (пишеться)
  • Кеш: /var/cache/myservice/ (пишеться, відновлюваний)
  • Логи: віддавайте перевагу journald; інакше /var/log/myservice/ для кожного сервісу
  • Сокети/pid: /run/myservice/ створюються systemd через RuntimeDirectory=

Цікаві факти й історичний контекст (коротко й по суті)

  • AppArmor починався як комерційний продукт від Immunix; згодом увійшов у ядро Linux за участі Novell.
  • На відміну від SELinux з його моделлю на основі міток, AppArmor працює за шляхами: політики посилаються на файлові шляхи, що робить їх зрозумілими, але вразливими до особливостей шляхів, якщо бути недбалим.
  • Профілі AppArmor підтримують «complain-режим», який логуює порушення без їхнього застосування — корисно для побудови політик за спостереженням.
  • Фреймворк Linux Security Modules (LSM) — це механізм, що дозволяє AppArmor, SELinux та іншим підключатися до перевірок ядра.
  • Профілі AppArmor можуть використовувати «hat»-и (підпрофілі) для тимчасової зміни обмежень у частинах програми, хоча в сучасних розгортаннях сервісів це менш поширено.
  • Шляхи прийняття AppArmor в різних дистрибутивах відрізнялися; Ubuntu рано зробила AppArmor базовим елементом, що вплинуло на інструменти та практики спільноти.
  • Поява systemd змінила процес розслідування: journald став основним місцем для відмов, а не класичні syslog-файли.
  • AppArmor може обмежувати не лише файли: сигнали, взаємодії D-Bus і певні можливості ядра теж з’являються як відмови в реальних інцидентах.

FAQ

1) Як зрозуміти, що це AppArmor, а не Unix-права?

Шукайте рядок відмови в ядрі/аудіті з apparmor="DENIED". Якщо він з’являється одночасно з помилкою — це AppArmor.
Якщо такого рядка немає — ймовірно, проблема в правах файлової системи, systemd sandboxing або багах додатку.

2) Чи варто використовувати complain-режим у продакшні?

Так, коротко і навмисно: complain-режим корисний для збору відмов без порушення трафіку.
Але ставтесь до нього як до інструмента вікна змін. Коли правила коректні — повертайтесь до enforce.

3) Чому sudo -u myuser cat /path працює, а сервіс не може прочитати файл?

Бо сервіс обмежений, а ваш ручний тест зазвичай ні. Рішення AppArmor залежить від контексту обмеженого процесу, а не лише від UID/GID.
Перевірте з /proc/<pid>/attr/current.

4) Який найбезпечніший спосіб додати доступ до секретів?

Дозвольте читання саме файлу секрету (не всю директорію) і тримайте власність і права суворими.
Якщо потрібна ротація — дозвольте мінімальний глоб, що відповідає схемі ротації (наприклад конкретний шаблон імені файлу).

5) Чи можна просто дозволити /etc/** r, щоб припинити проблеми?

Можна, і це «працюватиме», але також тихо дозволить читання не пов’язаних конфігів і секретів.
Якщо сервіс буде скомпрометовано, широкий доступ полегшить крадіжку даних. Будьте точними.

6) Мій сервіс запускається з /usr/local/bin. Чому пакувальний профіль не застосовується?

Більшість профілів прив’язуються до шляху виконуваного файлу. Якщо профіль для /usr/bin/myservice, а ви запускаєте /usr/local/bin/myservice,
то профіль, про який ви думаєте, не застосовується. Або змініть unit на очікуваний шлях, або створіть/змініть профіль для фактичного бінарника.

7) Я виправив одну відмову, і тепер бачу іншу. Це нормально?

Так. Сервіси часто падають на першій заблокованій операції. Після розблокування наступна відсутня привілегія проявиться.
Ітеруйте, поки старт і steady-state не пройдуть чисто в enforce-режимі.

8) Як уникнути повторення цього при кожному оновленні?

Сприймайте профілі як частину сервісу, а не як післямову. Зберігайте зміни в контролі версій, тестуйте в CI де можливо,
і запускайте канарки в complain-режимі після оновлень, щоб зібрати нову поведінку перед загальним застосуванням.

9) Чи AppArmor «слабший» за SELinux?

Різні моделі — різні компроміси. Шляховий підхід AppArmor часто доступніший і швидший для впровадження на рівні сервісів.
«Слабший» зазвичай означає «менш повсюдно розгорнутий і менше строго підтримуваний», а не те, що він принципово нездатний.

Висновок: наступні кроки, які ви реально можете зробити сьогодні

Якщо Debian 13 блокує ваш сервіс — не вимикайте AppArmor. Використайте його як інструмент діагностики й ремінь безпеки.
Знайдіть рядок відмови. Визначте профіль, що застосувався. Додайте мінімальне правило, що відповідає легітимній поведінці. Перезавантажте. Доведіть через логи.

Практичні кроки:

  1. Виберіть один падаючий сервіс і зберіть його рядки відмов з journalctl -k або /var/log/audit/audit.log.
  2. Підтвердіть обмеження через /proc/<pid>/attr/current, потім знайдіть відповідний файл у /etc/apparmor.d/.
  3. Запустіть aa-logprof, приймайте лише правила, які можете пояснити, перезавантажте профіль і протестуйте.
  4. Коли стабільно, тримайте профіль в enforce-режимі і моніторте нові відмови після змін.

Мета не в тому, щоб змусити AppArmor мовчати. Мета — зробити так, щоб ваш сервіс передбачувано поводився під обмеженнями — бо передбачуваність тримає інциденти малими.

← Попередня
DNS SERVFAIL від постачальника: як довести, що проблема на їхньому боці
Наступна →
ZFS-знімки: суперсила, яка може заповнити ваш пул

Залишити коментар