SELinux

enforcing, kontexty, jak řešit denials.

SELinux (Security-Enhanced Linux) je bezpečnostní vrstva navíc nad klasickými právy. Běžná práva (kdo smí číst/zapisovat soubor) říkají, co komu patří. SELinux přidává politiku, která říká co která služba vůbec smí dělat — a platí i na roota. Když ti hackeři prolomí webserver, klasická práva mu možná dovolí sáhnout kam nemá; SELinux ho drží v ohrádce, ze které nesmí ven. Na distribucích z rodiny RHEL (Fedora, RHEL, Rocky, CentOS…) je zapnutý defaultně, takže se s ním dřív nebo později setkáš (Ubuntu a Debian mají obdobný AppArmor). Lepší je mu rozumět než ho vypínat.


Pár pojmů na úvod

  • DAC (Discretionary Access Control) = klasická unixová práva (rwx, vlastník, skupina). Rozhoduje o nich vlastník souboru — komu chce, tomu dá přístup. Root je nad tím vším a smí prakticky cokoli.
  • MAC (Mandatory Access Control) = to, co přidává SELinux. Pravidla určuje centrální politika, ne vlastník, a nejde je obejít — omezuje i roota. Proto „mandatory" (povinné, vynucené).
  • Kontext (label) = štítek nalepený na každém souboru a procesu ve tvaru user:role:type:level. Podle něj politika rozhoduje. Nejdůležitější je type.
  • Politika (policy) = sada pravidel „proces typu X smí sáhnout na soubor typu Y". Obvyklá je tzv. targeted policy — hlídá hlavně síťové služby (webserver, DB…), běžné uživatele nechává být.
  • AVC denial = záznam v logu „SELinux tohle zakázal". Když něco záhadně nejde, hledáš právě tohle.

DAC vs MAC — proč práva navíc

Představ si webserver httpd, který běží pod uživatelem apache. Klasická práva (DAC) mu dovolí číst všechno, na co má apache právo. Když ale někdo zneužije chybu v serveru, získá přesně tahle práva — a může začít šmejdit po systému, číst cizí konfiguráky, otevírat síťová spojení.

SELinux (MAC) přidá druhý zámek: webserver běží jako proces typu httpd_t a politika mu dovolí sáhnout jen na soubory typu httpd_sys_content_t, poslouchat jen na povolených portech a nic víc. I kdyby útočník server ovládl, pořád je uvězněný v tom, co smí httpd_t — ke zbytku systému se nedostane.

💡 Klíčová věta: DAC ověří „patří ti to?", MAC ověří „smíš to ty vůbec dělat?". Přístup projde, jen když ho povolí oba. SELinux nikdy nepřidává práva navíc — jen ubírá.


Režimy: enforcing, permissive, disabled

SELinux umí běžet ve třech režimech:

  • enforcing — politika se vynucuje: co je zakázané, se zablokuje a zapíše do logu. Normální stav.
  • permissive — politika se nevynucuje, ale porušení se loguje. Nic neblokuje — slouží k ladění: necháš appku běžet, posbíráš, co všechno by SELinux zakázal, a opravíš to. Ochrana je ale vypnutá.
  • disabled — SELinux úplně mimo. Žádné labely, žádná ochrana. Nedělej to.
getenforce              # vypíše aktuální režim (Enforcing / Permissive / Disabled)
sudo setenforce 0       # dočasně přepni na permissive (do rebootu)
sudo setenforce 1       # zpět na enforcing

Trvalé nastavení je v /etc/selinux/config (řádek SELINUX=enforcing). Změna režimu z/na disabled vyžaduje reboot (systém musí soubory přeznačkovat), přepnutí enforcing ↔ permissive jde za běhu.

🚩 Permissive je nástroj na ladění, ne cílový stav. Když něco nejde, přepni dočasně na permissive, ověř, jestli za to může SELinux (najednou to jede?), posbírej denials a oprav kontext. Pak zpět na enforcing. Nechat systém natrvalo v permissive/disabled = zahodíš celou vrstvu ochrany, kvůli které tam je.


Kontext (label): co je na souboru napsané

Kontext uvidíš přepínačem -Z — funguje u spousty nástrojů:

ls -Z /var/www/html          # kontext souborů
ps -Z                        # kontext procesů
id -Z                        # kontext tvého shellu

