Webserver, Reverse Proxy und Container – wie die Schichten zusammenspielen
Webserver, Reverse Proxy und Container – wie die Schichten zusammenspielen
Dieser Artikel erklärt die grundlegenden Konzepte hinter einem modernen Web-Setup: Was macht eigentlich welche Schicht? Und warum müssen alle zusammenpassen?
Das Prinzip gilt unabhängig davon ob die Dienste in Docker-Containern laufen oder direkt auf einem Server ("bare metal") installiert sind.
Das Grundprinzip: Schichten
Ein modernes Web-Setup besteht aus mehreren unabhängigen Schichten, die jeweils eine eigene Aufgabe haben:
| Schicht | Aufgabe | Beispiel |
|---|---|---|
| Netzwerk | Wer darf mit wem reden? | Docker-Netzwerk / Firewall |
| Reverse Proxy | Wer bekommt welche Anfrage? | Caddy / nginx |
| Anwendung | Was wird verarbeitet? | Nextcloud / Collabora |
| Browser | Was wird angezeigt? | Chrome / Firefox |
Jede Schicht schützt und kontrolliert – unabhängig von den anderen. Fällt eine Schicht aus oder wird umgangen, stehen die anderen noch. Dieses Prinzip heißt Defense in Depth.
"Bare Metal" vs. Docker – derselbe Gedanke, andere Werkzeuge
Ob ein Dienst direkt auf dem Server läuft ("Bare Metal") oder in einem Container: Die Konzepte sind identisch. Nur die Werkzeuge unterscheiden sich.
| Konzept | "Bare Metal" | Docker |
|---|---|---|
| Dienst starten | systemd / init.d | docker compose up
|
| Netzwerk trennen | Firewall (iptables, ufw) | Docker-Netzwerk |
| Dienste verbinden | localhost / IP-Adresse | Container-Name |
| Reverse Proxy | nginx / Caddy direkt installiert | Caddy als Container |
| Konfiguration | Dateien in /etc/ | docker-compose.yml + Caddyfile |
Konkret: Ob Nextcloud auf Port 8080 unter localhost:8080 läuft
oder als Container namens nextcloud – Caddy spricht in beiden Fällen
dieselbe Sprache. Nur die Adresse ändert sich.
Die drei Schutzschichten vor einem Dienst
Am Beispiel von Collabora (dem Office-Server hinter Nextcloud) lässt sich gut zeigen wie die Schichten zusammenwirken.
Schicht 1: Das Netzwerk
Wo: docker-compose.yml, Abschnitt networks
networks:
tuxi_network:
external: true
Und beim Collabora-Container:
collabora: networks: [tuxi_network] # keine "ports:" → kein direkter Zugriff von außen!
Was es bewirkt: Collabora ist nur innerhalb des Docker-Netzwerks erreichbar. Von außen – also vom Internet – gibt es keinen direkten Weg zum Dienst. Nur Caddy, der ebenfalls im selben Netzwerk sitzt, kann Collabora ansprechen.
Auf bare metal entspricht das einer Firewall-Regel:
iptables -A INPUT -p tcp --dport 9980 -j DROP – Port 9980 von außen gesperrt,
nur lokale Verbindungen erlaubt.
Schicht 2: Die Pfad-Whitelist im Reverse Proxy
Wo: Caddyfile, Block collabora.tuxi.ddnss.de
@allowed path /browser/* /hosting/capabilities /hosting/discovery /cool/* /lool/*
handle @allowed {
reverse_proxy collabora:9980
}
handle {
respond 403
}
Was es bewirkt: Caddy lässt nur definierte Pfade durch. Alles andere – auch wenn jemand den Dienst erreichen könnte – bekommt sofort eine 403-Antwort, ohne dass Collabora überhaupt kontaktiert wird.
Collabora hat viele interne Pfade die nie für die Außenwelt gedacht waren. Die Whitelist sorgt dafür dass nur die fünf benötigten Pfade erreichbar sind.
Auf bare metal: dieselbe nginx-Konfiguration, dieselbe Logik – nur andere Syntax.
Schicht 3: Die Anwendungs-Allowlist (aliasgroup)
Wo: docker-compose.yml, Umgebungsvariablen des Collabora-Containers
environment: - aliasgroup1=https://cirruscloud.tuxi.ddnss.de - domain=^nextcloud$|^cirruscloud\.tuxi\.ddnss\.de(:443)?$
Was es bewirkt: Das ist eine interne Collabora-Einstellung – unabhängig von Caddy. Collabora selbst prüft bei jedem Dokument-Aufruf: Kommt dieser WOPI-Request von einer erlaubten Quelle?
Nur Nextcloud (cirruscloud.tuxi.ddnss.de) darf Dokumente öffnen.
Ein Bot der es durch Caddy schafft würde an dieser Stelle scheitern –
Collabora würde das Dokument schlicht nicht öffnen.
Auf bare metal: dieselbe Einstellung in /etc/coolwsd/coolwsd.xml
unter <storage> → <wopi> → <host>.
Das Zusammenspiel: Wer spricht mit wem?
Internet
│
▼
[Caddy] ──────────────────────── einziger Eintrittspunkt
│ prüft: Pfad erlaubt?
│ setzt: Security-Header
│
▼
[Collabora] ◄──────────────────── nur aus dem Docker-Netz erreichbar
│ prüft: kommt der Request von Nextcloud?
│
▼
[Nextcloud] ◄──────────────────── speichert / liefert Dokumente
Der Browser des Nutzers spricht nur mit Caddy. Caddy spricht mit Collabora und Nextcloud. Collabora spricht mit Nextcloud (WOPI). Nextcloud spricht mit der Datenbank.
Keiner dieser internen Dienste ist direkt vom Internet erreichbar.
Die Security-Header: Was der Browser wissen muss
Wo: Caddyfile, header-Block in jedem Domain-Abschnitt
header {
-Server
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "no-referrer"
}
Diese Header schützen nicht den Server – sie schützen den Browser des Nutzers. Sie sagen dem Browser wie er mit den Antworten umgehen soll.
Auf bare metal: exakt dieselben Header, nur in der nginx/Apache-Konfiguration gesetzt. Das Konzept ist HTTP-Standard – unabhängig von der Software.
Für eine detaillierte Erklärung der einzelnen Header: → Caddy – Konzepte und Konfiguration#header
Warum muss das alles zusammenpassen?
Compose und Caddyfile sprechen verschiedene Sprachen – aber sie müssen denselben Namen für einen Dienst verwenden:
# docker-compose.yml container_name: collabora
# Caddyfile reverse_proxy collabora:9980
Docker's internes DNS löst collabora automatisch zur richtigen
IP-Adresse auf. Das ist die unsichtbare Brücke zwischen beiden Dateien.
Auf bare metal gibt es kein Docker-DNS – dort steht dann localhost:9980
oder eine feste IP. Das Prinzip ist dasselbe, die Adresse ist eine andere.
Zusammenfassung
| Was | Wo konfiguriert | Schützt gegen |
|---|---|---|
| Kein direkter Port | docker-compose.yml (ports: weggelassen) |
Direktzugriff aus dem Internet |
| Pfad-Whitelist | Caddyfile (@allowed) |
Zugriff auf interne Dienst-Pfade |
| aliasgroup | docker-compose.yml (Umgebungsvariable) | Fremde WOPI-Clients |
| Security-Header | Caddyfile (header) |
Browser-seitige Angriffe |
| Docker-Netzwerk | docker-compose.yml (networks:) |
Kommunikation zwischen unbekannten Containern |
Jede Zeile ist unabhängig. Alle zusammen bilden die Festung. 🏰
