SELinux-cheat-sheet: Unterschied zwischen den Versionen

Aus Xinux Wiki
Zur Navigation springen Zur Suche springen
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 nach repo)
  • /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:

  1. Apache-Config ist syntaktisch korrekt (apachectl configtest → Syntax OK), Dienst startet, Browser zeigt trotzdem "Forbidden".
  2. Prüfen: ausearch -m avc -ts recent – zeigt AVC-Denial mit tcontext=unconfined_u:object_r:var_t:s0 statt httpd_sys_content_t.
  3. 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.
  4. Fix: eigenen fcontext-Eintrag anlegen und anwenden.
semanage fcontext -a -t httpd_sys_content_t "/var/repo(/.*)?"
restorecon -Rv /var/repo
  1. 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