Výstup vypadá třeba takhle:

system_u:object_r:httpd_sys_content_t:s0   index.html

Rozklad user:role:type:level:

  • user (system_u) — SELinux uživatel (není totéž co unixový účet), zřídka řešíš.
  • role (object_r) — role, u souborů skoro vždy object_r.
  • type (httpd_sys_content_t) — tohle je to podstatné. Podle typu politika rozhoduje. Typy poznáš podle koncovky _t.
  • level (s0) — úroveň pro MLS/MCS (víceúrovňová bezpečnost), na desktopu/serveru většinou s0.

💡 V 95 % případů tě zajímá jen type. Když ti webserver nečte soubor, skoro vždy je to tím, že soubor má špatný type — a proces typu httpd_t na něj podle politiky nesmí.


restorecon, semanage, chcon — jak měnit kontext

Tři nástroje, snadno se pletou. Rozdíl je zásadní:

restorecon — nastaví soubor na správný kontext podle politiky. Politika ví, jaký kontext mít soubor na daném místě (např. cokoli v /var/www/htmlhttpd_sys_content_t). Tohle je tvoje každodenní záchrana:

sudo restorecon -Rv /var/www/html    # -R rekurzivně, -v ukáže, co změnil

semanage fcontext — přidá trvalé pravidlo do politiky: „soubory tady mají mít tenhle typ". Používáš, když děláš appku na netypickém místě (třeba web v /srv/web). Nejdřív pravidlo, pak restorecon:

sudo semanage fcontext -a -t httpd_sys_content_t "/srv/web(/.*)?"
sudo restorecon -Rv /srv/web

chcon — změní kontext dočasně a ručně. Rychlé, ale nepřežije restorecon ani relabel systému — politika o té změně neví, takže ji dřív nebo později přepíše. Dobré na rychlý test, ne na trvalo:

sudo chcon -t httpd_sys_content_t soubor.html   # jen dočasně!

🚩 Pořadí důležitosti: restorecon (oprav podle politiky) → semanage (uč politiku nové místo) → chcon (jen dočasný test). Když použiješ chcon a myslíš, že máš hotovo, první restorecon (nebo automatický relabel) ti to potichu vrátí zpátky.


Booleans: přepínače politiky

Někdy nechceš měnit labely, ale zapnout schopnost, kterou politika defaultně zakazuje. Na to slouží booleans — pojmenované přepínače on/off. Třeba „smí webserver navazovat odchozí síťová spojení?" je defaultně vypnuté (kvůli bezpečnosti), a když ho appka potřebuje (volá cizí API), zapneš boolean:

getsebool -a                          # vypiš všechny booleans a jejich stav
getsebool httpd_can_network_connect   # stav jednoho
sudo setsebool -P httpd_can_network_connect on   # zapni TRVALE (-P = persistent)

💡 Nezapomeň na -P! Bez něj se boolean vrátí po rebootu do původního stavu a „už to zase nejde". -P zapíše změnu natrvalo do politiky.

Booleans jsou elegantní: místo psaní vlastních pravidel jen zapneš předpřipravený scénář, který autoři politiky očekávali. Než začneš vyrábět vlastní pravidla přes audit2allow, vždycky se podívej, jestli na to není hotový boolean.


Ladění: když něco záhadně nejde

Typický příznak SELinuxu: appka funguje „skoro", ale nějaká akce tiše selže — 403 z webu, „permission denied" tam, kde práva (DAC) vypadají v pořádku. SELinux totiž akci zablokuje a zapíše AVC denial do audit logu. Postup:

# 1) najdi denials v audit logu
sudo ausearch -m avc -ts recent

# 2) čitelné vysvětlení + doporučení (balík setroubleshoot-server)
sudo sealert -a /var/log/audit/audit.log

sealert (ze setroubleshoot) je nejpřívětivější: řekne ti lidsky, co bylo zakázáno a co s tím — často rovnou navrhne správný restorecon nebo boolean. Na desktopu ti denial i vyskočí jako notifikace.

audit2allow umí z denialu vygenerovat vlastní pravidlo (modul politiky), které to povolí:

sudo ausearch -m avc -ts recent | audit2allow -M mojePravidlo
# sudo semodule -i mojePravidlo.pp     # teprve tohle ho nainstaluje

