SELinux-cheat-sheet: Unterschied zwischen den Versionen
| Zeile 7: | Zeile 7: | ||
== Modi temporär umschalten == | == Modi temporär umschalten == | ||
| − | |||
;Wechsel zu Permissive | ;Wechsel zu Permissive | ||
* setenforce 0 | * setenforce 0 | ||
| − | |||
;Wechsel zu Enforcing | ;Wechsel zu Enforcing | ||
* setenforce 1 | * setenforce 1 | ||
| Zeile 20: | Zeile 18: | ||
== Dateikontexte verwalten == | == Dateikontexte verwalten == | ||
| − | |||
;Kontexte anzeigen | ;Kontexte anzeigen | ||
zeigt alle Prozesse mit Kontexten | zeigt alle Prozesse mit Kontexten | ||
| Zeile 29: | Zeile 26: | ||
wird durch restorecon oder Relabel überschrieben | wird durch restorecon oder Relabel überschrieben | ||
* chcon -t httpd_sys_content_t /var/www/html/index.html | * chcon -t httpd_sys_content_t /var/www/html/index.html | ||
| + | |||
| + | ;Kontext zurücksetzen auf Policy-Vorgabe | ||
| + | stellt den durch fcontext-Regeln definierten Kontext wieder her, unabhängig von manuellen chcon-Änderungen | ||
| + | * restorecon -v /pfad/zur/datei | ||
| + | * restorecon -Rv /pfad/zum/verzeichnis | ||
;Kontext dauerhaft definieren und anwenden | ;Kontext dauerhaft definieren und anwenden | ||
| + | semanage trägt die Regel in die Policy-Datenbank ein, restorecon wendet sie auf bestehende Dateien an | ||
* semanage fcontext -a -t httpd_sys_content_t "/custom/www(/.*)?" | * semanage fcontext -a -t httpd_sys_content_t "/custom/www(/.*)?" | ||
* restorecon -Rv /custom/www | * restorecon -Rv /custom/www | ||
| + | |||
| + | ;Vorhandene Kontext-Regeln nachschlagen | ||
| + | hilfreich um den passenden Typ für einen neuen Anwendungsfall zu finden, statt zu raten | ||
| + | * semanage fcontext -l | grep httpd | ||
| + | |||
| + | === Regex-Grundlagen bei semanage fcontext === | ||
| + | {{Achtung| | ||
| + | '''semanage fcontext nutzt POSIX Extended Regex, keine Shell-Glob-Syntax.''' Das führt regelmäßig zu Verwechslungen, weil die Sonderzeichen anders funktionieren als man es aus der Shell (Bash-Globbing) gewohnt ist. | ||
| + | }} | ||
| + | |||
| + | {| class="wikitable" | ||
| + | ! Shell-Glob !! Regex !! Bedeutung | ||
| + | |- | ||
| + | | <code>*</code> || <code>.*</code> || "beliebig viele Zeichen" – in Regex ist <code>*</code> allein nur "vorheriges Element beliebig oft wiederholen", kein eigenständiges Wildcard | ||
| + | |- | ||
| + | | <code>?</code> || <code>.</code> || "genau ein beliebiges Zeichen" – in Regex bedeutet <code>?</code> "vorheriges Element optional", nicht "ein Zeichen" | ||
| + | |} | ||
| + | |||
| + | Konkretes Beispiel aus der Praxis: | ||
| + | |||
| + | <pre> | ||
| + | semanage fcontext -a -t httpd_sys_content_t "/var/repo(/.*)?" | ||
| + | </pre> | ||
| + | |||
| + | {| class="wikitable" | ||
| + | ! Teil !! Bedeutung | ||
| + | |- | ||
| + | | <code>/var/repo</code> || literaler, fester Text – muss genau so vorkommen | ||
| + | |- | ||
| + | | <code>( )</code> || Gruppierung – fasst den eingeschlossenen Ausdruck zusammen | ||
| + | |- | ||
| + | | <code>/</code> || literales Zeichen – ein fester Slash | ||
| + | |- | ||
| + | | <code>.</code> || beliebiges einzelnes Zeichen (Wildcard) | ||
| + | |- | ||
| + | | <code>*</code> || 0 bis unendlich Wiederholungen des davorstehenden Elements (hier: <code>.</code>) | ||
| + | |- | ||
| + | | <code>.*</code> || zusammen: beliebig viele beliebige Zeichen (auch keins) | ||
| + | |- | ||
| + | | <code>(/.*)</code> || Gruppe: ein <code>/</code> gefolgt von beliebig vielem Inhalt | ||
| + | |- | ||
| + | | <code>?</code> || macht die davorstehende Gruppe optional (0 oder 1 mal) | ||
| + | |- | ||
| + | | <code>(/.*)?</code> || die ganze Gruppe "/ + beliebiger Inhalt" ist optional | ||
| + | |} | ||
| + | |||
| + | '''Matched:''' | ||
| + | * <code>/var/repo</code> (Gruppe fehlt – erlaubt durch <code>?</code>) | ||
| + | * <code>/var/repo/</code> (Gruppe = <code>/</code> + leerer Rest) | ||
| + | * <code>/var/repo/index.html</code> | ||
| + | * <code>/var/repo/sub/dir/datei.txt</code> (beliebige Tiefe) | ||
| + | |||
| + | '''Matched nicht:''' | ||
| + | * <code>/var/repository</code> (kein <code>/</code> oder Ende direkt nach <code>repo</code>) | ||
| + | * <code>/var/repo2</code> | ||
| + | |||
| + | {{Hinweis| | ||
| + | Eine tückische Variante ist <code>/etc/ssl/own.*</code> (ohne Gruppierung). Das sieht aus wie Shell-Glob ("own, dann beliebiger Suffix"), ist aber reine Regex: der <code>.</code> matched ein beliebiges Zeichen, <code>*</code> wiederholt diesen Punkt beliebig oft. Trifft <code>own.crt</code> und <code>own.key</code> zufällig auch, aber aus einem anderen Grund als man beim Lesen vermutet – nämlich "own + ein-beliebiges-Zeichen + beliebig oft wiederholt", nicht "own + beliebiger Suffix". Sauberer wäre <code>/etc/ssl/own\.(crt|key)</code>. | ||
| + | }} | ||
== Booleans verwenden == | == Booleans verwenden == | ||
| + | Booleans sind An/Aus-Schalter für ganze Verhaltensklassen einer SELinux-Policy, unabhängig von Datei-Labels. Sie werden nur gebraucht, wenn ein Dienst ein Verhalten zeigt, das die Standard-Policy für seinen Typ nicht automatisch erlaubt. | ||
;Alle Booleans anzeigen | ;Alle Booleans anzeigen | ||
* getsebool -a | * getsebool -a | ||
| − | |||
;Spezifischen Wert prüfen | ;Spezifischen Wert prüfen | ||
* getsebool httpd_can_network_connect | * getsebool httpd_can_network_connect | ||
| − | |||
;Boolean dauerhaft aktivieren | ;Boolean dauerhaft aktivieren | ||
* setsebool -P httpd_can_network_connect on | * setsebool -P httpd_can_network_connect on | ||
| + | |||
| + | {{Hinweis| | ||
| + | Nicht jedes SELinux-Problem ist ein Boolean-Problem. Ein Webserver, der nur statischen Content ausliefert und PHP über einen lokalen Unix-Socket an PHP-FPM übergibt, braucht in der Regel '''keine''' zusätzlichen Booleans – <code>httpd_can_network_connect</code> ist z. B. nur relevant, wenn httpd selbst aktiv Netzwerkverbindungen aufbaut (Reverse-Proxy zu einem Backend, externe API-Calls etc.). Vor dem Setzen eines Booleans erst prüfen, ob das eigentliche Problem nicht schlicht ein fehlender oder falscher Dateikontext (fcontext) ist – das ist der weitaus häufigere Fall bei "Forbidden"-Fehlern auf eigenem Content. | ||
| + | }} | ||
== Ports verwalten == | == Ports verwalten == | ||
| − | |||
;Erlaubte Ports für einen Typ anzeigen | ;Erlaubte Ports für einen Typ anzeigen | ||
* semanage port -l | grep http_port_t | * semanage port -l | grep http_port_t | ||
| − | |||
;Port zu einem Typ hinzufügen | ;Port zu einem Typ hinzufügen | ||
* semanage port -a -t http_port_t -p tcp 8080 | * semanage port -a -t http_port_t -p tcp 8080 | ||
falls bereits vorhanden: | falls bereits vorhanden: | ||
* semanage port -m -t http_port_t -p tcp 8080 | * semanage port -m -t http_port_t -p tcp 8080 | ||
| + | |||
| + | {{Hinweis| | ||
| + | Nur relevant, wenn ein Dienst auf einem '''Nicht-Standard-Port''' läuft (z. B. httpd auf 8080/8443 statt 80/443). Die Standardports sind bereits als <code>http_port_t</code> gelabelt – für einen gewöhnlichen Webserver auf 80/443 ist hier nichts zu tun. | ||
| + | }} | ||
== Fehlersuche und Analyse == | == Fehlersuche und Analyse == | ||
| + | ;Vorhandene Policy-Regel prüfen | ||
| + | zeigt ob eine bestimmte Erlaubnis in der Policy überhaupt existiert, bevor man einen Boolean setzt oder ein Custom-Modul baut | ||
| + | * sesearch --allow -s httpd_t -t var_t -c file | ||
;Logs nach aktuellen Blockaden (AVC) durchsuchen | ;Logs nach aktuellen Blockaden (AVC) durchsuchen | ||
* ausearch -m avc -ts recent | * ausearch -m avc -ts recent | ||
| + | größeres Zeitfenster, falls "recent" nichts findet: | ||
| + | * ausearch -m avc -ts today | ||
| + | * ausearch -m avc -ts boot | ||
| + | |||
zeigt die Ursache der Blockade verständlich | zeigt die Ursache der Blockade verständlich | ||
* ausearch -m avc -ts recent | audit2why | * ausearch -m avc -ts recent | audit2why | ||
| Zeile 71: | Zeile 145: | ||
* grep "denied" /var/log/audit/audit.log | audit2allow -M mycustommodule | * grep "denied" /var/log/audit/audit.log | audit2allow -M mycustommodule | ||
* semodule -i mycustommodule.pp | * semodule -i mycustommodule.pp | ||
| + | |||
| + | == Praxisbeispiel: "Forbidden" trotz korrekter Apache-Config == | ||
| + | Typischer Ablauf einer SELinux-bedingten 403-Fehlersuche, wie sie beim Einrichten eines eigenen DocumentRoot (z. B. <code>/var/repo</code> statt <code>/var/www/html</code>) auftritt: | ||
| + | |||
| + | # Apache-Config ist syntaktisch korrekt (<code>apachectl configtest</code> → Syntax OK), Dienst startet, Browser zeigt trotzdem "Forbidden". | ||
| + | # Prüfen: <code>ausearch -m avc -ts recent</code> – zeigt AVC-Denial mit <code>tcontext=unconfined_u:object_r:var_t:s0</code> statt <code>httpd_sys_content_t</code>. | ||
| + | # Ursache: Der Pfad liegt außerhalb des von der Policy bereits abgedeckten <code>/var/www/html</code> und hat dadurch den generischen <code>var_t</code>-Kontext, den httpd nicht lesen darf. | ||
| + | # Fix: eigenen fcontext-Eintrag anlegen und anwenden. | ||
| + | <pre> | ||
| + | semanage fcontext -a -t httpd_sys_content_t "/var/repo(/.*)?" | ||
| + | restorecon -Rv /var/repo | ||
| + | </pre> | ||
| + | # Erneuter Test im Browser – Zugriff funktioniert. | ||
| + | |||
| + | {{Hinweis| | ||
| + | <code>/var/www/html</code> ist von Haus aus bereits mit <code>httpd_sys_content_t</code> gelabelt. Sobald ein eigener Pfad außerhalb davon als DocumentRoot verwendet wird, ist ein eigener fcontext-Eintrag praktisch immer nötig – das ist kein Fehler in der Apache-Config, sondern erwartetes SELinux-Verhalten. | ||
| + | }} | ||
== Best Practices == | == Best Practices == | ||
* Betrieb im Modus 'enforcing' sicherstellen. | * Betrieb im Modus 'enforcing' sicherstellen. | ||
* Modus 'permissive' nur zur Fehlersuche nutzen. | * Modus 'permissive' nur zur Fehlersuche nutzen. | ||
| − | * 'semanage fcontext' gegenüber 'chcon' bevorzugen. | + | * 'semanage fcontext' gegenüber 'chcon' bevorzugen (dauerhaft statt flüchtig). |
| − | * Vor Erstellung eigener Module prüfen, ob ein passender Boolean existiert. | + | * Vor Erstellung eigener Module prüfen, ob ein passender Boolean existiert (<code>getsebool -a</code>). |
| + | * Vor dem Setzen eines Booleans prüfen, ob nicht eigentlich ein fehlender fcontext-Eintrag die Ursache ist – bei "Forbidden" auf eigenem Content meist der Fall. | ||
| + | * Bei fcontext-Regex-Mustern beachten: POSIX Extended Regex, keine Shell-Glob-Syntax (<code>.*</code> statt <code>*</code>, <code>.</code> statt <code>?</code>). | ||
* Relabeling des Dateisystems bei Bedarf: touch /.autorelabel | * Relabeling des Dateisystems bei Bedarf: touch /.autorelabel | ||
Version vom 8. Juli 2026, 16:11 Uhr
Allgemeines
SELinux (Security-Enhanced Linux) implementiert Mandatory Access Control (MAC). Es isoliert Prozesse in Sicherheitskontexten, um das Schadenspotenzial bei Kompromittierungen zu minimieren.
Status und Modi prüfen
- sestatus
- getenforce
Modi temporär umschalten
- Wechsel zu Permissive
- setenforce 0
- Wechsel zu Enforcing
- setenforce 1
Modus dauerhaft setzen
Konfiguration in der Datei: /etc/selinux/config
- SELINUX=enforcing
- SELINUX=permissive
Dateikontexte verwalten
- Kontexte anzeigen
zeigt alle Prozesse mit Kontexten
- ps -eZ
- ls -Z /pfad/zur/datei
- Kontext temporär ändern
wird durch restorecon oder Relabel überschrieben
- chcon -t httpd_sys_content_t /var/www/html/index.html
- Kontext zurücksetzen auf Policy-Vorgabe
stellt den durch fcontext-Regeln definierten Kontext wieder her, unabhängig von manuellen chcon-Änderungen
- restorecon -v /pfad/zur/datei
- restorecon -Rv /pfad/zum/verzeichnis
- Kontext dauerhaft definieren und anwenden
semanage trägt die Regel in die Policy-Datenbank ein, restorecon wendet sie auf bestehende Dateien an
- semanage fcontext -a -t httpd_sys_content_t "/custom/www(/.*)?"
- restorecon -Rv /custom/www
- Vorhandene Kontext-Regeln nachschlagen
hilfreich um den passenden Typ für einen neuen Anwendungsfall zu finden, statt zu raten
- semanage fcontext -l | grep httpd
Regex-Grundlagen bei semanage fcontext
⚠️ Achtung: semanage fcontext nutzt POSIX Extended Regex, keine Shell-Glob-Syntax. Das führt regelmäßig zu Verwechslungen, weil die Sonderzeichen anders funktionieren als man es aus der Shell (Bash-Globbing) gewohnt ist.
| Shell-Glob | Regex | Bedeutung |
|---|---|---|
* |
.* |
"beliebig viele Zeichen" – in Regex ist * allein nur "vorheriges Element beliebig oft wiederholen", kein eigenständiges Wildcard
|
? |
. |
"genau ein beliebiges Zeichen" – in Regex bedeutet ? "vorheriges Element optional", nicht "ein Zeichen"
|
Konkretes Beispiel aus der Praxis:
semanage fcontext -a -t httpd_sys_content_t "/var/repo(/.*)?"
| Teil | Bedeutung |
|---|---|
/var/repo |
literaler, fester Text – muss genau so vorkommen |
( ) |
Gruppierung – fasst den eingeschlossenen Ausdruck zusammen |
/ |
literales Zeichen – ein fester Slash |
. |
beliebiges einzelnes Zeichen (Wildcard) |
* |
0 bis unendlich Wiederholungen des davorstehenden Elements (hier: .)
|
.* |
zusammen: beliebig viele beliebige Zeichen (auch keins) |
(/.*) |
Gruppe: ein / gefolgt von beliebig vielem Inhalt
|
? |
macht die davorstehende Gruppe optional (0 oder 1 mal) |
(/.*)? |
die ganze Gruppe "/ + beliebiger Inhalt" ist optional |
Matched:
/var/repo(Gruppe fehlt – erlaubt durch?)/var/repo/(Gruppe =/+ leerer Rest)/var/repo/index.html/var/repo/sub/dir/datei.txt(beliebige Tiefe)
Matched nicht:
/var/repository(kein/oder Ende direkt nachrepo)/var/repo2
ℹ️ Hinweis:
Eine tückische Variante ist /etc/ssl/own.* (ohne Gruppierung). Das sieht aus wie Shell-Glob ("own, dann beliebiger Suffix"), ist aber reine Regex: der . matched ein beliebiges Zeichen, * wiederholt diesen Punkt beliebig oft. Trifft own.crt und own.key zufällig auch, aber aus einem anderen Grund als man beim Lesen vermutet – nämlich "own + ein-beliebiges-Zeichen + beliebig oft wiederholt", nicht "own + beliebiger Suffix". Sauberer wäre /etc/ssl/own\.(crt
Booleans verwenden
Booleans sind An/Aus-Schalter für ganze Verhaltensklassen einer SELinux-Policy, unabhängig von Datei-Labels. Sie werden nur gebraucht, wenn ein Dienst ein Verhalten zeigt, das die Standard-Policy für seinen Typ nicht automatisch erlaubt.
- Alle Booleans anzeigen
- getsebool -a
- Spezifischen Wert prüfen
- getsebool httpd_can_network_connect
- Boolean dauerhaft aktivieren
- setsebool -P httpd_can_network_connect on
ℹ️ Hinweis:
Nicht jedes SELinux-Problem ist ein Boolean-Problem. Ein Webserver, der nur statischen Content ausliefert und PHP über einen lokalen Unix-Socket an PHP-FPM übergibt, braucht in der Regel keine zusätzlichen Booleans – httpd_can_network_connect ist z. B. nur relevant, wenn httpd selbst aktiv Netzwerkverbindungen aufbaut (Reverse-Proxy zu einem Backend, externe API-Calls etc.). Vor dem Setzen eines Booleans erst prüfen, ob das eigentliche Problem nicht schlicht ein fehlender oder falscher Dateikontext (fcontext) ist – das ist der weitaus häufigere Fall bei "Forbidden"-Fehlern auf eigenem Content.
Ports verwalten
- Erlaubte Ports für einen Typ anzeigen
- semanage port -l | grep http_port_t
- Port zu einem Typ hinzufügen
- semanage port -a -t http_port_t -p tcp 8080
falls bereits vorhanden:
- semanage port -m -t http_port_t -p tcp 8080
ℹ️ Hinweis:
Nur relevant, wenn ein Dienst auf einem Nicht-Standard-Port läuft (z. B. httpd auf 8080/8443 statt 80/443). Die Standardports sind bereits als http_port_t gelabelt – für einen gewöhnlichen Webserver auf 80/443 ist hier nichts zu tun.
Fehlersuche und Analyse
- Vorhandene Policy-Regel prüfen
zeigt ob eine bestimmte Erlaubnis in der Policy überhaupt existiert, bevor man einen Boolean setzt oder ein Custom-Modul baut
- sesearch --allow -s httpd_t -t var_t -c file
- Logs nach aktuellen Blockaden (AVC) durchsuchen
- ausearch -m avc -ts recent
größeres Zeitfenster, falls "recent" nichts findet:
- ausearch -m avc -ts today
- ausearch -m avc -ts boot
zeigt die Ursache der Blockade verständlich
- ausearch -m avc -ts recent | audit2why
- journalctl -t setroubleshoot
- Analyse der Log-Einträge
Paket ggf. erforderlich: setroubleshoot-server
- sealert -a /var/log/audit/audit.log
- Eigene Policy-Module aus Logs erstellen
nur nach Prüfung verwenden – kann Sicherheitsregeln aufweichen
- grep "denied" /var/log/audit/audit.log | audit2allow -M mycustommodule
- semodule -i mycustommodule.pp
Praxisbeispiel: "Forbidden" trotz korrekter Apache-Config
Typischer Ablauf einer SELinux-bedingten 403-Fehlersuche, wie sie beim Einrichten eines eigenen DocumentRoot (z. B. /var/repo statt /var/www/html) auftritt:
- Apache-Config ist syntaktisch korrekt (
apachectl configtest → Syntax OK), Dienst startet, Browser zeigt trotzdem "Forbidden".
- Prüfen:
ausearch -m avc -ts recent – zeigt AVC-Denial mit tcontext=unconfined_u:object_r:var_t:s0 statt httpd_sys_content_t.
- Ursache: Der Pfad liegt außerhalb des von der Policy bereits abgedeckten
/var/www/html und hat dadurch den generischen var_t-Kontext, den httpd nicht lesen darf.
- Fix: eigenen fcontext-Eintrag anlegen und anwenden.
semanage fcontext -a -t httpd_sys_content_t "/var/repo(/.*)?"
restorecon -Rv /var/repo
- Erneuter Test im Browser – Zugriff funktioniert.
ℹ️ Hinweis:
/var/www/html ist von Haus aus bereits mit httpd_sys_content_t gelabelt. Sobald ein eigener Pfad außerhalb davon als DocumentRoot verwendet wird, ist ein eigener fcontext-Eintrag praktisch immer nötig – das ist kein Fehler in der Apache-Config, sondern erwartetes SELinux-Verhalten.
Best Practices
- Betrieb im Modus 'enforcing' sicherstellen.
- Modus 'permissive' nur zur Fehlersuche nutzen.
- 'semanage fcontext' gegenüber 'chcon' bevorzugen (dauerhaft statt flüchtig).
- Vor Erstellung eigener Module prüfen, ob ein passender Boolean existiert (
getsebool -a).
- Vor dem Setzen eines Booleans prüfen, ob nicht eigentlich ein fehlender fcontext-Eintrag die Ursache ist – bei "Forbidden" auf eigenem Content meist der Fall.
- Bei fcontext-Regex-Mustern beachten: POSIX Extended Regex, keine Shell-Glob-Syntax (
.* statt *, . statt ?).
- Relabeling des Dateisystems bei Bedarf: touch /.autorelabel