Przejdź do treści
Lekcja 82 z 117

Kurs › Moduł 9 › Lekcja 82

SELinux bez strachu

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

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:s0

Uż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.c1023

Czytamy: 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 kernel

Znak 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
sestatus
Enforcing
Permissive
...
Current mode:                   enforcing
Mode from config file:          enforcing

setenforce 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.html
unconfined_u:object_r:user_home_t:s0 /var/www/html/index.html

mv 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:s0

restorecon 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/strona

semanage 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/strona

Istnieje 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 http
http_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, 9000

Ustawiasz 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 httpd

Pamię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_db
httpd_can_network_connect_db --> on

Flaga -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=file
  • denied { 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 setroubleshoot
SELinux 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

KomendaFlagaCo robi
getenforcepokazuje tryb: Enforcing, Permissive, Disabled
setenforce0 / 1permissive / enforcing do restartu
sestatuspełny stan, tryb bieżący i z pliku
ls, ps, id-Zpokazują etykiety plików, procesów, Twoją
restorecon-Rvprzykleja 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 PORTlista / dopisanie portu do typu
semanage boolean-lprzełączniki z opisami
getsebool-astan wszystkich przełączników
setsebool-Pzmienia przełącznik na stałe
ausearch-m AVC -ts recentodmowy z ostatnich minut
sealert-l "*"odmowy po ludzku z poradą
chcon-t TYPrę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):

  1. Zainstaluj Apache (httpd), uruchom go i zrób plik strona.html w swoim folderze domowym. Przenieś go przez mv do /var/www/html/. Sprawdź curl localhost/strona.html.
  2. Znajdź odmowę przez ausearch i powiedz, jaką naklejkę ma plik. Napraw ją jedną komendą.
  3. Sprawdź, jaką wartość ma przełącznik httpd_enable_homedirs, włącz go na stałe i potwierdź, że po restarcie maszyny dalej jest on.
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.html
403

Szukamy winnego:

sudo ausearch -m AVC -ts recent
ls -Z /var/www/html/strona.html

W 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.html
200

I przełącznik:

getsebool httpd_enable_homedirs
sudo setsebool -P httpd_enable_homedirs on
sudo reboot
getsebool httpd_enable_homedirs
httpd_enable_homedirs --> off
...
httpd_enable_homedirs --> on

Jeś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.