🚩 audit2allow používej opatrně a až jako poslední možnost. Slepě povolit každý denial znamená potichu odbourat přesně tu ochranu, kvůli které SELinux existuje. Nejdřív se ptej proč to bylo zakázáno: nemá soubor jen špatný kontext (→ restorecon)? Není na to boolean (→ setsebool)? Vlastní pravidlo dělej, až když víš, že přístup je legitimní a jinak to nejde.


Failure modes — časté omyly

  • Vypnu SELinux, ať mám klid. Nejčastější reflex — a nejhorší. Vypnutím (setenforce 0 natrvalo nebo disabled) neopravíš příčinu, jen zahodíš celou bezpečnostní vrstvu. Skoro vždy jde o špatný kontext souboru, což je práce na jeden restorecon. Řeš příčinu, ne symptom.
  • Přesunul jsem soubor přes mv a služba ho nečte. mv zachová původní kontext souboru z místa, odkud pochází (třeba user_home_t z domovské složky). Na novém místě má mít jiný typ, ale mv ho nezmění → služba na něj podle politiky nesmí. Oprava: restorecon na cíl. Naopak cp novému souboru přiřadí kontext podle cílové složky — proto cp často „prostě funguje" a mv ne.
  • Použil jsem chcon a za týden to zase nejde. chcon je dočasná ruční změna, o které politika neví. První restorecon nebo relabel systému (např. po velké aktualizaci) ji přepíše zpět. Na trvalé řešení patří semanage fcontext + restorecon.
  • Spustil jsem audit2allow na všechno. Vygenerovat pravidlo, které povolí každý denial, je jako vytrhnout baterku z hlásiče kouře, protože pípá. Klidně tím povolíš i to, co se snažil zablokovat útok. Vždy nejdřív pochop denial: kontext / boolean / až pak vlastní pravidlo.
  • Zapomněl jsem -P u setsebool. Změna funguje do rebootu a pak „záhadně" zmizí. Trvalé změny booleanů dělej s -P.

🛠️ Cvičení

  1. Podívej se na labely. Vypiš SELinux kontext souborů ve /var/www/html a kontext běžících procesů. Najdi u nějaké síťové služby (třeba sshd nebo httpd) její type a popiš, kterou část user:role:type:level bys řešil nejčastěji a proč.
  2. Enforcing vs permissive. Zjisti aktuální režim, dočasně přepni na permissive a zase zpět na enforcing. Kde je trvalé nastavení a proč přechod na/z disabled vyžaduje reboot?
  3. Rozbij a oprav kontext. Vytvoř soubor v domovské složce, přesuň ho přes mv do /var/www/html a porovnej jeho kontext (ls -Z) s ostatními soubory tam. Pak ho oprav správným nástrojem. Který to je?
  4. Najdi a použij boolean. Vypiš všechny booleans a najdi ten, který povolí webserveru navazovat odchozí síťová spojení. Jak ho zapneš tak, aby přežil reboot?
  5. Přečti denial. Najdi v audit logu poslední AVC denials (ausearch -m avc). Vyber jeden a popiš: který proces (type), na jaký objekt (type) a jakou akci se pokoušel — a jak bys zjistil, čím to opravit.
