Przejdź do treści
Lekcja 83 z 117

Kurs › Moduł 9 › Lekcja 83

AppArmor

⏱ ok. 9 min czytania· moduł 9: Użytkownicy i bezpieczeństwo

Po tej lekcji: kursant wie, czym AppArmor różni się od SELinuksa, sprawdza jego stan przez aa-status, czyta profil, przełącza go między trybami enforce i complain, znajduje odmowę w dzienniku i dopisuje własny wyjątek bez wyłączania ochrony.

Wstęp

Ubuntu, terminal, sudo tcpdump -i any -w /srv/zrzut.txt

Ubuntu, jesteś rootem, chcesz zapisać ruch sieciowy do pliku. A tcpdump na to:

tcpdump: /srv/zrzut.txt: Permission denied

Root. Permission denied. To jak dyrektor, któremu portier nie wydaje kluczy do własnego gabinetu. Zmieniasz nazwę na /srv/zrzut.pcap i nagle działa. Jakby system miał alergię na rozszerzenie .txt.

Nie ma alergii. Ma AppArmora. I to, co wygląda na złośliwość, to bardzo sensowna zasada zapisana w jednym pliku. Dziś go przeczytamy.

Przepustka z listą pomieszczeń

W poprzedniej lekcji SELinux był ochroniarzem, który patrzy na kolory plakietek i naklejki na drzwiach. Wszystko w budynku ma naklejkę.

AppArmor robi to inaczej. Nie nakleja niczego na drzwi. Zamiast tego wybrani pracownicy dostają imienną przepustkę z listą pomieszczeń: „tcpdump może wejść do pokoju 12, czytać w archiwum, pisać tylko do teczek z napisem .pcap”. Ochroniarz sprawdza przepustkę i nazwę pokoju. Kto nie ma przepustki, chodzi tylko na zwykłych kluczach.

Ta przepustka to profil. A reguły mówią o ścieżkach, nie o etykietach. Stąd najważniejsza różnica w praktyce:

SELinuxAppArmor
Czego się trzymaetykiet na plikach i procesachścieżek w profilu programu
Kogo pilnujecały system według politykitylko programy, które mają profil
Gdzie domyślnieRHEL, Rocky, Alma, FedoraUbuntu, Debian
mv z domowego folderupsuje (stara naklejka)nie ma znaczenia
Nowa ścieżka dla usługisemanage fcontext + restorecondopisanie ścieżki do profilu

AppArmor jest prostszy do czytania. Profil otwierasz w edytorze i od razu widzisz, co wolno. W SELinuksie regulamin ma tysiące reguł skompilowanych w binarny plik. Za to SELinux pilnuje dokładniej. Na rozmowie rekrutacyjnej to pytanie pada częściej niż „gdzie widzisz się za pięć lat”.

[RHEL] Na RHEL i pochodnych AppArmora po prostu nie ma. [Ubuntu] Na Ubuntu jest zainstalowany i włączony od pierwszego uruchomienia.

Kto ma przepustkę: aa-status

sudo aa-status
apparmor module is loaded.
117 profiles are loaded.
41 profiles are in enforce mode.
   /usr/bin/man
   tcpdump
   ...
4 profiles are in complain mode.
   ...
3 processes have profiles defined.
2 processes are in enforce mode.
   /usr/sbin/rsyslogd (812)
   ...
0 processes are unconfined but have a profile defined.

Liczby u Ciebie będą inne, ważny jest układ. Profile załadowane. Ile pilnuje naprawdę (enforce), a ile tylko notuje (complain). I które działające procesy są pod nadzorem. Ostatnia linijka jest podstępna: „procesy bez nadzoru, choć mają profil”. To programy uruchomione, zanim profil się załadował. Pomaga restart usługi.

W kontenerze albo na systemie bez AppArmora zobaczysz krótko:

apparmor not present.

aa-status jest w pakiecie apparmor. Komendy aa-enforce, aa-complain, aa-genprof i aa-logprof są w osobnym pakiecie:

sudo apt install apparmor-utils

Jak wygląda przepustka

Profile leżą w /etc/apparmor.d/. Nazwa pliku to zwykle ścieżka programu z kropkami zamiast ukośników: /usr/bin/tcpdump ma profil usr.bin.tcpdump. Otwórzmy go:

