Root-Verzeichnis wechseln, ohne sich zu täuschen
chroot ist eines der ältesten Isolationswerkzeuge unter Unix und gleichzeitig eines der am meisten missverstandenen. Wer eine Jail-Umgebung aufbaut, um einen Prozess vom Rest des Dateisystems abzuschotten, muss genau verstehen, was chroot tatsächlich absichert und wo diese Absicherung endet, denn viele Server wurden schon über eine als sicher geglaubte chroot-Umgebung kompromittiert.
Inhaltsverzeichnis
- 1. Was Prozessisolation bedeutet und warum chroot existiert
- 2. chroot-Grundlagen: das Root-Verzeichnis ändern
- 3. Eine chroot-Jail Schritt für Schritt aufbauen
- 4. Binaries, Bibliotheken und Devices in der Jail
- 5. Die Grenzen von chroot: kein Sicherheitsfeature im eigentlichen Sinn
- 6. Praxisbeispiel: chroot für SFTP und Rescue-Systeme
- 7. Häufige Fallstricke beim Betrieb einer Jail
- 8. chroot vs. Namespaces vs. Container: die Abgrenzung
- 9. Debugging und Vergleichstabelle
- 10. Zusammenfassung
- 11. FAQ
1. Was Prozessisolation bedeutet und warum chroot existiert
Prozessisolation bezeichnet die Idee, einem laufenden Prozess absichtlich nur einen begrenzten Ausschnitt der Systemressourcen sichtbar zu machen, statt ihm den vollen Zugriff auf das gesamte System zu erlauben. chroot, kurz für change root, ist der historisch erste und einfachste Baustein dieser Idee unter Unix: Der Systemaufruf ändert für einen Prozess und alle seine Kindprozesse das Wurzelverzeichnis, sodass dieser Prozess ein bestimmtes Unterverzeichnis fortan als / wahrnimmt und alles außerhalb davon schlicht nicht mehr sieht.
Die ursprüngliche Motivation für chroot war weniger Sicherheit als Testbarkeit: Entwickler wollten in den 1970er Jahren neue Softwareversionen in einer isolierten Verzeichnisstruktur testen, ohne das produktive System zu gefährden. Erst später etablierte sich die Vorstellung, chroot ließe sich auch als Sicherheitsgrenze gegen kompromittierte Dienste einsetzen, ein Missverständnis, das bis heute zu unsicheren Konfigurationen führt. Wer eine Jail, also eine mit chroot abgeschottete Umgebung, aufsetzt, sollte deshalb zuerst verstehen, wofür das Werkzeug tatsächlich entworfen wurde.
2. chroot-Grundlagen: das Root-Verzeichnis ändern
Der Befehl chroot /pfad/zur/jail /bin/bash startet eine neue Bash-Instanz, deren Wurzelverzeichnis auf /pfad/zur/jail gesetzt ist. Innerhalb dieser Shell verweist /etc/passwd nicht mehr auf die systemweite Datei, sondern auf /pfad/zur/jail/etc/passwd, sofern diese überhaupt existiert. Genau hier liegt die erste praktische Hürde: chroot ändert nur die Sicht auf das Dateisystem, es kopiert keine Dateien und richtet keine Umgebung ein. Fehlt eine benötigte Datei innerhalb der Jail, meldet der Prozess schlicht, dass sie nicht existiert, obwohl sie außerhalb der Jail durchaus vorhanden wäre.
Wichtig ist außerdem, dass chroot ausschließlich Root-Rechte voraussetzt, denn ohne diese Einschränkung könnte jeder Benutzer beliebige Pfadwechsel vornehmen und damit die Sicherheitsannahmen des Systems unterlaufen. Der Kernel selbst erzwingt diese Beschränkung über die Capability CAP_SYS_CHROOT. Nach dem Wechsel bleibt der Prozess dauerhaft in der neuen Root-Sicht gefangen, es sei denn, er verfügt weiterhin über Root-Rechte und nutzt einen bekannten Fluchtweg, der im Abschnitt zu den Grenzen von chroot beschrieben wird.
# Create a minimal jail directory structure
mkdir -p /srv/jail/{bin,lib,lib64,usr,etc,dev,proc}
# Change root and start a shell inside — requires root privileges
sudo chroot /srv/jail /bin/bash
# Inside the chroot, / now refers to /srv/jail on the host
ls / # shows only what exists under /srv/jail
cat /etc/passwd # fails unless /srv/jail/etc/passwd was created
exit # returns to the original shell, outside the jail
3. Eine chroot-Jail Schritt für Schritt aufbauen
Der Aufbau einer funktionierenden Jail beginnt mit der Verzeichnisstruktur, die ein Programm für seinen normalen Betrieb erwartet: mindestens /bin, /lib, häufig /lib64 auf 64-Bit-Systemen, sowie /etc für Konfigurationsdateien, die das Programm tatsächlich benötigt. Anschließend kopiert man die gewünschten Binaries, etwa bash oder ls, in das entsprechende Verzeichnis der Jail. Der entscheidende Schritt danach ist häufig der am meisten übersehene: Jedes kopierte Binary bringt eigene Abhängigkeiten an dynamischen Bibliotheken mit, die ebenfalls innerhalb der Jail bereitgestellt werden müssen, sonst schlägt der Start mit einem Fehler wie "No such file or directory" fehl, obwohl die Datei sichtbar vorhanden ist.
Das Werkzeug ldd listet genau diese Abhängigkeiten auf und ist damit der zentrale Helfer beim Jail-Aufbau. Für jede Zeile aus der ldd-Ausgabe muss die entsprechende Bibliothek an derselben relativen Pfadposition innerhalb der Jail existieren, wie sie außerhalb referenziert wird. Bei komplexeren Programmen mit vielen Abhängigkeiten summiert sich dieser Aufwand schnell, weshalb produktive Jails heute meist mit fertigen Werkzeugen wie debootstrap aufgebaut werden, die automatisch ein minimales, konsistentes Basissystem in ein Zielverzeichnis installieren.
#!/usr/bin/env bash
# Populate a chroot jail with a binary and its shared library dependencies
set -euo pipefail
JAIL="/srv/jail"
BINARY="/bin/ls"
# Copy the binary itself into the jail, preserving its relative path
mkdir -p "$JAIL$(dirname "$BINARY")"
cp "$BINARY" "$JAIL$BINARY"
# Resolve dynamic library dependencies and copy each one
ldd "$BINARY" | awk '{print $3}' | grep '^/' | while read -r lib; do
mkdir -p "$JAIL$(dirname "$lib")"
cp -n "$lib" "$JAIL$lib"
done
# The dynamic linker itself must also be present
LINKER=$(ldd "$BINARY" | grep -o '/lib.*ld-linux[^ ]*')
mkdir -p "$JAIL$(dirname "$LINKER")"
cp -n "$LINKER" "$JAIL$LINKER"
4. Binaries, Bibliotheken und Devices in der Jail
Über Binaries und Bibliotheken hinaus benötigen viele Programme Zugriff auf virtuelle Dateisysteme wie /proc und Gerätedateien wie /dev/null oder /dev/urandom. Diese existieren nicht automatisch innerhalb der Jail, sondern müssen explizit über mount --bind beziehungsweise als eigenständige Gerätedateien mit mknod eingebunden werden. Fehlt etwa /dev/null, scheitern viele Standard-Tools bereits beim Versuch, Ausgaben dorthin umzuleiten, mit einer schwer nachvollziehbaren Fehlermeldung.
Der Bind-Mount ist dabei die deutlich robustere Wahl gegenüber manuell mit mknod erzeugten Gerätedateien, weil er die Berechtigungen und Eigenschaften der Originaldatei automatisch übernimmt und bei Änderungen am Host synchron bleibt. Ein häufiges Muster in produktiven Jails ist deshalb, /dev, /proc und teilweise /sys vom Host per Bind-Mount in die Jail einzuhängen, statt eigene, isolierte Kopien dieser virtuellen Dateisysteme zu pflegen.
# Bind-mount host device and proc filesystems into the jail
sudo mount --bind /dev /srv/jail/dev
sudo mount --bind /dev/pts /srv/jail/dev/pts
sudo mount -t proc proc /srv/jail/proc
# Verify from inside the jail
sudo chroot /srv/jail /bin/bash -c 'ls /dev; cat /proc/version'
# Always unmount in reverse order before deleting the jail directory
sudo umount /srv/jail/proc
sudo umount /srv/jail/dev/pts
sudo umount /srv/jail/dev
5. Die Grenzen von chroot: kein Sicherheitsfeature im eigentlichen Sinn
Der wichtigste Punkt zu chroot ist, dass es niemals als alleinige Sicherheitsgrenze gedacht war und heute in der Sicherheitsliteratur ausdrücklich nicht als solche empfohlen wird. Ein Prozess mit Root-Rechten innerhalb einer chroot-Jail kann diese unter bestimmten Bedingungen verlassen, klassischerweise über den sogenannten "chroot breakout" mittels eines zweiten chroot()-Aufrufs kombiniert mit einem offenen Dateideskriptor außerhalb der Jail, der noch aus der Zeit vor dem Wechsel stammt. Dieser Fluchtweg ist seit Jahrzehnten dokumentiert und funktioniert bis heute, wenn der Prozess innerhalb der Jail Root-Rechte besitzt.
Zusätzlich isoliert chroot weder Prozess-IDs noch Netzwerk-Sockets noch Benutzer-IDs. Ein Prozess innerhalb der Jail sieht weiterhin dieselben Prozesse über /proc, sofern dieses eingebunden ist, kann weiterhin auf offene Netzwerkports des Hosts zugreifen und teilt sich denselben Kernel vollständig mit dem restlichen System. Für echte Sicherheitsisolation braucht es zusätzlich Linux-Namespaces, cgroups und idealerweise den Verzicht auf Root-Rechte innerhalb der Jail, also genau die Bausteine, aus denen moderne Container-Laufzeiten aufgebaut sind.
6. Praxisbeispiel: chroot für SFTP und Rescue-Systeme
Ein legitimer und bis heute weit verbreiteter Einsatzzweck von chroot ist die Beschränkung von SFTP-Benutzern auf ihr eigenes Verzeichnis über ChrootDirectory in der OpenSSH-Konfiguration. Da diese Benutzer keine Root-Rechte innerhalb der Jail besitzen und OpenSSH selbst die Jail-Umgebung streng kontrolliert, entfällt hier das Problem des chroot-Breakouts weitgehend, solange keine setuid-Binaries in der Jail bereitgestellt werden. Diese Konfiguration ist deutlich robuster als ein allgemeiner chroot-Aufruf für beliebige Dienste.
Ein zweiter klassischer Anwendungsfall ist das Rescue-System eines Servers: Bootet man von einem Live-System, um ein defektes Betriebssystem zu reparieren, chrootet man in die eigentliche Systempartition, um Werkzeuge wie grub-install oder dpkg mit dem korrekten Root-Kontext auszuführen, statt sie auf das Live-System selbst anzuwenden. In diesem Szenario ist chroot kein Sicherheitswerkzeug, sondern ein reines Kontextwechsel-Werkzeug, das genau seiner ursprünglichen Bestimmung entspricht.
# /etc/ssh/sshd_config — restrict a user to their home directory via chroot
Match User sftpuser
ChrootDirectory /srv/sftp/%u
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
7. Häufige Fallstricke beim Betrieb einer Jail
Der häufigste Fehler ist, eine Jail einzurichten und dabei zu vergessen, dass Zeitzonendaten, DNS-Auflösung über /etc/resolv.conf und Locale-Dateien ebenfalls innerhalb der Jail vorhanden sein müssen, wenn der eingesperrte Prozess Netzwerkzugriffe durchführt oder lokalisierte Ausgaben erzeugt. Ohne diese Dateien scheitern DNS-Auflösungen scheinbar grundlos, obwohl das Netzwerk selbst funktioniert, weil der Prozess innerhalb der Jail schlicht keine gültige resolv.conf findet.
Ein zweiter verbreiteter Fehler ist, vergessene Bind-Mounts beim Löschen einer Jail-Verzeichnisstruktur zu übersehen. Löscht man das Jail-Verzeichnis mit rm -rf, während /dev oder /proc noch eingehängt sind, werden versehentlich Inhalte des Host-Dateisystems gelöscht, weil rm -rf transparent durch den Bind-Mount hindurch operiert. Vor jeder Löschung einer Jail müssen deshalb alle Bind-Mounts zuverlässig mit umount in umgekehrter Reihenfolge entfernt werden.
8. chroot vs. Namespaces vs. Container: die Abgrenzung
Moderne Container-Laufzeiten wie Docker nutzen chroot beziehungsweise dessen sichereren Nachfolger pivot_root lediglich als eine von mehreren Isolationsschichten. Zusätzlich kommen Linux-Namespaces zum Einsatz, die Prozess-IDs, Netzwerk-Stacks, Hostnamen und Benutzer-IDs jeweils eigenständig isolieren, sowie cgroups, die Ressourcenlimits für CPU und Speicher durchsetzen. Ein Container ist also technisch eine Kombination aus chroot-artiger Dateisystemisolation plus Namespaces plus cgroups, nicht ein Ersatz für chroot, sondern eine erhebliche Erweiterung.
Wer heute eine echte Sicherheitsgrenze benötigt, sollte deshalb direkt zu Namespaces und containerbasierten Werkzeugen greifen, statt chroot allein als vermeintliche Isolationsschicht zu betrachten. chroot bleibt dennoch relevant als leichtgewichtiges Werkzeug für Fälle, in denen keine echte Sicherheitsgrenze gebraucht wird, sondern lediglich eine kontrollierte Dateisystemsicht, etwa bei SFTP-Jails oder Build-Umgebungen für Paketierung.
9. Debugging und Vergleichstabelle
Zum Debugging einer nicht startenden Jail-Umgebung liefert strace -f chroot /srv/jail /bin/programm die genaue Stelle, an der ein fehlendes File oder eine fehlende Bibliothek den Start verhindert. ldd innerhalb der Jail selbst zeigt, ob alle Abhängigkeiten eines Binaries tatsächlich aufgelöst werden können. Für die laufende Jail hilft lsof auf dem Host, um offene Dateideskriptoren des chrooteten Prozesses zu identifizieren, die potenziell einen Fluchtweg darstellen könnten.
| Mechanismus | Isoliert Dateisystem | Isoliert Prozesse/Netzwerk | Sicherheitsgrenze |
|---|---|---|---|
| chroot | Ja | Nein | Schwach, umgehbar mit Root |
| chroot + Non-Root | Ja | Nein | Für einfache Fälle ausreichend (SFTP) |
| Linux Namespaces | Ja (mit pivot_root) | Ja | Deutlich stärker |
| Docker-Container | Ja | Ja | Namespaces + cgroups + Seccomp |
Diese Tabelle zeigt deutlich, dass chroot allein am unteren Ende der Isolationsskala steht. Für produktive Sicherheitsanforderungen ist immer mindestens die Kombination mit Namespaces und dem Verzicht auf Root-Rechte innerhalb der Jail notwendig.
Mironsoft
Linux-Serverhärtung und Prozessisolation für PHP-Hosting
Isolation, die tatsächlich hält, was sie verspricht?
Wir bewerten bestehende chroot-Jails auf ihre tatsächliche Sicherheitswirkung, migrieren kritische Dienste auf Namespaces und Container, und richten SFTP- oder Build-Jails sauber und wartbar ein.
Isolations-Audit
Prüfung bestehender Jails auf Breakout-Risiken und fehlende Non-Root-Konfiguration
SFTP-Jails
Saubere ChrootDirectory-Konfiguration für Kunden- und Deployment-Zugänge
Container-Migration
Umzug kritischer Dienste von chroot auf Namespaces und cgroups
10. Zusammenfassung
chroot ändert die Sicht eines Prozesses auf das Wurzelverzeichnis und war ursprünglich für Testumgebungen gedacht, nicht für Sicherheitsisolation. Eine funktionierende Jail braucht eigene Kopien aller benötigten Binaries, deren Bibliotheksabhängigkeiten sowie Bind-Mounts für /dev und /proc. Ein Prozess mit Root-Rechten kann eine chroot-Jail über bekannte Techniken verlassen, weshalb chroot niemals als alleinige Sicherheitsgrenze dienen sollte.
Sinnvolle Einsatzzwecke bleiben SFTP-Beschränkungen ohne Root-Rechte, Rescue-Systeme und Build-Umgebungen. Für echte Sicherheitsisolation braucht es zusätzlich Linux-Namespaces und cgroups, also genau die Bausteine, aus denen moderne Container bestehen. Wer diese Abgrenzung kennt, nutzt chroot dort, wo es sinnvoll ist, und verlässt sich für kritische Isolationsanforderungen auf die stärkeren, moderneren Werkzeuge.
chroot und Jails: Das Wichtigste auf einen Blick
Funktionsweise
Ändert nur das Wurzelverzeichnis eines Prozesses, kopiert oder installiert keine Dateien automatisch.
Jail-Aufbau
Binaries, ldd-Abhängigkeiten und Bind-Mounts für /dev und /proc sind notwendig, sonst schlägt der Start fehl.
Grenzen
Kein Schutz gegen Root-Prozesse, keine Isolation von Prozess-IDs, Netzwerk oder Benutzer-IDs.
Empfehlung
Für echte Sicherheit zusätzlich Namespaces und cgroups einsetzen, chroot nur für Non-Root-Fälle wie SFTP.