Náčrt řešení — rozbal, až si cvičení zkusíš sám
  1. Labelyls -Z /var/www/html (kontext souborů), ps -Z nebo ps -eZ | grep sshd (kontext procesů). U sshd uvidíš type sshd_t, u webu httpd_t. Řešíš skoro výhradně type — podle něj politika rozhoduje; user/role/level na běžném serveru měníš zřídka. Pozor: type poznáš podle koncovky _t.
  2. Režimygetenforce ukáže stav, sudo setenforce 0 → permissive, sudo setenforce 1 → enforcing (obojí za běhu, do rebootu). Trvale v /etc/selinux/config (SELINUX=). Přechod na/z disabled chce reboot, protože při vypnutém SELinuxu se soubory nelabelují — po zapnutí je systém musí celé přeznačkovat (relabel při bootu). Pozor: enforcing ↔ permissive jde za běhu, disabled ne.
  3. mv zachová kontext — nový soubor v domově má type user_home_t (nebo admin_home_t); po mv do /var/www/html si tenhle type ponese s sebou, kdežto sousední soubory mají httpd_sys_content_t. Webserver (httpd_t) na user_home_t nesmí → 403. Oprava: sudo restorecon -v soubor (nastaví kontext podle politiky). Pozor: cp by rovnou dostal správný type podle cílové složky — problém dělá hlavně mv.
  4. Booleangetsebool -a | grep httpd a najdeš httpd_can_network_connect. Zapnout trvale: sudo setsebool -P httpd_can_network_connect on. Bez -P se to po rebootu vrátí. Pozor: než píšeš vlastní pravidlo přes audit2allow, vždy hledej hotový boolean.
  5. Denialsudo ausearch -m avc -ts recent vypíše řádky se scontext= (source = proces, jeho type), tcontext= (target = objekt, jeho type) a { ... } (zakázaná akce, např. read, open, name_connect). Čitelně to rozebere sudo sealert -a /var/log/audit/audit.log a rovnou navrhne řešení (často restorecon nebo boolean). Pozor: nejdřív pochop, PROČ byl přístup zakázán — audit2allow až jako poslední možnost.

🧠 Otázky & odpovědi

Jaký je rozdíl mezi klasickými právy a SELinuxem?

Klasická práva jsou DAC (Discretionary Access Control) — o přístupu rozhoduje vlastník souboru přes rwx a root je nad vším. SELinux přidává MAC (Mandatory Access Control): pravidla určuje centrální politika, nejde je obejít a omezují i roota. Rozdíl v jedné větě — DAC řeší co komu patří, MAC řeší co která služba vůbec smí dělat. Přístup projde jen tehdy, když ho povolí oba. SELinux nikdy nepřidává práva navíc, jen ubírá, takže i prolomená služba zůstane uvězněná v tom, co jí politika dovolí.

Proč se nemá SELinux vypínat, když něco nejde?

Protože vypnutím neopravíš příčinu, jen zahodíš celou bezpečnostní vrstvu. V drtivé většině případů jde jen o špatný kontext souboru — což je práce na jeden restorecon. Když opravdu potřebuješ ladit, přepni dočasně na permissive (setenforce 0): SELinux pak nic neblokuje, ale loguje, co by zakázal. Posbíráš denials, opravíš kontext nebo zapneš boolean a vrátíš se na enforcing. Permissive je nástroj na ladění, ne cílový stav — disabled natrvalo znamená, že jsi zbytečně zbavil ochrany, kvůli které tam SELinux je.

Co je kontext a která jeho část je nejdůležitější?

Kontext (label) je štítek na každém souboru a procesu ve tvaru user:role:type:level — uvidíš ho přepínačem -Z (ls -Z, ps -Z). Politika podle něj rozhoduje, kdo smí na co. Zdaleka nejdůležitější je type (koncovka _t, třeba httpd_sys_content_t u webových souborů, httpd_t u procesu webserveru). V praxi skoro každý SELinux problém je o tom, že soubor má špatný type a proces na něj podle politiky nesmí. user, role a level na běžném serveru řešíš jen výjimečně.

Proč soubor přesunutý přes mv služba nepřečte, ale přes cp ano?

Protože mv zachová původní kontext souboru — přinese si type z místa, odkud pochází (třeba user_home_t z domovské složky). Na novém místě má mít jiný type, ale mv ho nemění, takže služba (např. httpd_t) na něj podle politiky nesmí. Naproti tomu cp vytvoří nový soubor, který dostane kontext podle cílové složky — proto „prostě funguje". Oprava po mv je jednoduchá: restorecon na soubor nastaví správný kontext podle politiky.

K čemu jsou booleans a proč nezapomenout na -P?

Booleans jsou pojmenované přepínače politiky (on/off), kterými zapneš nebo vypneš celý předpřipravený scénář — třeba httpd_can_network_connect povolí webserveru odchozí síťová spojení, která jsou defaultně zakázaná. Vypíšeš je getsebool -a, nastavíš setsebool. Přepínač -P znamená persistent — zapíše změnu natrvalo do politiky. Bez -P funguje boolean jen do rebootu a pak „záhadně" zmizí. Booleans jsou lepší volba než psát vlastní pravidla přes audit2allow — pokud na tvůj případ boolean existuje, použij ho.