SELinux bez strachu
Po tej lekcji: kursant rozumie, czym SELinux różni się od zwykłych uprawnień, sprawdza i zmienia tryb, czyta etykiety plików i procesów, naprawia złą etykietę, port i przełącznik (boolean) oraz umie przeczytać odmowę z dziennika, zamiast wyłączać SELinuksa.
Wstęp
przeglądarka, duży napis Forbidden, obok terminal z ls -l i uprawnieniami -rw-r--r--
Rocky Linux, świeży Apache. Wrzucasz stronę, otwierasz przeglądarkę: 403 Forbidden. Sprawdzasz uprawnienia: wszyscy mogą czytać. Dajesz chmod 777. Dalej 403. Dajesz właściciela apache. Dalej 403. Wpisujesz błąd w wyszukiwarkę, a pierwsza odpowiedź na forum brzmi: „wyłącz SELinuksa”. Działa! Wszyscy szczęśliwi.
To jest jak wyjęcie baterii z czujnika dymu, bo piszczy przy smażeniu kotletów. Kotlety wychodzą, dom może spłonąć. Dzisiaj zrobimy to porządnie: przeczytamy, dlaczego czujnik piszczy, i naprawimy przyczynę jedną komendą.
Drugi ochroniarz w biurowcu
Znasz już uprawnienia: klucze do pokojów. Właściciel pliku decyduje, kto dostaje klucz. To się nazywa DAC, Discretionary Access Control, kontrola dostępu według uznania właściciela. Problem: jak włamywacz przejmie serwer WWW, przejmuje też jego klucze. A jak przejmie roota, ma pęk kluczy do wszystkiego.
SELinux dokłada drugiego ochroniarza. Ten nie patrzy na klucze. Patrzy na identyfikatory. Każdy pracownik ma plakietkę w kolorze działu, każdy pokój ma naklejkę. Ochroniarz ma w ręku regulamin: „dział WWW wchodzi do pokojów z naklejką WWW. Kropka.” Nieważne, że masz klucz. Nieważne, że jesteś dyrektorem. Plakietka nie pasuje do naklejki, nie wchodzisz.
To się nazywa MAC, Mandatory Access Control, obowiązkowa kontrola dostępu. Regulamin ustala administrator, a nie właściciel pliku. I ważna kolejność: ochroniarz SELinuksa pyta dopiero wtedy, gdy klucze już pozwoliły. Jak zwykłe uprawnienia mówią „nie”, SELinux nawet się nie odzywa i nic nie zapisuje do dziennika.
SELinux powstał w amerykańskiej agencji NSA, a dziś rozwija go społeczność i Red Hat. Na RHEL, Rocky, Almie i Fedorze jest włączony od instalacji. [Ubuntu] Na Ubuntu i Debianie domyślnie siedzi inny ochroniarz, AppArmor, i jemu poświęcimy cały następna lekcja.
Etykiety: plakietki i naklejki
Każdy plik i każdy proces ma etykietę, po angielsku context. Wygląda jak cztery słowa sklejone dwukropkami:
system_u:object_r:httpd_sys_content_t:s0Użytkownik SELinuksa, rola, typ i poziom. Z tych czterech na co dzień obchodzi Cię jeden: typ, ten z końcówką _t. Prawie cały regulamin jest pisany na typach. Reszta jest dla wojska i banków.
Etykiety pokazuje stara znajoma flaga -Z. Działa w ls, ps i id:
ls -Z /var/www/html/index.html
ls -dZ /var/www/html
ps -eZ | grep httpd
id -Z[RHEL] Na Rocky zobaczysz mniej więcej to:
unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html
system_u:object_r:httpd_sys_content_t:s0 /var/www/html
system_u:system_r:httpd_t:s0 1234 ? 00:00:00 httpd
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023Czytamy: proces Apache ma plakietkę httpd_t. Pliki strony mają naklejkę httpd_sys_content_t. Regulamin mówi: httpd_t może czytać httpd_sys_content_t. A Ty jako człowiek jesteś unconfined_t, czyli „bez nadzoru”. Ochroniarz pilnuje głównie usług, a nie ludzi przy terminalu.
[Ubuntu] Na Ubuntu te same komendy nie mają czego pokazać:
$ ls -Z /etc/hostname
? /etc/hostname
$ id -Z
id: --context (-Z) works only on an SELinux-enabled kernelZnak zapytania znaczy: „naklejek tu nie ma, ten budynek ma innego ochroniarza”.
Trzy tryby pracy
Ochroniarz może pracować na trzy sposoby:
- enforcing — pilnuje i nie wpuszcza. Tryb domyślny i zalecany.
- permissive — wpuszcza każdego, ale zapisuje w zeszycie, kogo by nie wpuścił. Idealny do szukania problemu.
- disabled — ochroniarz poszedł do domu. Nikt nic nie zapisuje, a nowe pliki nie dostają naklejek.
getenforce
sudo setenforce 0 # permissive, do restartu
getenforce
sudo setenforce 1 # z powrotem enforcing
sestatusEnforcing
Permissive
...
Current mode: enforcing
Mode from config file: enforcingsetenforce działa tylko do restartu. Na stałe ustawiasz linijkę SELINUX= w pliku /etc/selinux/config: enforcing, permissive albo disabled.
Złota zasada: na produkcji, jak coś szukasz, przełączasz na permissive. Nie na disabled. Permissive dalej notuje w zeszycie, więc po teście wiesz dokładnie, co by zablokował.
Zła naklejka: restorecon i semanage fcontext
90% problemów z SELinuksem to zła naklejka na pliku. Skąd się bierze? Najczęściej z mv.
Zobacz. Robisz stronę w domowym folderze, a potem przenosisz:
mv ~/index.html /var/www/html/
ls -Z /var/www/html/index.htmlunconfined_u:object_r:user_home_t:s0 /var/www/html/index.htmlmv przenosi plik razem z naklejką. A naklejka mówi user_home_t, czyli „rzecz z mieszkania pracownika”. Apache ma plakietkę WWW, więc ochroniarz go nie wpuszcza. 403. Gdybyś zrobił cp, nowa kopia dostałaby naklejkę folderu docelowego i wszystko by działało. Taki szczegół, a potrafi zjeść pół dnia.
Naprawa jednym poleceniem:
sudo restorecon -Rv /var/www/html/Relabeled /var/www/html/index.html from unconfined_u:object_r:user_home_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0restorecon to „przywróć naklejki zgodnie z regulaminem”. -R w głąb folderu, -v mów, co zmieniasz. Skąd wie, jaka naklejka jest właściwa? Z bazy reguł: ścieżka → typ.
A jak trzymasz stronę w nietypowym miejscu, na przykład w /srv/strona? Wtedy baza nic o nim nie wie. Najpierw dopisujesz regułę, potem przyklejasz:
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/strona(/.*)?"
sudo restorecon -Rv /srv/stronasemanage fcontext -a dopisuje regułę do regulaminu, -t podaje typ. Dziwny ogon (/.*)? to wyrażenie regularne: „ten folder i wszystko w środku”. I uwaga: semanage sam niczego nie przykleja. Zmienia tylko regulamin. Przykleja restorecon. To jak wydrukowanie nowego planu biura: naklejki na drzwiach same się nie zmienią, ktoś musi przejść z rolką.
Jest też skrót: „traktuj ten folder jak /var/www”:
sudo semanage fcontext -a -e /var/www /srv/stronaIstnieje jeszcze chcon -t, które przykleja naklejkę od razu, bez regulaminu. Kusi, ale przy najbliższym restorecon albo pełnym przelepianiu wszystko wraca. Jak pisanie długopisem na naklejce zamiast drukowania nowej.
[RHEL] semanage jest w pakiecie policycoreutils-python-utils. Na świeżym serwerze często go brak: sudo dnf install policycoreutils-python-utils.
Zły port: semanage port
SELinux pilnuje też portów. Apache ma prawo otwierać tylko okienka z naklejką http_port_t:
sudo semanage port -l | grep httphttp_cache_port_t tcp 8080, 8118, 8123, 10001-10010
http_cache_port_t udp 3130
http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000Ustawiasz w Apache Listen 3131 i usługa nie wstaje: Permission denied: AH00072: make_sock: could not bind to address [::]:3131. Root, a „Permission denied”. Rozwiązanie:
sudo semanage port -a -t http_port_t -p tcp 3131
sudo systemctl restart httpdPamiętaj, że to nie otwiera portu w zaporze. Ochroniarz SELinuksa i ochroniarz z listą okienek z lekcji 73 to dwie różne osoby. Każdemu trzeba osobno powiedzieć.
Przełączniki: getsebool i setsebool
Regulamin ma gotowe przełączniki na typowe sytuacje, tak zwane booleany. Jak pstryczki w skrzynce: „czy Apache może łączyć się z bazą danych”, „czy może czytać foldery domowe”, „czy może korzystać z NFS”.
getsebool -a | grep httpd
sudo semanage boolean -l | grep httpd_can_network_connect
sudo setsebool -P httpd_can_network_connect_db on
getsebool httpd_can_network_connect_dbhttpd_can_network_connect_db --> onFlaga -P to persistent, na stałe. Bez niej przełącznik wraca po restarcie. To klasyka egzaminu RHCSA: wszystko działa, egzaminator restartuje maszynę i nagle nie działa. Bo zabrakło jednej litery.
Czytanie odmów
Teraz najważniejsze: skąd wiedzieć, co zablokował ochroniarz. Każdą odmowę zapisuje w dzienniku audytu, /var/log/audit/audit.log, jako komunikat typu AVC.
sudo ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -ts recent-m to typy komunikatów, -ts recent to „z ostatnich dziesięciu minut”. Wynik wygląda strasznie, ale czyta się go jak mandat:
type=AVC msg=audit(1226874073.147:96): avc: denied { getattr } for pid=2465 comm="httpd"
path="/var/www/html/file1" dev=dm-0 ino=284133 scontext=unconfined_u:system_r:httpd_t:s0
tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=filedenied { getattr }— czego zabroniono: odczytu informacji o pliku.comm="httpd"— kto próbował: Apache.path=— do czego.scontext— plakietka sprawcy:httpd_t.tcontext— naklejka celu:samba_share_t, czyli plik oklejony dla Samby.tclass=file— rodzaj celu: plik.
Czyli: „Apache chciał zajrzeć do pliku, który ma naklejkę Samby”. Diagnoza w jednym zdaniu. Dla portu zobaczysz { name_bind } i tclass=tcp_socket.
Jeszcze wygodniej z narzędziem sealert z pakietu setroubleshoot-server. Tłumaczy odmowę na ludzki język i podsuwa naprawę:
sudo dnf install setroubleshoot-server
sudo sealert -l "*"
journalctl -t setroubleshootSELinux is preventing httpd from getattr access on the file /var/test_www/html/index.html.Czasem sealert zaproponuje audit2allow, czyli zbudowanie własnej reguły z odmowy. Red Hat wprost pisze, żeby to nie był pierwszy wybór. Kolejność naprawiania jest zawsze ta sama: najpierw naklejka, potem przełącznik, potem port. Własna reguła dopiero na końcu, jak już naprawdę wiesz, co robisz.
Jak to działa pod spodem
SELinux to moduł bezpieczeństwa jądra, tak zwany LSM, Linux Security Module. Jądro przy każdej ważnej operacji, czyli otwarciu pliku, uruchomieniu programu, otwarciu portu, woła „haczyk” i pyta moduł: wolno? Najpierw jednak sprawdza zwykłe uprawnienia. SELinux odpowiada na podstawie załadowanej polityki. Na RHEL to polityka targeted: pilnuje usług sieciowych, a ludzi zostawia jako unconfined_t.
Odpowiedzi trafiają do pamięci podręcznej, która nazywa się AVC, Access Vector Cache. Stąd nazwa komunikatów w dzienniku. Dzięki temu jądro nie przelicza regulaminu przy każdym odczycie bajtu.
Naklejki plików są zapisane w samym systemie plików, w rozszerzonych atrybutach. A plakietkę proces dostaje przy starcie: systemd uruchamia /usr/sbin/httpd, którego plik ma typ httpd_exec_t, a regulamin mówi, że program z takiego pliku startuje jako httpd_t. To się nazywa przejście domeny.
Ściąga flag
| Komenda | Flaga | Co robi |
|---|---|---|
getenforce | pokazuje tryb: Enforcing, Permissive, Disabled | |
setenforce | 0 / 1 | permissive / enforcing do restartu |
sestatus | pełny stan, tryb bieżący i z pliku | |
ls, ps, id | -Z | pokazują etykiety plików, procesów, Twoją |
restorecon | -Rv | przykleja naklejki wg regulaminu, w głąb, z opisem |
semanage fcontext | -a -t TYP "ŚCIEŻKA(/.*)?" | dopisuje regułę ścieżka → typ |
semanage fcontext | -a -e WZÓR CEL | „traktuj CEL jak WZÓR” |
semanage port | -l / -a -t TYP -p tcp PORT | lista / dopisanie portu do typu |
semanage boolean | -l | przełączniki z opisami |
getsebool | -a | stan wszystkich przełączników |
setsebool | -P | zmienia przełącznik na stałe |
ausearch | -m AVC -ts recent | odmowy z ostatnich minut |
sealert | -l "*" | odmowy po ludzku z poradą |
chcon | -t TYP | ręczna naklejka, znika przy restorecon |
⚠ Typowy błąd
„Zrobiłem setenforce 0 i działa, to zostawiam.”
Po restarcie serwera tryb wraca do tego, co jest w /etc/selinux/config, i problem wraca razem z nim. Zwykle w nocy. A jeśli ktoś „na stałe” wpisze disabled, to nowe pliki przestają dostawać naklejki i przy ponownym włączeniu system potrafi nie wstać bez przelepienia całego dysku.
Poprawna droga ma cztery kroki: setenforce 0, sprawdzasz, czy problem znika. Znika? To SELinux. Czytasz ausearch albo sealert. Naprawiasz naklejkę, przełącznik albo port. I od razu setenforce 1. Permissive to latarka do szukania, nie tryb życia.
✎ Zadanie
Na maszynie wirtualnej z Rocky albo Almą (SELinux w trybie enforcing):
- Zainstaluj Apache (
httpd), uruchom go i zrób plikstrona.htmlw swoim folderze domowym. Przenieś go przezmvdo/var/www/html/. Sprawdźcurl localhost/strona.html. - Znajdź odmowę przez
ausearchi powiedz, jaką naklejkę ma plik. Napraw ją jedną komendą. - Sprawdź, jaką wartość ma przełącznik
httpd_enable_homedirs, włącz go na stałe i potwierdź, że po restarcie maszyny dalej jeston.
Pokaż rozwiązanie
Rocky, SELinux w trybie enforcing. Najpierw psujemy:
sudo dnf install -y httpd
sudo systemctl enable --now httpd
echo '<h1>Działa!</h1>' > ~/strona.html
sudo mv ~/strona.html /var/www/html/
curl -s -o /dev/null -w '%{http_code}\n' localhost/strona.html403Szukamy winnego:
sudo ausearch -m AVC -ts recent
ls -Z /var/www/html/strona.htmlW odmowie zobaczysz comm="httpd", scontext=...httpd_t... i najważniejsze: tcontext=...user_home_t.... Plik przyjechał z mieszkania razem ze swoją naklejką. Naprawa:
sudo restorecon -v /var/www/html/strona.html
curl -s -o /dev/null -w '%{http_code}\n' localhost/strona.html200I przełącznik:
getsebool httpd_enable_homedirs
sudo setsebool -P httpd_enable_homedirs on
sudo reboot
getsebool httpd_enable_homedirshttpd_enable_homedirs --> off
...
httpd_enable_homedirs --> onJeśli po restarcie wróciło off, to wiesz, której litery zabrakło. Litery P jak „Pamiętaj”. I uwaga: sam przełącznik nie wystarczy, żeby Apache naprawdę serwował strony z /home. Potrzebny jeszcze moduł userdir w konfiguracji Apache i uprawnienia do folderu. SELinux to tylko jeden z trzech zamków w tych drzwiach.
Co dalej
SELinux nakleja etykiety na wszystko. Ubuntu poszło inną drogą: zamiast naklejek każdy program dostaje listę dozwolonych ścieżek. W następnej lekcji AppArmor: profile, tryby i to, jak go uciszyć, nie wyłączając całości.