"Container sind isolierte Umgebungen, sollte es da nicht in Ordnung sein, innerhalb davon root zu benutzen?"

"Warum sollte ich einen USER erstellen? Das erhöht nur die Schichten und ist umständlich..."

"Es ist mühsam, Berechtigungen einzurichten, damit der Container USER auf vom Host gebundene Verzeichnisse zugreifen kann..."

Viele Entwickler denken so und verwenden weiterhin den Standardbenutzer root in ihren Dockerfile.

Doch das ist eine sehr gefährliche Praxis aus Sicherheitsgründen. Die 'Isolation' des Containers ist keine perfekte Firewall, und das Ausführen eines Containers mit root-Rechten kann eine Abkürzung sein, die den gesamten Server gefährdet.

In diesem Artikel erkläre ich klar, warum man Container als root ausführen sollte und wie man dies verhindern kann.


1. Schlimmstes Szenario: "Container Escape"



Ein entscheidender Grund, warum man auf die Verwendung von root verzichten sollte.

  • Standardabbildung: Der root-Benutzer innerhalb eines Docker-Containers (UID 0) ist standardmäßig der gleiche Benutzer wie der Host-Server’s root Benutzer (UID 0).

  • Wenn die Isolation durchbrochen wird: Nehmen wir an, ein Angreifer nutzt eine Schwachstelle in der Anwendung oder im Docker oder im Linux-Kernel selbst, um erfolgreich aus der isolierten Container-Umgebung zu 'entkommen'.

  • Folge: Wenn der Container mit root-Rechten läuft, erhält der Angreifer sofort root-Rechte auf dem Host-Server. Der gesamte Server wird vollständig übernommen.

Analogie: Einen Container als root auszuführen, ist wie einen „Hotelgast mit einem Master-Key“ in einem Zimmer unterzubringen. Sobald dieser Gast die Tür seines Zimmers (Containers) öffnet, kann er sich frei im gesamten Hotel (Host) bewegen.

Wenn hingegen der Container als appuser (UID 1001), einem nicht privilegierten Benutzer, ausgeführt würde? Selbst wenn ein Angreifer entkommt, hätte er nur die Berechtigungen des appuser, sodass der Schaden minimiert werden kann.


2. Prinzip der geringsten Privilegien (Principle of Least Privilege)

Das grundlegendste Prinzip der Sicherheit ist, dass "alle Programme und Benutzer nur die minimalen Berechtigungen haben sollten, die erforderlich sind, um ihre Aufgaben auszuführen".

Für den Betrieb einer Webanwendung sind root-Rechte überhaupt nicht erforderlich.

  • Bei root: Wenn ein Angreifer in den Container eindringt (auch wenn er nicht entkommt), ist er innerhalb des Containers root.

    • Er kann beliebig schädliche Scanner oder Krypto-Miner mit Befehlen wie apt-get install installieren.

    • Er kann alle Dateien, einschließlich von Konfigurationsdateien mit Datenbankverbindungsinformationen (settings.py usw.), ändern oder löschen.

  • Bei appuser: Selbst wenn ein Angreifer eindringt, bleibt er lediglich appuser.

    • apt-get install? Zugriff verweigert.

    • Änderungen an den Systemkonfigurationsdateien? Zugriff verweigert.

    • Der Schaden ist auf das Anwendungsverzeichnis des appuser beschränkt.


3. Missverständnisse klären



🤔 „Das USER-Kommando vergrößert nur die Bildschichten, oder?“

Nein. Das ist das größte Missverständnis.

Das USER-Kommando im Dockerfile erzeugt keine Dateisystemschichten. Stattdessen fügt es Metadaten (Metadata) zur Bilddatei hinzu, die angeben, dass der Standardbenutzer, wenn dieser Container gestartet wird, 'appuser' sein soll.

Die Bildgröße erhöht sich um keinen einzigen Byte und hat keinen Einfluss auf die Buildgeschwindigkeit.

Natürlich erzeugt der Befehl RUN useradd... einen neuen Benutzer und fügt eine sehr kleine Schicht hinzu, aber das ist ein Preis, den man für die Sicherheit zahlen muss.

🤔 „Um Port 80 zu öffnen, braucht man sowieso root-Rechte?"

Das ist richtig. In Linux können nur root-Benutzer Ports unter 1024 (z.B. 80, 443) öffnen. Das bedeutet jedoch nicht, dass man Container weiterhin als root ausführen sollte.

Der moderne Ansatz funktioniert folgendermaßen:

  1. Die Anwendung wird im Container mit appuser-Berechtigungen auf einem hohen Port wie 8000 ausgeführt.

  2. Von außen mappt Docker die Ports (docker run -p 80:8000) oder ein Reverse Proxy wie Nginx leitet externe Anfragen an Port 80 an den internen Port 8000 weiter.

Im Container sind root-Rechte überhaupt nicht erforderlich.


4. WIE: Best Practices für Dockerfile

Wie kann man das also umsetzen? Das Hinzufügen der folgenden Zeilen am Ende des Dockerfile ist keine Verschwendung, sondern eine grundlegende und wichtige Sicherheitsrichtlinie.

Dockerfile

FROM python:3.12-slim

WORKDIR /app

# ... (notwendige Arbeiten wie apt-get install, pip install usw.)

# 1. Erstellen einer nicht-privilegierten Gruppe und eines Benutzers
# -r: als Systembenutzer/-gruppe erstellen, --no-create-home: kein Heimatverzeichnis erstellen
RUN groupadd -r appgroup && useradd -r -g appgroup --no-create-home appuser

# 2. Besitzer der Anwendungsdateien auf appuser setzen
# (auch die Dateien im WORKDIR folgen diesem Besitzrecht)
COPY --chown=appuser:appgroup . .

# 3. Den Benutzer für die nachfolgenden Befehle auf appuser umschalten
USER appuser

# 4. Anwendung auf einem hohen Port wie 8000 ausführen
EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]

Zusammenfassung

Container als root auszuführen, bedeutet, das Konzept der "Vertieften Verteidigung" (Defense in Depth) aufzugeben. Es ist wie wenn man die zweite Verteidigungslinie (USER) selbst abreißt, wenn die erste Verteidigungslinie durchbrochen wird.

Überprüfen Sie sofort Ihr Dockerfile.