Einleitung
In diesem Artikel zeige ich, wie ich meinen Pangolin Reverse Proxy mit Mutual TLS (mTLS) abgesichert habe. Dadurch können nur Geräte mit einem gültigen Client-Zertifikat auf das Dashboard und die dahinter liegenden Dienste zugreifen.
Was ist Pangolin?
Pangolin ist ein selbst gehosteter Reverse Proxy, der auf Traefik basiert. Er ermöglicht es, interne Dienste sicher über das Internet erreichbar zu machen und bietet dabei eine benutzerfreundliche Oberfläche zur Verwaltung.
Was ist mTLS?
Bei einer normalen HTTPS-Verbindung authentifiziert sich nur der Server gegenüber dem Client (einseitiges TLS). Bei Mutual TLS (mTLS) müssen sich beide Seiten authentifizieren:
-
Der Server weist sich mit seinem Zertifikat aus (wie bei normalem HTTPS)
-
Der Client muss ebenfalls ein gültiges Zertifikat vorlegen
Das bedeutet: Selbst wenn jemand die URL kennt, kann er ohne das passende Client-Zertifikat nicht auf den Dienst zugreifen. Das ist eine zusätzliche Sicherheitsebene, die über Passwörter hinausgeht.
Vorteile von mTLS
-
Starke Authentifizierung: Zertifikate sind schwerer zu stehlen als Passwörter
-
Keine Passwort-Müdigkeit: Keine komplexen Passwörter merken
-
Geräte-Bindung: Das Zertifikat ist an ein spezifisches Gerät gebunden
-
Schutz vor Phishing: Ohne Zertifikat kein Zugriff, selbst bei geleakten Credentials
Schritt 1: Ordnerstruktur anlegen
Zuerst habe ich die notwendige Ordnerstruktur für die Zertifikate erstellt:
mkdir -p config/traefik/certs/ca
mkdir -p config/traefik/certs/clients
Die Struktur sieht dann so aus:
config/traefik/certs/
├── ca/ # Certificate Authority (CA) Zertifikate
│ ├── ca.crt # CA Zertifikat (öffentlich)
│ └── ca.key # CA Private Key (geheim!)
├── clients/ # Client-Zertifikate
│ ├── benutzer1.crt
│ ├── benutzer1.key
│ ├── benutzer1.p12 # Für Browser-Import
│ └── ...
└── create-client.sh # Skript zur Zertifikatserstellung
Schritt 2: Certificate Authority (CA) erstellen
Bevor ich Client-Zertifikate ausstellen kann, benötige ich eine eigene Certificate Authority (CA). Diese CA signiert später alle Client-Zertifikate.
cd config/traefik/certs/ca
# CA Private Key erstellen (4096 Bit für hohe Sicherheit)
openssl genrsa -out ca.key 4096
# CA Zertifikat erstellen (10 Jahre gültig)
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt \
-subj "/C=DE/ST=Berlin/L=Berlin/O=Meine Organisation/OU=IT/CN=Meine mTLS CA"
Wichtig: Den
ca.keyunbedingt sicher aufbewahren! Wer diesen Key besitzt, kann gültige Client-Zertifikate ausstellen.
Schritt 3: Traefik Dynamic Configuration anpassen
Der Kern der mTLS-Konfiguration liegt in der dynamischen Traefik-Konfiguration. Wichtig ist, dass man die drei Routen next-router, api-router und ws-router mit mTLS ausstattet, da Traefik sonst Fehlermeldungen wirft, dass die Routen unterschiedliche TLS-Optionen nutzen. Zudem werden dadurch alle Domains, die man im Pangolin Dashboard sichert, zusätzlich mit mTLS geschützt, da man sich ja erst beim Pangolin Dashboard authentifizieren muss.
Ich habe folgende Datei erstellt bzw. angepasst:
# config/traefik/dynamic_config.yml
tls:
options:
# mTLS erforderlich - Client MUSS gültiges Zertifikat haben
mtls-strict:
clientAuth:
caFiles:
- /etc/traefik/certs/ca/ca.crt
clientAuthType: RequireAndVerifyClientCert
minVersion: VersionTLS12
sniStrict: true
http:
middlewares:
redirect-to-https:
redirectScheme:
scheme: https
routers:
main-app-router-redirect:
rule: "Host(`pangolin.domain.de`)"
service: next-service
entryPoints:
- web
middlewares:
- redirect-to-https
next-router:
rule: "Host(`pangolin.domain.de`) && !PathPrefix(`/api/v1`)"
service: next-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
options: mtls-strict
api-router:
rule: "Host(`pangolin.domain.de`) && PathPrefix(`/api/v1`)"
service: api-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
options: mtls-strict
ws-router:
rule: "Host(`pangolin.domain.de`)"
service: api-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
options: mtls-strict
services:
next-service:
loadBalancer:
servers:
- url: "http://pangolin:3002"
api-service:
loadBalancer:
servers:
- url: "http://pangolin:3000"
Erklärung der wichtigsten Einstellungen
| Einstellung | Beschreibung |
|————-|————–|
| clientAuthType: RequireAndVerifyClientCert | Client-Zertifikat ist zwingend erforderlich |
| caFiles | Pfad zur CA, die Client-Zertifikate signiert hat |
| minVersion: VersionTLS12 | Mindestens TLS 1.2 (für Sicherheit) |
| sniStrict: true | Strikte SNI-Prüfung aktiviert |
| options: mtls-strict | Wendet die mTLS-Option auf den Router an |
Schritt 4: Docker Compose anpassen
In meiner docker-compose.yml habe ich das Zertifikats-Volume für Traefik eingebunden:
traefik:
image: traefik:v3.3.3
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil # Ports appear on the gerbil service
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
volumes:
- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
- ./config/traefik/logs:/var/log/traefik:rw
- ./config/traefik/certs:/etc/traefik/certs:ro #Volume to store mtls certs
Schritt 5: Client-Zertifikate erstellen
Um die Erstellung von Client-Zertifikaten zu vereinfachen, habe ich folgendes Skript erstellt:
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "Usage: $0 <client-name> [email]"
exit 1
fi
CLIENT_NAME="$1"
EMAIL="${2:-$CLIENT_NAME@email.com}"
DAYS="${3:-730}"
cd "$(dirname "$0")"
echo "=== Erstelle Client-Zertifikat für: $CLIENT_NAME ==="
# Config erstellen
cat > clients/${CLIENT_NAME}.cnf << CNFEOF
[req]
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
C = DE
ST = City
L = State
O = User
OU = Benutzer
CN = ${CLIENT_NAME}
emailAddress = ${EMAIL}
CNFEOF
# Key erstellen
openssl genrsa -out clients/${CLIENT_NAME}.key 4096
# CSR erstellen
openssl req -new -key clients/${CLIENT_NAME}.key \
-out clients/${CLIENT_NAME}.csr \
-config clients/${CLIENT_NAME}.cnf
# Mit CA signieren
openssl x509 -req -days $DAYS \
-in clients/${CLIENT_NAME}.csr \
-CA ca/ca.crt \
-CAkey ca/ca.key \
-CAcreateserial \
-out clients/${CLIENT_NAME}.crt \
-extfile <(echo "basicConstraints=CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=clientAuth")
# P12 für Browser erstellen
openssl pkcs12 -export \
-out clients/${CLIENT_NAME}.p12 \
-inkey clients/${CLIENT_NAME}.key \
-in clients/${CLIENT_NAME}.crt \
-certfile ca/ca.crt \
-name "${CLIENT_NAME} mTLS Zertifikat"
echo ""
echo "=== Fertig! ==="
echo "Dateien erstellt:"
echo " - clients/${CLIENT_NAME}.crt (Zertifikat)"
echo " - clients/${CLIENT_NAME}.key (Private Key)"
echo " - clients/${CLIENT_NAME}.p12 (Browser-Import)"
Verwendung des Skripts
# Skript ausführbar machen
chmod +x config/traefik/certs/create-client.sh
# Zertifikat für einen Benutzer erstellen
./create-client.sh max max@example.com
# Das Skript fragt nach einem Export-Passwort für die .p12 Datei
Schritt 6: Client-Zertifikat im Browser importieren
Firefox
-
Einstellungen öffnen
-
“Datenschutz & Sicherheit” → “Zertifikate”
-
“Zertifikate anzeigen” klicken
-
Tab “Ihre Zertifikate” → “Importieren”
-
Die
.p12Datei auswählen und Passwort eingeben
Chrome / Edge
-
Einstellungen → “Datenschutz und Sicherheit”
-
“Sicherheit” → “Zertifikate verwalten”
-
Tab “Eigene Zertifikate” → “Importieren”
-
Die
.p12Datei auswählen
iOS (iPhone/iPad)
-
Die
.p12Datei per AirDrop oder E-Mail aufs Gerät übertragen -
Auf die Datei tippen → “Profil geladen”
-
Einstellungen → “Profil geladen” → “Installieren”
-
Passwort eingeben
Android
-
Einstellungen → “Sicherheit” → “Verschlüsselung & Anmeldedaten”
-
“Zertifikat installieren” → “VPN- und App-Nutzerzertifikat”
-
Die
.p12Datei auswählen
Ergebnis
Nach der Konfiguration passiert Folgendes, wenn jemand auf pangolin.domain.de zugreift:
-
Mit gültigem Zertifikat: Der Browser zeigt eine Zertifikatsauswahl an, man wählt sein Zertifikat und hat Zugriff
-
Ohne Zertifikat: Die Verbindung wird sofort abgelehnt - keine Anmeldeseite, keine Fehlermeldung, einfach “Verbindung abgelehnt”
Das bietet einen sehr starken Schutz, da Angreifer nicht einmal bis zur Anmeldeseite kommen.
Tipps und Best Practices
Zertifikate widerrufen
Sollte ein Zertifikat kompromittiert werden, gibt es zwei Möglichkeiten:
-
Einfach: Neue CA erstellen und alle Client-Zertifikate neu ausstellen
-
Professionell: Eine Certificate Revocation List (CRL) pflegen
Backup nicht vergessen
Folgende Dateien unbedingt sichern:
-
ca/ca.key- Ohne diesen Key können keine neuen Zertifikate erstellt werden -
ca/ca.crt- Wird von Traefik benötigt
Zertifikate sicher übertragen
Die .p12 Dateien niemals unverschlüsselt per E-Mail versenden. Besser:
-
Persönlich übergeben
-
Über einen sicheren Kanal (Signal, etc.)
-
Temporär auf einem passwortgeschützten Share
Fazit
Mit mTLS habe ich eine zusätzliche, sehr starke Sicherheitsebene für meinen Pangolin Reverse Proxy eingerichtet. Nur Geräte mit einem gültigen Client-Zertifikat können überhaupt eine Verbindung aufbauen. Das schützt effektiv vor unbefugtem Zugriff, selbst wenn Zugangsdaten geleakt werden sollten.
Der initiale Aufwand lohnt sich - einmal eingerichtet, ist die Verwaltung mit dem Skript sehr einfach und die Sicherheit deutlich erhöht.