Ein Linux-System in der Funktion als Server bietet zahlreiche Netzwerk-Services an. Zwar werden nicht alle gebraucht, doch sie bieten einen recht freien Zugriff auf Ressourcen. Und auch ein benötigter Service sollte nicht jedem System und Benutzer den Zugriff erlauben.
Nachdem ein grundlegender Funktionstest erfolgreich durchlaufen ist, empfiehlt es sich im nächsten Schritt, das Linux-System an die Bedürfnisse anzupassen. Allgemein spricht man hier im anglizistischen Sprachgebrauch von "Customizing" oder speziell von "Hardening", wenn das System wirklich nur die vorgegebenen Aufgaben und Anforderungen erfüllen soll. Es sollten also nur die notwendigen Prozesse mit minimalen Rechten aktiv sein.
Grob kann man den Zugriff auf ein Linux-System differenzieren nach dem Zugriff von außen auf das System (über die lokale Konsole oder das Netzwerk), dem ausgehenden Netzwerkverkehr und den erlaubten Aktionen auf dem System (von innen). Deaktivieren wir das Netzwerk-Interface, so kann ein Benutzer, der Zugriff zum System über die Konsole erhalten hat, noch Daten manipulieren oder über die vorhandenen I/O-Schnittstellen (USB, Firewire, FiberChannel, eSata) diese kopieren.
Hardening
Ein IT-System mit dem Betriebssystem Linux sicherer zu machen ermöglicht auch einen Blick hinter die Kulissen, und dabei kann man einige grundlegende Fragen beantworten:
-
Was wird benötigt?
-
Was läuft wann auf dem System?
-
Welche Logins existieren? Sind Passwörter gesetzt oder Accounts gesperrt?
-
Wie erfolgt der Netzwerkzugriff von und zu welchen Ports?
-
Wie erfolgt der Aufruf des Prozesses und in welchem Kontext?
-
Welche Zugriffsrechte werden benötigt?
-
Wer darf was auf diesem System?
Einem Desktop-System mit immer dem gleichen Benutzer braucht man in der Regel keine restriktiven Vorgaben zu machen. Aber auch hier gilt: Je restriktiver, desto sicherer. Zudem lässt sich eine "Stateful"-Firewall, wenn diese schon im Kernel vorhanden ist, mit einfachen Mitteln in kurzer Zeit aktivieren.
Grundsätzlich zeigt sich, dass über die Jahre die Linux-Distributionen restriktiver mit Ressourcen und Zugängen umgehen. Der X11-Window-Server erlaubt keinen oder nur lokalen Netzwerkzugriff, die Firewall gestattet lediglich eingehende Antwortpakete, der Nameserver verrichtet seine Tätigkeit in einem Chroot-Umfeld, und viele privilegierte Prozesse verwerfen ihre Rechte.
Aus der Praxis
Ein Prozess mit Root-Rechten (privilegierter Prozess), der vollen Zugriff auf das Dateisystem hat und an keine Prozesslimits stößt, ist für viele Applikationsentwickler der Idealfall. So braucht man sich im Vorfeld keine Gedanken über Zugriffsrechte und Isolation zu machen.
Allerdings gibt es dafür eine Reihe von Nachteilen:
-
Ressourcen: Jedem Prozess werden standardmäßig nur begrenzte Ressourcen (etwa Speichernutzung, Datei-Handles oder Rechenzeit) zugewiesen, die sich per "ulimit" ansehen und modifizieren lassen. Die Softlimits lassen sich bis zum Hardlimit ausreizen, und viele Werte sehen als Vorgabe "unlimited", also nicht begrenzt, vor. Als Benutzer mit Root-Rechten kann man aber auch die Hardlimits entsprechend mit zum Systemmaximum inkrementieren, sodass eine eigentliche Begrenzung für das System nicht mehr zum Tragen kommt.
-
Dateisystem - reservierte Blöcke: Die meisten Dateisysteme reservieren eine Anzahl an Blöcken für Prozesse mit der Benutzer-ID 0. Ein nicht privilegierter Prozess sollte also niemals in der Lage sein, ein Dateisystem zu 100 Prozent zu füllen, wenn diese Reserve vorhanden ist. Ein Prozess, der mit Root-Rechten gestartet wurde und diese auch nicht verändert, kann diese reservierten Blöcke ebenfalls nutzen, was ein Eingreifen des Systemadministrators massiv erschwert, da im Dateisystem kein freier Platz mehr vorhanden ist, der für einen normalen Betrieb notwendig wäre.
-
Dateisystem - Quota: Mit Quota bezeichnet man ein Accounting für die Nutzung von Dateiblöcken und I-Nodes pro Benutzer- und Gruppen-ID. Es wird auch hier nach Soft- und Hardlimits differenziert, wobei Letzteres nach dem Überschreiten des (größeren) Softlimits für eine vorgegebene Zeit zur Anwendung kommt.
-
Zwar lassen sich auch für den User "Root"-Quotalimits auf Dateisystemebene setzen, aber wenn eine Datei keinem definierten Benutzer oder Daemon-Account zugeordnet wurde, ist der Eigentümer meist "Root", und damit fallen dann sehr viele Dateien unter das Accounting. Von daher betrachtet empfiehlt es sich kaum, dieses Limit zu setzen.
-
Zugriffsrechte: Der Prozess mit der Benutzer-ID 0 hat auch vollen Zugriff auf alle lokalen Dateisysteme, kann daher leicht vieles löschen, Prozesse starten oder beenden. Die klassische Zugriffkontrolle mit "Root darf alles", ist daher ein Risiko. Eine erweiterte Zugriffskontrolle, wie beispielsweise bei Security Enhanced Linux (SELinux) oder AppArmore, bietet hier auch die Möglichkeit, diese Zugriffe zu kontrollieren. Allgemein sollten Prozesse, wann immer möglich, nicht mit den vollen Rechten im System (UID und/ oder GID=0) aktiv sein. Denn sollte es einem anderen Prozess oder Benutzer möglich sein, über den privilegierten Prozess Aktionen auszuführen, so erfolgt dies ohne Restriktionen. Dies wird noch oft beim sogenannten Buffer-Overflow, das heißt dem Überschreiben von Daten und Ausführen von Programmcode auf dem Stack, ausgenutzt.
Privilegien
Linux- wie auch traditionelle Unix-Implementierungen unterscheiden nach privilegierten (effektive Benutzer-ID ist null) und nicht privilegierten Prozessen. Erstere umgehen die meisten Kernel-Zugriffskontrollen. Die anderen durchlaufen die vollen Zugriffskontrollen, basierend auf den Prozessrechten (normalerweise: die effektive UID, effektive GID und zusätzliche Gruppenzugehörigkeit).
Seit Kernel 2.2 unterteilt Linux die privilegierten Zugriffsrechte, den sogenannten Capabilities, die auf Thread-Basis unabhängig voneinander de- und aktiviert werden können.
Ansatzpunkte für Hardening
Möchte man ein Linux-System härten, so bieten sich nachfolgende Ansatzpunkte an:
Netzwerk:
-
Kommunikation ausschließlich über Sockets oder Localhost für lokalen Zugriff
-
tcp-wrapper, xinetd und Applikations-Zugriffsrestriktionen nutzen
-
Integrierte Firewall 'iptables' eingehend nur für notwendige Ports und Protokolle öffnen
-
Syslog- und Audit-Protokolle aktivieren und auf einem anderen System kopieren
-
Unsichere Protokolle und Services durch sicherere Ausführungen ersetzen
Prozesse:
-
Nur notwendige Prozesse starten
-
Prozesse nicht als root (uid=0) laufen lassen, sondern als unprivilegierte Prozesse
-
Prozess-Limits (ulimits) für Speicher, Anzahl Datei-Handles, Prozesslaufzeit usw. nutzen
-
Prozesse in einer Chroot-Umgebung einrichten
-
Virtualisierung nutzen (qemu, kvm, xen, lxc)
Ressourcen:
-
AppArmor oder SELinux aktivieren
-
ACLs einsetzen
-
Mandatory Access Controls (MAC)
-
I/O-Ports über den udev-daemon deaktivieren
Hardening: Virtualisierung & SELinux
Die Linux Container (lxc) bieten die Möglichkeit, einzelne Prozesse oder ein vollständiges Betriebssystem vom laufenden System zu isolieren und eine Ressourcensteuerung zu implementieren.
Auch andere Virtualisierungslösungen wie qemu, KVM, XEN oder VMware trennen das laufende System vom Gastsystem und bieten so zwar einen erhöhten Verwaltungsaufwand, aber eine gewisse Isolierung ist vorhanden.
SELinux
Sind Dateien und Verzeichnisse beim Einsatz von SELinux korrekt mit einem Label versehen, dann wird jeder Zugriff überwacht. Aktivieren lässt es sich zu jeder Zeit per "setenforce enforcing", wobei hier zwischen den beiden Modi gewechselt wird. Der Vorgang "labeln" ordnet jedem Dateiobjekt einen Kontext zu, beispielsweise "f_tmp" für Prozesse und Verzeichnisse, die nach /tmp oder /var/tmp schreiben dürfen.
Zwei wesentliche Modi kommen hier im täglichen Betrieb vor:
-
permissive
-
enforced
Der Permissive-Modus unterscheidet sich vom Enforced-Modus dadurch, dass dieser einen Zugriff zwar überwacht und loggt, aber nicht verweigert. Auch kann SELinux so konfiguriert werden, dass der Modus erst nach einem Neustart geändert werden kann.
Konfigurationsdateien für SELinux finden sich unter /etc/selinux.
Hardening: tcpwrapper, xinetd & udev
Bei Netzwerkdiensten, die nicht über den inetd beziehungsweise xinetd gestartet werden und sich somit der einfachen Zugriffskontrolle und -überwachung dort entziehen, lässt sich die Konfiguration meist über tcpwrapper aktivieren. Ob ein Prozess diese Methoden nutzen kann, verrät ein Blick in die Manualseite; man kann aber auch nachsehen, ob die notwendige Shared-Library - sinngemäß "libwrap"-- eingebunden ist.
# ldd /usr/sbin/snmpd | grep --color libwrap
libwrap.so.0 => /lib64/libwrap.so.0
Die Zugriffssteuerung erfolgt hierbei im Wesentlichen über zwei Konfigurationsdateien im /etc-Verzeichnis:
-
/etc/hosts.allow
-
/etc/hosts.deny
Deren Aufbau ist durch folgende Zeile charakterisiert:
Prozessnamen : System [ : shell_command]
xinetd
Über die Konfigurationen des neuen Superdaemons xinetd kann der Zugriff auf einen Netzwerkservice vielfältig beschränkt und überwacht werden. Eine White- oder Blacklist (only_from, no_access) für IP-Adressen, Host-Namen und Netzwerknamen, ein Zeitfenster (access_times) oder eine Zugriffsperre bei hoher Systemlast (max_load) sind nur einige davon. Ein erfolgreicher oder misslungener Zugriff kann ebenso geloggt werden. Die Möglichkeit, den neuen Prozess mit anderer User- und Gruppen-ID auszuführen, sollte man nutzen. Mit der "bind/ interface"-Option kann der Service auf ein definiertes Netzwerk-Interface fixiert und so beispielsweise der Zugriff über telnet nur aus dem LAN heraus aktiviert werden.
Bei einem Stand-alone-Daemon, der seinen I/O-Verkehr nicht über die Dateihandle stdin und stdout abwickelt, bleibt nur die Möglichkeit, über den tcpwrapper oder letztendlich über die Firewall eine Zugriffskontrolle zu implementieren. Die Möglichkeit, über openSSH einen Tunnel aufzubauen, erschwert die Kontrolle der Zugriffe.
Wenn möglich, sollte ein Netzwerkservice immer auf den Host-Namen "localhost" beziehungsweise die IP 127.0.0.1 oder das Interface "lo" gebunden werden. Denn nur so ist ein Zugriff darauf ausschließlich vom gleichen System möglich, und es können auch Firewall-Regeln mit dem Interface "lo" definiert werden.
udev
Wurden die sogenannten Device-Knoten im Verzeichnis "/dev" vor einiger Zeit noch statisch per "mknod" angelegt, übernimmt dies heute der "udev"-Daemon. Dieser ist in der Lage, anhand von speziellen Merkmalen der Hardware, wie Hersteller, Seriennummer oder Typ, den Device-Knoten immer mit dem gleichen Namen und der gleichen Major- und Minor-Nummer anzulegen, auch wenn Letztere in neueren Kernels keine große Rolle mehr spielen.
Die Systemregeln unter /lib/udev/rules.d können durch eigene Regeln unter /etc/udev/rules.d ersetzt und somit auch deaktiviert werden.
Secure Shell (SSH)
Die Secure Shell ist mittlerweile der De-facto-Standard, wenn es um Remote-Logins , Port-Weiterleitung, secure-ftp und Tunneling geht. Von daher sollte SSH gegenüber telnet, rlogin und ftp bevorzugt werden.
Die Konfiguration erfolgt über die Dateien im Verzeichnis /etc/ssh/:
-
für den Serverprozess: /etc/ssh/sshd_config
-
für den Client: /etc/ssh/ssh_config
Die eigenen Keys finden sich dann im Home-Verzeichnis $HOME/.ssh und heißen normalerweise je nach Schlüssel-Type id_rsa* und id_dsa* für die privaten Schlüssel und tragen die Endung ".pub" für die öffentlichen Schlüssel.
Über die Konfigurationsdatei config kann jeder Benutzer noch individuelle Einstellungen vornehmen. In der Datei known_hosts merkt sich der Client-SSH die kontaktierten Systeme in Form des öffentlichen Systemschlüssels aus /etc/ssh/ssh_host*.pub. Der SSH-Client würde also bemerken, wenn sich ein Host-Schlüssel verändert hat, und somit eine Man-in-the-Middle-Attacke erkennen.
Ist der Zugriff für ein oder mehrere Systeme auf einen IP-Port über die IP-Adresse oder die Host-Namen per tcpwrapper, xinetd-Konfiguration oder die iptables-Firewall freigeschaltet, sollte man auf jeden Fall die Applikation und die Services individuell für die notwendigen Zugriffsarten konfigurieren. Für die Secure-Shell (SSH) empfiehlt es sich, nur den Zugriff über die vorher generierten Schlüssel (Public/ Private Keys) zu erlauben und die interaktive Passwort-Authentifizierung zu verbieten:
PasswordAuthentication no
UsePAM no
Die Verzeichnisrechte auf $HOME/.ssh sollten restriktiv gesetzt werden, ansonsten verweigert der SSH-Daemon schon mal den erwarteten Login über den öffentlichen Key (Option "StrictModes yes").
Capabilities
Neben einer chroot-Umgebung und dem Ausführen unter einer Benutzer- und Gruppen-ID größer null kann ein Prozess noch selektiv seine Freiheiten begrenzen. Die Capabilities, das heißt die Rechte eines privilegierten Threads, kann dieser je nach Bedarf deaktivieren und somit seine Privilegien begrenzen. Die Liste der möglichen Capabilities wächst mit der Zeit.
Als Beispiel kann ein Prozess oder auch Thread, der mit der effektiven Benutzer-ID 0 gestartet wurde und das Privileg "CAP_NET_BIND_SERVICE" deaktiviert hat, sich nicht mehr auf einen Netzwerk-Port unterhalb der Port-Nummer 1024 binden.
Fazit
Mit wenig Aufwand lässt sich in aktuellen Distributionen das Linux-System den Bedürfnissen anpassen und auch gleich besser absichern. Auch wenn der Aufwand für den Betrieb dafür größer ist, das Resultat rechtfertigt diesen in jedem Fall.
Ein System-Hardening über die integrierte Firewall, den Zugriffsschutz über tcpwrapper oder den Inet-Superdaemon sollte heute eine Selbstverständlichkeit sein. (cvi)