Traefik absichern: WAF, Rate-Limiting und TLS
Traefik kann mehr als nur Routing. In diesem Artikel zeige ich dir, wie du deinen Reverse Proxy als Sicherheitsschicht konfigurierst — mit TLS, Rate-Limiting, IP-Filterung, Security Headers und einer Web Application Firewall. Alles mit Docker Compose und konkreten Konfigurationsbeispielen.
DarkWolfCave.de
ℹ️ Hinweis: Dieser Artikel setzt voraus, dass du Traefik bereits grundlegend eingerichtet hast. Falls nicht, findest du die Basis-Installation in meinem Traefik-Einrichtungsartikel. Die hier gezeigten Konfigurationen sind für Traefik v3 — bei v2 weichen einzelne Middleware-Namen ab (z.B.
ipWhiteListstattipAllowList).
Warum dein Reverse Proxy mehr als nur Routing kann
Du hast Traefik als Reverse Proxy eingerichtet und leitest Anfragen an deine Docker-Container weiter. Grafana, Portainer, Home Assistant — alles läuft hinter Traefik. Aber nutzt du Traefik auch als Sicherheitsschicht?
Ein Reverse Proxy sitzt an einer strategisch wichtigen Stelle: zwischen dem Internet und deinen Diensten. Jede Anfrage muss durch ihn hindurch. Das macht ihn zum idealen Punkt für Sicherheitsmaßnahmen — TLS-Terminierung, Rate-Limiting, IP-Filterung und sogar eine Web Application Firewall.
Die Maßnahmen in diesem Artikel setzen genau hier an. Sie ergänzen die Netzwerk-Sicherheit und Firewall auf Applikationsebene und arbeiten zusammen mit der Server-Härtung auf Betriebssystemebene.
Du wirst hier einen groben Überblick finden.
Allerdings biete ich dir auch noch etwas mehr Support an:
- Du benötigst persönlichen Support
- Du möchtest von Beginn an Unterstützung bei deinem Projekt
- Du möchtest ein hier vorgestelltes Plugin durch mich installieren und einrichten lassen
- Du würdest gerne ein von mir erstelltes Script etwas mehr an deine Bedürfnisse anpassen
Für diese Punkte und noch einiges mehr habe ich einen limitierten VIP-Tarif eingerichtet.
Falls der Tarif gerade nicht verfügbar ist, kontaktiere mich auf Discord!
TLS-Terminierung mit Let’s Encrypt
Unverschlüsselter HTTP-Traffic ist ein offenes Buch. Jeder im Netzwerkpfad — vom lokalen Netzwerk bis zum ISP — kann mitlesen. TLS löst das Problem, und Traefik macht die Einrichtung mit Let’s Encrypt automatisch.
Certificate Resolver konfigurieren
In deiner traefik.yml (statische Konfiguration) definierst du einen Certificate Resolver:
# traefik.yml
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: admin@deine-domain.de
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
Die redirections-Konfiguration leitet alle HTTP-Anfragen automatisch auf HTTPS um. Kein Client erreicht deine Dienste unverschlüsselt.
Docker Compose für TLS
# docker-compose.yml
services:
traefik:
image: traefik:v3.3
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./letsencrypt:/letsencrypt
networks:
- proxy
mein-dienst:
image: mein-dienst:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.mein-dienst.rule=Host(`dienst.deine-domain.de`)"
- "traefik.http.routers.mein-dienst.entrypoints=websecure"
- "traefik.http.routers.mein-dienst.tls.certresolver=letsencrypt"
networks:
- proxy
networks:
proxy:
name: proxy
💡 Tipp: Setze
tls.certresolver=letsencryptauf jeden Router. Traefik holt und erneuert die Zertifikate vollautomatisch — du musst dich nie wieder um Zertifikate kümmern.
TLS-Optionen verschärfen
Für zusätzliche Sicherheit kannst du die TLS-Version und Cipher-Suites einschränken:
# traefik.yml — TLS-Optionen
tls:
options:
default:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256
| Einstellung | Empfehlung | Begründung |
|---|---|---|
| Min. TLS-Version | 1.2 | TLS 1.0/1.1 gelten als unsicher |
| Cipher-Suites | Nur AEAD | GCM und ChaCha20 sind aktueller Standard |
| HTTP → HTTPS Redirect | Aktivieren | Kein unverschlüsselter Traffic |
Rate-Limiting gegen Brute-Force
Rate-Limiting begrenzt die Anzahl der Anfragen pro Zeitraum. Das bremst Brute-Force-Angriffe auf Login-Seiten, API-Missbrauch und einfache DDoS-Versuche.
Middleware definieren
In Traefik definierst du Rate-Limiting als Middleware — per Docker-Label oder in einer dynamischen Konfigurationsdatei:
# Per Docker-Label
labels:
- "traefik.http.middlewares.rate-limit.ratelimit.average=20"
- "traefik.http.middlewares.rate-limit.ratelimit.burst=50"
- "traefik.http.middlewares.rate-limit.ratelimit.period=1s"
Das bedeutet: Maximal 20 Anfragen pro Sekunde im Durchschnitt, mit einem Burst von bis zu 50 Anfragen. Anfragen darüber hinaus erhalten HTTP 429 (Too Many Requests).
Unterschiedliche Limits pro Dienst
Nicht jeder Dienst braucht die gleichen Limits. Ein Login-Formular sollte strenger limitiert sein als eine öffentliche Webseite:
# Strenges Limit für Login-Seiten
labels:
- "traefik.http.middlewares.auth-limit.ratelimit.average=5"
- "traefik.http.middlewares.auth-limit.ratelimit.burst=10"
- "traefik.http.middlewares.auth-limit.ratelimit.period=1s"
# Moderates Limit für APIs
labels:
- "traefik.http.middlewares.api-limit.ratelimit.average=50"
- "traefik.http.middlewares.api-limit.ratelimit.burst=100"
- "traefik.http.middlewares.api-limit.ratelimit.period=1s"
Middleware auf Router anwenden
labels:
- "traefik.http.routers.mein-dienst.middlewares=rate-limit@docker"
Mehrere Middlewares kombinierst du kommagetrennt:
labels:
- "traefik.http.routers.mein-dienst.middlewares=rate-limit@docker,security-headers@docker"
Funktionstest
# 100 Anfragen schnell hintereinander senden
for i in $(seq 1 100); do
curl -s -o /dev/null -w "%{http_code}\n" https://dienst.deine-domain.de/
done
Ab einer bestimmten Anzahl sollten 429-Codes auftauchen — dann greift dein Rate-Limit.
IP-Whitelisting für sensible Dienste
Manche Dienste sollten nur von bestimmten IP-Adressen erreichbar sein — Admin-Panels, Datenbank-Frontends oder Monitoring-Dashboards. Traefik bietet dafür die ipAllowList-Middleware.
Konfiguration per Docker-Label
# Nur lokales Netzwerk und VPN erlauben
labels:
- "traefik.http.middlewares.local-only.ipallowlist.sourcerange=192.168.1.0/24,10.0.0.0/8"
Alle Anfragen von anderen IPs erhalten HTTP 403 (Forbidden).
Beispiel: Admin-Panel absichern
services:
portainer:
image: portainer/portainer-ce
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`portainer.deine-domain.de`)"
- "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
- "traefik.http.routers.portainer.middlewares=local-only@docker,rate-limit@docker"
- "traefik.http.middlewares.local-only.ipallowlist.sourcerange=192.168.1.0/24"
networks:
- proxy
⚠️ Wichtig: Wenn Traefik hinter einem Load Balancer oder Cloudflare steht, siehst du nicht die echte Client-IP, sondern die des Proxys. Konfiguriere in dem Fall
forwardedHeadersoderproxyProtocolin deinen Entrypoints, damit Traefik die echte IP aus demX-Forwarded-For-Header liest.
| Einsatz | IP-Range | Beschreibung |
|---|---|---|
| Lokales Netzwerk | 192.168.1.0/24 | Nur LAN-Zugriff |
| VPN-Clients | 10.8.0.0/24 | Nur über VPN |
| Einzelne IP | 203.0.113.5/32 | Eine spezifische IP |
| Kombiniert | 192.168.1.0/24,10.8.0.0/24 | LAN + VPN |
Security Headers setzen
HTTP Security Headers weisen den Browser an, bestimmte Sicherheitsrichtlinien durchzusetzen. Traefik kann diese Headers automatisch zu jeder Antwort hinzufügen.
Headers-Middleware konfigurieren
Per Docker-Labels:
labels:
- "traefik.http.middlewares.sec-headers.headers.browserxssfilter=true"
- "traefik.http.middlewares.sec-headers.headers.contenttypenosniff=true"
- "traefik.http.middlewares.sec-headers.headers.framedeny=true"
- "traefik.http.middlewares.sec-headers.headers.stsincludesubdomains=true"
- "traefik.http.middlewares.sec-headers.headers.stspreload=true"
- "traefik.http.middlewares.sec-headers.headers.stsseconds=31536000"
- "traefik.http.middlewares.sec-headers.headers.referrerpolicy=strict-origin-when-cross-origin"
- "traefik.http.middlewares.sec-headers.headers.permissionspolicy=camera=(), microphone=(), geolocation=()"
Oder als dynamische Konfigurationsdatei:
# dynamic/security-headers.yml
http:
middlewares:
sec-headers:
headers:
browserXssFilter: true
contentTypeNosniff: true
frameDeny: true
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
customFrameOptionsValue: "SAMEORIGIN"
referrerPolicy: "strict-origin-when-cross-origin"
permissionsPolicy: "camera=(), microphone=(), geolocation=()"
Was bewirken die einzelnen Headers?
| Header | Einstellung | Wirkung |
|---|---|---|
X-XSS-Protection | browserXssFilter: true | Browser blockiert erkannte XSS-Angriffe |
X-Content-Type-Options | contentTypeNosniff: true | Verhindert MIME-Type-Sniffing |
X-Frame-Options | frameDeny: true | Verhindert Einbettung in iframes (Clickjacking) |
Strict-Transport-Security | stsSeconds: 31536000 | Browser erzwingt HTTPS für 1 Jahr |
Referrer-Policy | strict-origin-when-cross-origin | Begrenzt Referrer-Informationen |
Permissions-Policy | camera=(), microphone=() | Deaktiviert nicht benötigte Browser-APIs |
Headers prüfen
# Response-Headers anzeigen
curl -I https://dienst.deine-domain.de/
In der Ausgabe sollten alle konfigurierten Security Headers auftauchen. Tools wie securityheaders.com bewerten deine Konfiguration von A+ bis F.
Coraza WAF — Web Application Firewall
Eine Web Application Firewall analysiert HTTP-Anfragen auf bekannte Angriffsmuster — SQL-Injection, Cross-Site-Scripting, Path Traversal und mehr. Coraza ist eine moderne WAF-Engine in Go und der Nachfolger von ModSecurity.
Coraza als Traefik-Plugin
Traefik unterstützt Coraza als WASM-Plugin. Das Plugin prüft jede eingehende Anfrage gegen konfigurierbare Regeln — inklusive dem OWASP Core Rule Set (CRS), einer Sammlung von über 200 Regeln gegen die häufigsten Webangriffe.
Zuerst registrierst du das Plugin in der statischen Konfiguration:
# traefik.yml
experimental:
plugins:
coraza:
moduleName: github.com/jcchavezs/coraza-http-wasm-traefik
version: v0.3.0
Middleware konfigurieren
# dynamic/waf.yml
http:
middlewares:
waf:
plugin:
coraza:
directives:
- "SecRuleEngine On"
- "SecRequestBodyAccess On"
- "SecResponseBodyAccess Off"
- "SecRule REQUEST_URI \"@streq /admin\" \"id:101,phase:1,t:lowercase,log,deny,status:403\""
- "SecRule ARGS \"@detectSQLi\" \"id:102,phase:2,deny,status:403,msg:'SQL Injection blocked'\""
- "SecRule ARGS \"@detectXSS\" \"id:103,phase:2,deny,status:403,msg:'XSS blocked'\""
Per Docker-Label:
labels:
- "traefik.http.middlewares.waf.plugin.coraza.directives[0]=SecRuleEngine On"
- "traefik.http.middlewares.waf.plugin.coraza.directives[1]=SecRequestBodyAccess On"
- "traefik.http.middlewares.waf.plugin.coraza.directives[2]=SecRule ARGS \"@detectSQLi\" \"id:102,phase:2,deny,status:403\""
- "traefik.http.routers.mein-dienst.middlewares=waf@docker,sec-headers@docker,rate-limit@docker"
OWASP Core Rule Set — Was wird geprüft?
Das Core Rule Set deckt die gängigsten Angriffsvektoren ab:
| Kategorie | Beispiel | CRS-Regeln |
|---|---|---|
| SQL-Injection | ' OR 1=1 -- | 942xxx |
| Cross-Site-Scripting (XSS) | <script>alert(1)</script> | 941xxx |
| Path Traversal | ../../etc/passwd | 930xxx |
| Remote Code Execution | ; cat /etc/passwd | 932xxx |
| Scanner-Erkennung | Nikto, SQLMap User-Agents | 913xxx |
Funktionstest
# Normaler Request — sollte 200 zurückgeben
curl -I https://dienst.deine-domain.de/
# SQL-Injection-Versuch — sollte 403 zurückgeben
curl -I "https://dienst.deine-domain.de/?id=1' OR 1=1 --"
# XSS-Versuch — sollte 403 zurückgeben
curl -I "https://dienst.deine-domain.de/?q=<script>alert(1)</script>"
⚠️ Wichtig: Teste neue WAF-Regeln zuerst im Detection-Only-Modus (
SecRuleEngine DetectionOnly). Im Normalbetrieb kann das CRS False Positives verursachen — besonders bei Anwendungen die HTML in Formularen verarbeiten. Beobachte die Logs und passe die Regeln an, bevor du aufOnumstellst.
Alternative: ModSecurity als Sidecar
Wenn du ModSecurity statt Coraza bevorzugst, kannst du es als separaten Container betreiben:
services:
modsecurity:
image: owasp/modsecurity-crs:nginx
environment:
- MODSEC_RULE_ENGINE=On
- PARANOIA=1
networks:
- internal
Traefik leitet an den ModSecurity-Container weiter, der die Anfragen filtert und an den eigentlichen Dienst weitergibt. Der Nachteil: eine zusätzliche Netzwerk-Hop pro Anfrage und mehr Konfigurationsaufwand.
Docker-Netzwerk-Isolation
Die beste Middleware hilft nichts, wenn deine Dienste direkt aus dem Internet erreichbar sind — am Proxy vorbei. Docker-Netzwerk-Isolation stellt sicher, dass alle Anfragen durch Traefik laufen müssen.
Interne Netzwerke verwenden
services:
traefik:
image: traefik:v3.3
ports:
- "80:80"
- "443:443"
networks:
- proxy
mein-dienst:
# KEINE ports: Sektion!
networks:
- proxy
- internal
datenbank:
# KEINE ports: Sektion!
networks:
- internal
networks:
proxy:
name: proxy
internal:
name: internal
internal: true # Kein Internetzugang
Der Punkt: Nur Traefik hat ports konfiguriert. Deine Dienste haben keine eigenen Port-Mappings und sind nur über das Docker-Netzwerk erreichbar. Die Datenbank sitzt in einem separaten internal-Netzwerk ohne Internetzugang.
| Netzwerk | Internetzugang | Container |
|---|---|---|
proxy | Ja (über Traefik) | Traefik + alle Web-Dienste |
internal | Nein | Datenbanken, Redis, interne Services |
Docker-Socket absichern
Traefik braucht Zugriff auf den Docker-Socket um Container zu erkennen. Das ist ein Sicherheitsrisiko — wer den Socket kontrolliert, kontrolliert den Host. Zwei Maßnahmen helfen:
1. Read-Only-Zugriff:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
2. Docker Socket Proxy dazwischenschalten:
services:
docker-socket-proxy:
image: tecnativa/docker-socket-proxy
environment:
- CONTAINERS=1
- NETWORKS=1
- SERVICES=1
- TASKS=0
- POST=0
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxy
traefik:
environment:
- DOCKER_HOST=tcp://docker-socket-proxy:2375
# Kein docker.sock Volume mehr nötig
networks:
- proxy
Der Socket Proxy gibt Traefik nur Lesezugriff auf die Container-API — ohne die Möglichkeit Container zu starten, stoppen oder zu löschen.
Access-Logs und Monitoring
Sicherheitsmaßnahmen sind nur so gut wie deine Fähigkeit, Angriffe zu erkennen. Traefik-Access-Logs liefern die Daten dafür.
Access-Logs aktivieren
# traefik.yml
accessLog:
filePath: /var/log/traefik/access.log
format: json
filters:
statusCodes:
- "400-499"
- "500-599"
retryAttempts: true
Das JSON-Format ist einfacher zu parsen als das Common Log Format und enthält mehr Informationen — Client-IP, Anfrage-Dauer, Middleware-Entscheidungen und Status-Codes.
Integration mit Fail2Ban
Fail2Ban kann die Traefik-Logs überwachen und IPs nach wiederholten Fehlversuchen sperren:
# /etc/fail2ban/filter.d/traefik-auth.conf
[Definition]
failregex = ^.*"ClientHost":"<HOST>".*"OriginStatus":40[13].*$
# /etc/fail2ban/jail.d/traefik.conf
[traefik-auth]
enabled = true
filter = traefik-auth
logpath = /var/log/traefik/access.log
maxretry = 5
bantime = 3600
findtime = 300
Das sperrt IPs für eine Stunde nach 5 fehlgeschlagenen Authentifizierungsversuchen innerhalb von 5 Minuten.
💡 Tipp: Wenn du bereits Monitoring mit auditd und AIDE betreibst, füge die Traefik-Logs zu deiner Überwachung hinzu. Ungewöhnliche Muster — z.B. massenhafte 403er von einer IP — sind oft der erste Hinweis auf einen Angriff.
Alle Maßnahmen zusammen
Hier die acht Maßnahmen im Überblick — von einfach bis fortgeschritten:
| Maßnahme | Aufwand | Wirkung | Priorität |
|---|---|---|---|
| HTTP → HTTPS Redirect | 2 Minuten | Kein unverschlüsselter Traffic | Kritisch |
| TLS mit Let’s Encrypt | 10 Minuten | Automatische Zertifikatsverwaltung | Kritisch |
| Rate-Limiting | 5 Minuten | Bremst Brute-Force und DDoS | Hoch |
| Security Headers | 5 Minuten | Browser-seitige Schutzmaßnahmen | Hoch |
| Docker-Netzwerk-Isolation | 10 Minuten | Kein Bypass am Proxy vorbei | Hoch |
| IP-Whitelisting | 5 Minuten | Zugriffskontrolle für sensible Dienste | Mittel |
| Access-Logs + Fail2Ban | 15 Minuten | Angriffserkennung und automatische Sperrung | Mittel |
| WAF (Coraza) | 30 Minuten | Schutz vor OWASP Top 10 | Fortgeschritten |
Traefik ist mehr als ein Reverse Proxy — mit den richtigen Middlewares wird er zur Sicherheitsschicht vor deinen Diensten. Fang mit TLS und Rate-Limiting an, füge Security Headers hinzu und arbeite dich dann zu Docker-Netzwerk-Isolation und WAF vor.
Und wenn du die Grundlagen noch brauchst: Die Traefik-Einrichtung erklärt die Basis-Installation, in meiner SSH-Komplettanleitung geht es um die Absicherung des Serverzugangs, und mit Netzwerk-Sicherheit und Firewall schließt du die Lücken auf Netzwerkebene. Zusammen bilden diese Artikel eine Security-Strategie für jeden Linux-Server.
Hardware-Keys für 2FA und deutschsprachige Security-Bücher
Security Tools & Literatur
| Bild | Produkt | Preis | |
|---|---|---|---|
| Produktdaten werden geladen... | |||
Die komplette Artikelserie
Diese Artikel bilden zusammen einen Leitfaden für Linux-Server-Sicherheit:
- SSH-Server absichern — Die Eingangstür härten
- Netzwerk-Sicherheit & Firewall — Den Perimeter schützen
- Linux-Server härten — Die Angriffsfläche minimieren
- Monitoring & Intrusion Detection — Angriffe erkennen
- Endlessh SSH Tarpit — Bots aufhalten und beobachten
- Backup & Defense-in-Depth — Das Sicherheitsnetz und die Gesamtstrategie
- Traefik als Security-Layer (dieser Artikel) — WAF, Rate-Limiting und TLS
Kommentare