Tritt ein Fehler in der SELinux-Konfiguration eines Servers auf, fehlt vielen Admins oft die Zeit, dem eigentlichen Problem auf den Grund zu gehen. Kurzerhand lassen sie die Maschine ohne aktivierten SELinux-Schutz weiterlaufen und verschieben die Problemlösung auf später. Dieser Artikel zeigt, dass es auch anders geht: Mit dem nötigen Know-how und einigen kleinen Tricks läuft jede Anwendung auch mit aktivem Schutzschild.
SELinux ist ein Security-Framework auf Basis der Mandatory Access Control, das Framework basiert auf dem Flask-Forschungsprojekt der amerikanischen NSA-Behörde [1]. Im Standardkernel ist es über das Linux-Security-Module-API (LSM) integriert. Alle Interaktionen zwischen so genannten Subjekten und Objekten sind durch eine gesonderte Security-Policy zu autorisieren.
Die Anweisungen enthalten dabei jedoch keine Datei-, Prozess- oder Benutzernamen, stattdessen kommt ein abstrahiertes Model mit Security-Labels zum Einsatz. Prozesse und Benutzer bekommen diese dynamisch zugewiesen, Dateien speichern das Label in den erweiterten Attributen. Es ist möglich, diese Policy zur Laufzeit des Systems zu ändern. Hierbei findet aber eine strikte Trennung von Policy und deren Durchsetzung statt.
Für das Enforcement sind diverse Hooks im Linux-Kernel zuständig. Sie erweitern die klassischen Zugriffsrechte der Discretionary Access Control (DAC) um die entsprechende MAC-Funktionalität. Über eine binäre Policy-Datei gelangen die eigentlichen Regelwerke in den Security-Server des Linux-Kernels.
SELinux kennt drei Modi: Im Enforcing-Mode ist jeder einzelne Zugriff einer Kontrolle durch die SELinux-Policy ausgesetzt. Jeder Zugriff wird ausgewertet und gegebenenfalls untersagt, wenn es keine Regel gibt, die diesen Zugriff erlaubt. Im Permissive Mode wird ebenfalls jeder Zugriff überwacht, allerdings kommt es nicht zur Durchsetzung der Regeln. Das bedeutet, dass ein Zugriff selbst dann stattfindet, wenn keine Regel den Zugriff explizit erlaubt.
SELinux schreibt für jeden nicht durch eine Regel erlaubten Zugriff einen Logeintrag, allerdings nur beim ersten Mal. Alle weiteren Zugriffe unterdrückt das System stillschweigend. Im Disabled Mode ist SELinux überhaupt nicht aktiv. Es kommen nur Zugriffsentscheidungen auf Basis der DAC zum Einsatz. Dabei muss der Administrator daran denken, dass Dateien, die im Disabled-Modus erzeugt wurden, auch kein Security-Label bekommen.
Zur Erweiterung des klassischen Discretionary-Access-Control-Modells stellt SELinux verschiedene Implementierungen zur Verfügung:
Die wichtigste Implementierung ist hierbei das Type Enforcement. Es regelt den Zugriff von einem Subjekt, etwa einem Prozess, auf die diversen Objekte, beispielsweise Dateien oder Verzeichnisse. Allerdings gibt es eine Vielzahl unterschiedlicher Objekte. Zu diesen zählen neben Dateien und Verzeichnissen beispielsweise auch Netzwerkports. Die den Subjekten und Objekten zugewiesenen Security-Label sind auch unter dem Namen Domäne oder Typ bekannt. Allgemein ausgedrückt regelt das Type Enforcement, welche Domäne auf welchen Typ zugreifen darf.
Genau hier liegt oft das Problem. Besitzt ein Objekt nicht den richtigen Typ, ist der Zugriff darauf nicht möglich, wenn das System sich im SELinux-Enforcing-Modus befindet. Ein Logeintrag in der Datei »/var/log/audit/audit.log
«
sieht dann etwa wie in Listing 1 aus.
Listing 1
Zugriff verweigert
01 type=AVC msg=audit(1217917079.027:14267): avc: denied { getattr } for 02 pid=10054 comm="httpd" path="/var/www/html/index.html" dev=dm-2 ino=1974309 03 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_tmp_t:s0 04 tclass=file
Der Logtyp bestimmt hierbei den Ursprung der Nachricht. AVC steht für Access Vector Cache und ist ein Teil des Security-Servers im Kernel, der Zugriffsvektoren zwischenspeichert. Der Logtyp »SYSCALL
«
bezieht sich beispielsweise auf Events, die das Audit-Subsystem festhält, wenn es um das Monitoring von Systemcalls geht. Mit Hilfe von »ausearch -m Typ
«
ist ein Zugriff auf einen ganz bestimmten Logtyp möglich.
Der nächste Eintrag gibt den Zeitpunkt des Events in Unix-Zeit an. Mit »date -d @Unix-Zeit
«
lässt sich diese leicht in ein von Menschen lesbares Format verwandeln. Im Anschluss erfolgt die genaue Art der Logmeldung, also ob es sich um eine Deny-Meldung handelt oder nur um eine Information, etwa ob die SELinux-Policy neu geladen wurde oder ob sich der SELinux-Modus geändert hat.
Die nächsten beiden Angaben (»pid
«
und »comm
«
) bestimmen die Herkunft der Fehlermeldung, hier also einen Webserver-Prozess mit seiner Prozessnummer. Das Objekt der Begierde folgt unmittelbar in Form eines Dateipfads (»path
«
), Device (»dev
«
) und Inode-Nummer (»ino
«
). Da für die Interaktion zwischen Prozess und Datei jedoch Security-Label die größte Bedeutung haben, sind diese natürlich ebenfalls aufgeführt. »scontext
«
steht hierbei für den Source-Context des Webservers, »tcontext
«
für den Target-Context der Datei. Zum Abschluss gibt die Logmeldung noch Auskunft über den genauen Objekt-Typ. Damit ist die Unterscheidung von Objekten mit gleichem Security-Label möglich.
Seit einiger Zeit gibt es für diese doch recht kryptischen Logeinträge eine Alternative. Fedora Core hat mit Version 6 den Setroubleshoot-Daemon [2] eingeführt. Bei dieser Gnome-Anwendung handelt es sich um ein Plugin-basiertes Tool mit vielen unterschiedlichen Regelsätzen, das – abhängig vom stattgefundenen Event – dem Benutzer mit Rat und Tat zur Seite steht. Auch lassen sich mehrere Benachrichtigungs-Mechanismen einstellen. So kann der Benutzer veranlassen, dass er eine Mail erhält, wenn das SELinux-System eine nicht erlaubte Aktion unterbindet. Benutzer eines grafischen Desktops haben die Möglichkeit, ein Sealert-Applet in ihre Taskbar einzubinden, um sich direkt durch ein Pop-up über aktuelle Meldungen des Setroubleshoot-Daemon zu informieren. Die Logeinträge der Anwendung finden sich in der Datei »/var/log/messages
«
(Listing 2).
Listing 2
/var/log/messages
01 Aug 5 08:18:07 tiffy setroubleshoot: SELinux is preventing the httpd from 02 using potentially mislabeled files (/var/www/html/index.html). For complete 03 SELinux messages. run sealert -l 774df3c6-badf-4f42-b1f3-e8ff94897d6b
Folgt der Benutzer dem Rat des Setroubleshoot-Daemon und ruft den Befehl »sealert
«
mit den angegebenen Parametern auf, erhält er eine detaillierte Übersicht über das aufgetretene Problem und Informationen darüber, wie er es beheben kann (Listing 3).
Listing 3
sealert
01 [root@tiffy ~]# sealert -l 774df3c6-badf-4f42-b1f3-e8ff94897d6b 02 Summary: 03 04 SELinux is preventing the httpd from using potentially mislabeled files 05 (/var/www/html/index.html). 06 07 Detailed Description: 08 09 [SELinux is in permissive mode, the operation would have been denied but was 10 permitted due to permissive mode.] 11 12 SELinux has denied httpd access to potentially mislabeled file(s) 13 (/var/www/html/index.html). This means that SELinux will not allow httpd to 14 use these files. It is common for users to edit files in their home directory 15 or tmp directories and then move (mv) them to system directories. The problem 16 is that the files end up with the wrong file context which confined applications 17 are not allowed to access. 18 19 Allowing Access: 20 21 If you want httpd to access this files, you need to relabel them using 22 restorecon -v '/var/www/html/index.html'. You might want to relabel the entire 23 directory using restorecon -R -v '/var/www/html'. 24 Additional Information: 25 26 Source Context system_u:system_r:httpd_t:s0 27 Target Context unconfined_u:object_r:user_tmp_t:s0 28 Target Objects /var/www/html/index.html [ file ] 29 Source httpd 30 Source Path /usr/sbin/httpd 31 Port <Unknown> 32 Host tiffy.tuxgeek.de 33 Source RPM Packages httpd-2.2.8-3 34 Target RPM Packages 35 Policy RPM selinux-policy-3.3.1-79.fc9 36 Selinux Enabled True 37 Policy Type targeted 38 MLS Enabled True 39 Enforcing Mode Permissive 40 Plugin Name home_tmp_bad_labels 41 Host Name tiffy.tuxgeek.de 42 Platform Linux tiffy.tuxgeek.de 2.6.25.9-76.fc9.i686 #1 43 SMP 44 Fri Jun 27 16:14:35 EDT 2008 i686 i686 45 Alert Count 1 46 First Seen Tue Aug 5 08:17:59 2008 47 Last Seen Tue Aug 5 08:17:59 2008 48 Local ID 774df3c6-badf-4f42-b1f3-e8ff94897d6b 49 Line Numbers
Diese Meldung weist schon auf das eigentliche Problem hin. Die Datei »/var/www/html/index.html
«
besitzt das falsche Security-Label, somit ist ein Zugriff des Webservers hierauf nicht möglich. Eine mögliche Lösung ist ebenfalls aufgeführt:
# restorecon -R -v /var/www/html # ls -ldZ /var/www/html drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 /var/www/html
Der Aufruf von »restorecon
«
sorgt dafür, dass jedes Objekt im Verzeichnis »/var/www/html
«
das richtige Label bekommt. Danach sollte der Zugriff wieder funktionieren. Aber welches ist in diesem Zusammenhang eigentlich das richtige Label? Es ist jenes, das diese Datei in der binären Policy besitzt. Mit Hilfe von »semanage
«
bekommt der Admin schon im Vorfeld raus, wie das hinterlegte Label lautet:
# semanage fcontext -l | grep /var/www /var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
Was soll der Admin aber machen, um den Zugriff auf ein Objekt zu erlauben, das noch über keinen Eintrag in der Policy verfügt? In einem solchen Fall ist die Datei mit dem gewünschten Label in die Policy aufzunehmen. Das funktioniert ebenfalls mit dem Tool »semanage
«
. Angenommen das Webserver-Documentroot befindet sich unterhalb von »/www
«
, dann ist auch dieses Verzeichnis, mit allen darin enthaltenen Dateien, mit dem Label »httpd_sys_content_t
«
zu versehen. Der folgende Aufruf fügt der Policy einen entsprechenden Eintrag hinzu:
# semanage fcontext --add --type public_content_rw_t '/www(/.*)?' # restorecon -Rv /www
Der abschließende Aufruf von »restorecon
«
setzt das neue Label auf das Verzeichnis und alle darin enthaltenen Dateien.
Genauso wie der Zugriff auf Dateien oder Verzeichnisse anhand eines Security-Labels stattfindet, geschieht es auch mit Netzwerkports. Das Tool »semanage
«
bindet das Security-Label an einen Port:
semanage port -a -t http_port_t -p tcp 8000
Hier wird der TCP-Port 8000 mit dem Label »httpd_port_t
«
versehen. Verifizieren lässt sich dies mit dem erneuten Aufruf von »semanage
«
:
# semanage port -l |grep http_port_t http_port_t tcp 80, 443, 488, 8000, 8008, 8009
Manchmal funktioniert eine Anwendung im Enforcing-Modus nicht, die Logdatei zeigt jedoch keine Fehlermeldung an. In der Policy existieren nämlich so genannte Dontaudit-Regeln, die einen Zugriff zwar unterbinden, ihn aber nicht loggen. Das ist ganz praktisch, wenn zum Beispiel eine Anwendung Aktionen durchführen will, die für eine korrekte Funktion aber nicht notwendig sind. Da SELinux nach dem "Principle of least Privilege" arbeitet, sind solche Zugriffe nicht erlaubt. Natürlich soll aber auch nicht jedes Mal ein Eintrag im Log erfolgen.