less /etc/apparmor.d/usr.bin.tcpdump
profile tcpdump /usr/bin/tcpdump {
  #include <abstractions/base>
  #include <abstractions/nameservice>
  #include <abstractions/user-tmp>

  capability net_raw,
  network raw,
  network packet,
  ...
  # for -F and -w
  audit deny @{HOME}/.* mrwkl,
  owner @{HOME}/** rw,

  # for -r, -F and -w
  /**.[pP][cC][aA][pP] rw,
  /**.[pP][cC][aA][pP][nN][gG] rw,
  /**.[cC][aA][pP] rw,
  ...
  #include <local/usr.bin.tcpdump>
}

Czytamy jak przepustkę:

  • profile tcpdump /usr/bin/tcpdump — przepustka nazywa się tcpdump i dostaje ją program z tej ścieżki.
  • #include <abstractions/base> — gotowe zestawy uprawnień, których potrzebuje prawie każdy program: biblioteki, /dev/null i tak dalej. Jak „standardowy pakiet socjalny”.
  • capability net_raw — które supermoce roota wolno mu użyć. Tu: surowe gniazda sieciowe, bez tego nie podsłuchasz ruchu.
  • /**.[pP][cC][aA][pP] rw — czytaj i pisz pliki z końcówką .pcap, w dowolnym miejscu, w dowolnej wielkości liter. ** to „w dowolnej głębokości folderów”.
  • owner @{HOME}/** rw — we własnym folderze domowym wolno wszystko, ale tylko własne pliki.
  • audit deny @{HOME}/.* mrwkl — ale ukrytych plików w domu nie ruszać. Bo tam są klucze SSH i ustawienia. Gdyby ktoś przejął tcpdump, mógłby nadpisać Ci .bashrc.

I mamy zagadkę z haka. /srv/zrzut.txt nie kończy się na .pcap, nie jest w domu i nie jest w /tmp (to daje abstractions/user-tmp). Na przepustce nie ma takiego pokoju. Root czy nie root: odmowa.

Literki uprawnień to: r czytanie, w pisanie, m mapowanie do pamięci jako kod, k blokady, l dowiązania, x uruchamianie, najczęściej jako ix, czyli „uruchom z tą samą przepustką”. Każda reguła kończy się przecinkiem. Zapamiętaj ten przecinek, wróci w typowym błędzie.

Tryby: enforce i complain

Profil może pracować w dwóch trybach. Brzmi znajomo, bo to prawie to samo, co w SELinuksie:

  • enforce — pilnuje i blokuje. Odmowy trafiają do dziennika.
  • complain — wpuszcza, ale zapisuje, co by zablokował. Tryb do nauki i szukania problemów.

Przełączanie:

sudo aa-complain /usr/bin/tcpdump
sudo aa-enforce /usr/bin/tcpdump
Setting /usr/bin/tcpdump to complain mode.
Setting /usr/bin/tcpdump to enforce mode.

A co się naprawdę dzieje? Popatrz na pierwszą linijkę profilu po aa-complain:

grep '^profile' /etc/apparmor.d/usr.bin.tcpdump
profile tcpdump /usr/bin/tcpdump flags=(complain) {

Narzędzie po prostu dopisuje w pliku flags=(complain) i przeładowuje profil. aa-enforce to zdejmuje. Żadnej magii, edycja tekstu z przeładowaniem.

Jest też trzecie wyjście, awaryjne: wyłączenie jednego profilu.

sudo aa-disable /usr/bin/tcpdump
ls -l /etc/apparmor.d/disable/
Disabling /usr/bin/tcpdump.
lrwxrwxrwx 1 root root 34 ... usr.bin.tcpdump -> /etc/apparmor.d/usr.bin.tcpdump

W folderze disable pojawia się dowiązanie, a profil jest wyrzucany z jądra. To jak zabranie przepustki jednemu pracownikowi, a nie zwolnienie całej ochrony. Ale i tak: do szukania problemu używaj complain, nie disable.

Gdzie są odmowy

AppArmor zapisuje odmowy w dzienniku jądra. Najszybciej:

sudo journalctl -k | grep DENIED
sudo dmesg | grep DENIED

Wpis wygląda mniej więcej tak (rozbity na dwie linijki):

audit: type=1400 audit(1768214339.499:511): apparmor="DENIED" operation="mknod" class="file"
profile="tcpdump" name="/srv/zrzut.txt" pid=17786 comm="tcpdump" requested_mask="c" denied_mask="c" fsuid=0 ouid=0

Czytamy jak mandat z poprzedniej lekcji:

  • apparmor="DENIED" — zablokowane. W trybie complain zobaczysz tu ALLOWED.
  • operation="mknod" — próba utworzenia pliku.
  • profile="tcpdump" — która przepustka.
  • name= — do jakiej ścieżki.
  • requested_mask="c" — czego chciał: c jak create, utworzyć.

Jeśli masz zainstalowany auditd, te same wpisy lądują w /var/log/audit/audit.log. Bez niego w dzienniku systemd.

Dopisanie wyjątku: folder local

Chcesz, żeby tcpdump mógł zapisywać do /srv/zrzuty/? Nie edytuj głównego profilu. Przy aktualizacji pakietu może zostać nadpisany albo apt zacznie zadawać pytania. Popatrz na ostatnią linijkę profilu: #include <local/usr.bin.tcpdump>. To miejsce specjalnie na Twoje dopiski.

sudo mkdir -p /srv/zrzuty
echo '/srv/zrzuty/** rw,' | sudo tee -a /etc/apparmor.d/local/usr.bin.tcpdump
sudo apparmor_parser -r /etc/apparmor.d/usr.bin.tcpdump
sudo tcpdump -i any -c 10 -w /srv/zrzuty/test.txt

apparmor_parser -r to replace: wczytaj profil na nowo do jądra. Bez tego zmiana w pliku nic nie robi. To jak wydrukowanie nowej przepustki i zostawienie jej w drukarce.

Jedno ograniczenie: reguły deny wygrywają z regułami pozwalającymi. Ukrytych plików w domu nie odblokujesz dopiskiem w local, bo profil ma na nie wyraźne deny. I dobrze.

Własny profil

Do własnych programów służą dwa pomocniki. sudo aa-genprof /usr/local/bin/raport.sh tworzy szkic profilu: uruchamiasz program w drugim terminalu, robisz wszystko, co program normalnie robi, a narzędzie pyta o każdą zauważoną akcję: pozwolić, zabronić, uogólnić. Później sudo aa-logprof czyta dziennik i proponuje dopiski do istniejących profili.

Mały ręczny profil wygląda tak:

abi <abi/4.0>,
include <tunables/global>

profile raport /usr/local/bin/raport.sh {
  include <abstractions/base>
  include <abstractions/bash>

  /usr/local/bin/raport.sh r,
  /usr/bin/bash ix,
  /usr/bin/date ix,
  /usr/bin/df ix,
  /etc/hostname r,
  /var/log/raport/ r,
  /var/log/raport/** w,
}

Zanim go załadujesz, sprawdź składnię bez ładowania do jądra:

sudo apparmor_parser -QTK /etc/apparmor.d/usr.local.bin.raport && echo OK
OK

-Q znaczy „tylko skompiluj, nie ładuj”. Potem apparmor_parser -r i najlepiej najpierw aa-complain, żeby przez kilka dni zebrać w dzienniku to, o czym zapomniałeś.

Jak to działa pod spodem

AppArmor to, tak jak SELinux, moduł bezpieczeństwa jądra (LSM). Plik tekstowy profilu czyta apparmor_parser, kompiluje go do postaci binarnej i wysyła do jądra. Przy starcie systemu robi to usługa apparmor.service dla wszystkich profili z /etc/apparmor.d.

Gdy jakiś proces uruchamia program, jądro patrzy na ścieżkę pliku. Pasuje do któregoś profilu? Nowy proces dostaje tę przepustkę na całe życie. Potem przy każdym otwarciu pliku, użyciu supermocy czy gniazda sieciowego jądro sprawdza najpierw zwykłe uprawnienia, potem przepustkę.

I tu jest słaby punkt podejścia „po ścieżkach”: ten sam plik pod inną nazwą to dla AppArmora inna rzecz. Dlatego profile pilnują też tworzenia dowiązań (l). SELinux z naklejkami tego problemu nie ma, ma za to problem z mv. Nie ma idealnego ochroniarza, są tylko różne rodzaje pecha.

Ściąga flag

KomendaFlagaCo robi
aa-statusstan: profile, tryby, procesy pod nadzorem
aa-enforce/ścieżka/programuprofil w tryb enforce
aa-complain/ścieżka/programuprofil w tryb complain (dopisuje flags=(complain))
aa-disable/ścieżka/programuwyłącza jeden profil (dowiązanie w disable/)
apparmor_parser-rprzeładuj profil w jądrze
apparmor_parser-Rusuń profil z jądra
apparmor_parser-QTKtylko sprawdź składnię, bez ładowania i cache
aa-genprof/ścieżkainteraktywnie buduje nowy profil
aa-logprofproponuje dopiski na podstawie dziennika
journalctl-k | grep DENIEDodmowy w dzienniku jądra
systemctlreload apparmorprzeładuj wszystkie profile

⚠ Typowy błąd

Zapomniany przecinek. Dopisujesz regułę, przeładowujesz i dostajesz:

AppArmor parser error for /etc/apparmor.d/usr.local.bin.raport in profile ... at line 13: missing an end of line character? (entry: /etc/hostname)

Parser mówi „brakuje końca linijki”, ale chodzi o przecinek po /etc/hostname r. Każda reguła w profilu kończy się przecinkiem, nawet ostatnia przed klamrą. Gorzej, jeśli zmienisz profil i nie przeładujesz: plik mówi jedno, jądro pilnuje według starej wersji, a Ty od godziny szukasz błędu w miejscu, gdzie go już nie ma. Po każdej zmianie: apparmor_parser -r i aa-status.

✎ Zadanie

Na maszynie wirtualnej z Ubuntu:

  1. Sprawdź aa-status: ile profili jest w trybie enforce, a ile w complain?
  2. Spróbuj zapisać sudo tcpdump -i any -c 5 -w /srv/test.txt i znajdź odmowę w dzienniku jądra.
  3. Przełącz tcpdump w complain, ponów zapis, znajdź w dzienniku wpis ALLOWED i wróć do enforce.
  4. Pozwól tcpdumpowi zapisywać do /srv/zrzuty/ przez plik w /etc/apparmor.d/local/ i sprawdź, że działa.
Pokaż rozwiązanie

Ubuntu, AppArmor. Najpierw spis:

sudo aa-status | grep -E 'profiles are in (enforce|complain) mode'

U mnie kilkadziesiąt w enforce i kilka w complain. Liczby zależą od tego, co masz zainstalowane, więc nie porównuj się z sąsiadem. Potem odmowa:

sudo tcpdump -i any -c 5 -w /srv/test.txt
sudo journalctl -k | grep DENIED | tail -1

W dzienniku: apparmor="DENIED", profile="tcpdump", name="/srv/test.txt". Teraz tryb nauki:

sudo aa-complain /usr/bin/tcpdump
sudo tcpdump -i any -c 5 -w /srv/test.txt
sudo journalctl -k | grep ALLOWED | tail -1
sudo aa-enforce /usr/bin/tcpdump

Zapis przechodzi, a w dzienniku ten sam wpis, tylko z apparmor="ALLOWED". Ochroniarz przepuścił, ale zapisał w zeszycie. I na koniec wyjątek:

sudo mkdir -p /srv/zrzuty
echo '/srv/zrzuty/** rw,' | sudo tee -a /etc/apparmor.d/local/usr.bin.tcpdump
sudo apparmor_parser -r /etc/apparmor.d/usr.bin.tcpdump
sudo tcpdump -i any -c 5 -w /srv/zrzuty/test.txt

Jeśli zapomniałeś o aa-enforce po teście, to tcpdump do dziś chodzi bez nadzoru. Taki strażnik, który na próbę zostawił otwarte drzwi i poszedł na obiad. Sprawdź aa-status.

Co dalej

Mamy dwóch ochroniarzy. Ale ochroniarz nie pomoże, jeśli tylne drzwi są otwarte, a hasło do SSH to „admin123”. W następnej lekcji robimy przegląd całego budynku: utwardzanie serwera, fail2ban, auditd i automatyczni audytorzy, którzy wystawią Twojemu serwerowi ocenę.