SELinux

enforcing, kontexty, jak řešit denials.

Co se naučíš: Přestaneš SELinux vypínat a naučíš se přečíst, co přesně zakázal a jak to povolit správně.

14 min čtení + cvičeníNavazuje na:🔐 Práva hlouběji

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á mít soubor na daném místě (např. cokoli v /var/www/html → httpd_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. Labely, ls -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žimy, getenforce 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. Boolean, getsebool -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. Denial, sudo 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.