SELinux-cheat-sheet
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