sichere https Verbindung: Traefik Reverse Proxy + Let´s Encrypt
Für nahezu alle bekannten Websysteme gibt es fertige Docker-Container, bzw. können diese relativ einfach selbst erstellt oder bestehende Container angepasst werden. Als Docker-Container können die Webservices sehr einfacher installiert und betrieben werden.
Sicherer Zugriff vom Internet auf Webservices im Heimnetzwerk
Ziel dieses Beitrags Aufwand Voraussetzung
Vom NGINX Proxy zu Traefik
Ursprünglich habe ich für den Betrieb mehrerer Docker-basierter Webseiten "NGINX Proxy Automation" von Evert Ramos verwendet, siehe: nginx-LetsEncrypt Reverse Proxy in der Praxis. Das Projekt funktioniert wunderbar, hat aber im Vergleich zum alternativen Reverse-Proxy: Traefik einige Limitationen. Mit Traefik ist es gefühlt noch einfacher und dank des Web-Dashboards übersichtlicher mehrere Webseiten auf einem Server zu betreiben. Zahlreiche Middleware bieten zudem zusätzliche Funktionen.
Schematische Darstellung
Der Traefik-Docker-Container kümmert sich um das Ausstellen, Verwalten und Verwenden von SSL-Zertifikaten und leitet Webanfragen auf die jeweiligen Webservices, die wiederum auch als Docker-Container bereitgestellt werden.
Traefik kümmert sich um
-
-
- den Zugriff auf einen oder mehrere Docker-Webanwendungen
- die Zertifikatsverwaltung für eine HTTPS-Verschlüsselung
-
Software | Traefik |
---|---|
GitHub | https://github.com/traefik/traefik |
aktuelle Version | 3.2.0 |
gefunden | 28.10.2024 |
Installation: Server, Domain (DNS) und Docker
Docker Basics
Docker ermöglicht es, Services oder Applikationen per Befehl in einem sogenannten Container zu starten.Ein Container ist eine vom Betriebssystem (OS) unabhängige isolierte Umgebung:
Beim ersten Start eines Containers, lädt Docker selbstständig alle notwendigen Quellen
aus dem Internet.
Docker kann unter Windows, macOS oder einer Linux-Distribution installiert werden,
siehe auch: Docker
Für das hier vorgestellte Setup wird Docker benötigt, siehe: Docker Installation Linux oder Docker Installation Windows. Bei einem Server eines Hosting-Providers kann Docker und das hier vorgestellte Setup über eine SSH-Verbindung zum Server und über die beschriebenen Bash-Befehle erfolgen. Sollte sich der Server im eigenen Heimnetzwerk befinden, muss am Router ein entsprechendes Port-Forwarding eingerichtet werden, siehe: aus dem Internet verfügbar machen: Port-Forwarding - OpenWRT. Sollte der Internetprovider die öfftenliche IP-Adresse regelmässig ändern, siehe: free DynDNS Service - Zugriff bei wechselnder öffentlicher IP.
Wer Docker bereits installiert hat und die entsprechenden A-Records in der Domainverwaltung eingetragen hat, benötigt eine Textdatei mit Namen docker-compose.yml und als Vorlage folgenden Inhalt:
docker-compose.yml
services:
traefik:
image: "traefik:v2.8"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
- "--certificatesresolvers.myresolver.acme.email=admin@domain.tld"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
expose:
# traefik dashboard port
- 8080
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.domain.tld`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls.certresolver=myresolver"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.middlewares=traefik-auth"
- "traefik.http.middlewares.traefik-auth.basicauth.users=admin:??????"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
restart: "always"
networks:
default:
name: webproxy
external: true
Das Beispiel verwendet Bind-Mounts und keine Volumes, siehe: Docker Daten speichern: Docker Volumes vs. Host-Ordner.
Anpassen der docker-compose.yml-Datei
Die docker-compose-yml-Vorlage verwendet nicht nur für die veröffentlichten Web-Service-Container ein Zertifikat, sondern auch für das Traefik-Dashboard, wodurch auch der Zugriff auf das Dashboard verschlüsselt stattfindet. Damit das Dashboard nicht von jedermann aufgerufen werden kann, wurde eine Basic-Authentication mit einem Admin-User hinterlegt.
Vor dem eigentlichen Start muss die Datei natürlich ein klein wenig angepasst werden:
- traefik.domain.tld sollte mit einer eigenen Domäne für das Traefik-Dashboard,
- acme.email mit einer Mailadresse und
- basicauth.users mit einem eigenen Benutzernamen und einem entsprechenden md5-Kennwort ersetzt werden.
Anstelle von traefik.domain.tld kann für den Zugriff auf das Dashboard eine Subdomain einer vorhandenen Domain verwendet werden. Als Beispiel könnte in der DNS-Zonenverwaltung für die Domäne libe.net ein A-Record für eine Subdomain traefik.libe.net auf die IP-Adresse des Webservers erstellt werden.
Natürlich muss in der docker-compose.yml-Datei für dieses Beispiel auch traefik.libe.net anstelle von traefik.domain.tld verwendet werden.
Die Mailadresse wird von Let’s Encrypt verwendet, um bestimmte Informationen oder Warnungen zu versenden: Zum Beispiel, falls ein Zertifikat bald abläuft. Keine Sorge, Traefik kümmert sich um die Verlängerung der Zertifikate, entsprechend sollten E-Mails mit der Information über den Ablauf eines Zertifikats ernst genommen werden.
Das Kennwort für die Basic-Authentifizierung kann nicht im Klartext eingetragen werden und muss daher vorab über einen Befehl als MD5-Hash erzeugt werden und für die Verwendung in docker-compose müssen einfache $ mit $$ ersetzt werden, siehe: doc.traefik.io/traefik/middlewares/http/basicauth/
In Linux kann dazu folgender Befehl verwendet werden: echo $(htpasswd -nB admin) | sed -e s/\\$/\\$\\$/g
root@Webserver:~# echo $(htpasswd -nB admin) | sed -e s/\\$/\\$\\$/g
New password:
Re-type new password:
admin:$$2y$$05$$7liwlteyGcPyCr2oXc4Do.G/wWxjwWLYpTWg6vvQ4pU7PLAS4ysMm
Anstelle von admin kann hier auch ein anderer Benutzername verwendet werden.
Nach dem Anpassen der Datei können wir den Traefik-Container starten. Nachträglich gestartete Docker-Container mit den hinterlegten Labels für dessen Webservice werden im laufenden Betrieb von Traefik verarbeitet und veröffentlicht.
Start des Traefik-Containers
Als Netzwerk für Traefik und für die später erstellen Webservices verwende ich ein eigenes Netzwerk mit Namen „webproxy“, welches ich vor dem Start von Traefik (docker compose up) erstelle:
cd /OrdnerDockerComposeFileTraefik
docker network create webproxy
docker compose up -d
Sollte alles richtig funktionieren, kann das Traefik-Dashboard über die angegebene Domain und nach Eingabe des angegebenen Benutzers aufgerufen werden:
Websites oder Services erstellen und hosten
Wird ein anderer Docker-Container mit dem Netzwerk webproxy und entsprechenden Labels für Traefik gestartet, kann dieser über Traefik aufgerufen werden.
Wer mehrere Webservices starten will, muss darauf achten, dass unterschiedliche Labels in den jeweiligen Konfigurationen verwendet werden:
z. B. Webservice 1:
labels:
- "traefik.enable=true"
- "traefik.http.routers.webservice1.rule=Host(`service1.domain.tld`)"
- "traefik.http.routers.webservice1.entrypoints=web"
- "traefik.http.routers.webservice1.entrypoints=websecure"
...
und für einen weiteren Docker-Container mit einem anderen Service:
labels:
- "traefik.enable=true"
- "traefik.http.routers.webservice2.rule=Host(`service2.domain.tld`)"
- "traefik.http.routers.webservice2.entrypoints=web"
- "traefik.http.routers.webservice2.entrypoints=websecure"
...
„web“ als Entrypoint erlaubt den unverschlüsselten Zugriff auf den Service und „websecure“ den verschlüsselten Zugriff über HTTPS.
Siehe auch: eigener Webserver - wie funktioniert eigentlich das Internet?
Authentifizierung
Wer für einen Container eine simple Basic-Authentifizierung hinzufügen möchte, kann dies durch das Hinzufügen der Basic-Authentication-Middleware. (gleich dem Traefik-Dashboard)
(Basic-Authentication: Bevor das Dashboard aufgerufen werden kann, muss der hinterlegte Benutzername und das Passwort eingegeben werden)
Die folgende Zeile legt eine Middleware mit Namen: webserver-basic-auth an und definiert damit den Benutzernamen und das Passwort:
- "traefik.http.middlewares.webserver-basic-auth.basicauth.users=admin:md5Passwort"
Das Kennwort kann, wie bereits beschrieben mit einem Linux-Befehl in einen md5-Hash umgewandelt werden:
echo $(htpasswd -nB admin) | sed -e s/\\$/\\$\\$/g
Damit der Router, hier mit Namen "webserver" die angelegte Middleware verwendet, habe ich folgende Zeile hinzugefügt:
- "traefik.http.routers.webserver.middlewares=webserver-basic-auth"
labels:
- "traefik.enable=true"
- "traefik.http.routers.webserver.rule=Host(`domain.tld`)"
- "traefik.http.routers.webserver.entrypoints=web"
- "traefik.http.routers.webserver.entrypoints=websecure"
- "traefik.http.routers.webserver.tls.certresolver=myresolver"
- "traefik.http.middlewares.webserver-basic-auth.basicauth.users=admin:md5Passwort"
- "traefik.http.routers.webserver.middlewares=webserver-basic-auth"
Alternativ zur Basic-Authentifizierung kann auch ein externer Anmeldeanbieter verwendet werden. Als Beispiel die Verwendung von Google als Anmeldeanbieter, also die Verwendung eines Google-Kontos für die Anmeldung auf den eigenen Webservices, siehe: Traefik Google-Authentifizierung.
Webservices außerhalb von Docker bereitstellen
Um zum Beispiel sicher auf meinen Router zu Hause zugreifen zu können, habe ich diesen auch über Traefik veröffentlicht. Zunächst habe ich einen neuen Ordner für mögliche weitere Webservices angelegt: "/conf". Der Ordner kann dann in der docker-compose.yml-Datei in "command:" erwähnt und mittels "volumes:" eingebunden werden:
version: "3.3"
services:
traefik:
image: "traefik"
container_name: "traefik"
command:
...
- "--providers.file.directory=/etc/traefik/conf"
...
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./conf:/etc/traefik/conf"
restart: "always"
Im Anschluss ist es möglich zusätzliche Webservices über eigene Konfig-Dateien zu veröffentlichen:
Als Beispiel, für die Veröffentlichung der Router-Weboberfläche:
File ./conf/router.yml
http:
routers:
openwrt:
entrypoints: "websecure"
service: "openwrt@file"
rule: "Host(`router.domain.tld`)"
tls:
certresolver: "myresolver"
middlewares: "forward-auth-verify@docker"
services:
openwrt:
loadbalancer:
servers:
- url: "http://192.168.1.1:80"
In dem Beispiel wird für eine zusätzliche Authentifizierung die zuvor erwähnte vorgeschaltete Anmeldung über Google aktiviert: "forward-auth-verify"-Middleware, siehe: Traefik Google-Authentifizierung.
Hier noch ein Beispiel eines Webservices mit Basic-Authentifizierung:
http:
middlewares:
webservice2-basic-auth:
basicAuth:
users:
- "root:???"
routers:
webservice2:
entrypoints: "websecure"
service: "webservice2@file"
rule: "Host(`webservice2.domain.tld`)"
tls:
certresolver: "myresolver"
middlewares: "webservice2-basic-auth"
services:
webservice2:
loadbalancer:
servers:
- url: "http://192.168.1.15:80"
Für eine Basic-Authentication muss das Passwort in der YAML-Datei nicht escaped werden, der Befehl für das Erstellen des Kennworts lautet daher wie folgt:
echo $(htpasswd -nB root)
https-Backend Webservice mit Self-Signed Zertifikat
Sollte das Backend-Webservice mit https und einem Self-Signed-Zertifikat betrieben werden, können mit dem Command "serverstransport.insecureskipverify" alle Zertifikate akzeptieren werden:
version: "3.3"
services:
traefik:
image: "traefik"
container_name: "traefik"
command:
...
- '--serverstransport.insecureskipverify'
...
Zusätzliche SAN (Subject Alternative Names)
Damit ein Webzertifikat für mehrere Subdomains ausgestellt werden kann, können sogenannte SAN verwendet werden:
labels:
- "traefik.enable=true"
- "traefik.http.routers.webservice.rule=Host(`domain.tld`,`www.domain.tld`,`dev.domain.tld`,`dev2.domain.tld)"
- "traefik.http.routers.webservice.entrypoints=web"
- "traefik.http.routers.webservice.entrypoints=websecure"
- "traefik.http.routers.webservice.tls.certresolver=myresolver"
Umleitungen
Für das Umleiten von Subdomains auf eine andere Subdomain, können Regex-Ausdrücke verwendet werden.
Diverse Subdomains und/oder Non-WWW auf WWW umleiten
Das folgende Beispiel leitet alle Subdomains und die Root-Domain (Non-WWW) auf eine URL mit WWW um:
domain.tld, dev.domain.tld und dev2.domain.tld werden auf www.domain.tld umgeleitet. Damit das Zertifikat auch alle Domänen-Namen enthält, müssen diese in der Host-Rule angegeben werden:
labels:
- "traefik.enable=true"
- "traefik.http.routers.webservice.rule=Host(`domain.tld`,`dev.domain.tld`,`dev2.domain.tld`,`www.domain.tld`)"
- "traefik.http.routers.webservice.entrypoints=web"
- "traefik.http.routers.webservice.entrypoints=websecure"
- "traefik.http.routers.webservice.tls.certresolver=myresolver"
- "traefik.http.routers.webservice.middlewares=redirect_???2www"
- "traefik.http.middlewares.redirect_???2www.redirectregex.regex=^https://(dev.|dev2.)?domain.tld(.*)"
- "traefik.http.middlewares.redirect_???2www.redirectregex.replacement=https://www.domain.tld$${2}"
- "traefik.http.middlewares.redirect_???2www.redirectregex.permanent=true"
Diverse Subdomains und/oder WWW auf Non-WWW
labels:
- "traefik.enable=true"
- "traefik.http.routers.webservice.rule=Host(`domain.tld`,`dev.domain.tld`,`dev2.domain.tld`,`www.domain.tld`)"
- "traefik.http.routers.webservice.entrypoints=web"
- "traefik.http.routers.webservice.entrypoints=websecure"
- "traefik.http.routers.webservice.tls.certresolver=myresolver"
- "traefik.http.routers.webservice.middlewares=redirect_www2none"
- "traefik.http.middlewares.redirect_www2none.redirectregex.regex=^https://(www.|dev.|dev2.)domain.tld(.*)"
- "traefik.http.middlewares.redirect_www2none.redirectregex.replacement=https://domain.tld$${2}"
- "traefik.http.middlewares.redirect_www2none.redirectregex.permanent=true"
HTTP auf HTTPS umleiten (nicht global, nur für den jeweiligen Container)
labels:
- "traefik.enable=true"
- "traefik.http.routers.webservice.rule=Host(`domain.tld`,`dev.domain.tld`,`dev2.domain.tld`,`www.domain.tld`)"
- "traefik.http.routers.webservice.entrypoints=web"
- "traefik.http.routers.webservice.entrypoints=websecure"
- "traefik.http.routers.webservice.tls.certresolver=myresolver"
- "traefik.http.routers.webservice_http.rule=Host(`domain.tld`,`www.domain.tld`,`dev.domain.tld`)"
- "traefik.http.routers.webservice_http.entrypoints=web"
- "traefik.http.routers.webservice_http.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
Access Log
Um Client-Zugriffe in einer Datei zu protokollieren, reicht es folgende Settings in die docker-compose.yml-Datei aufzunehmen:
version: "3.3"
services:
traefik:
image: "traefik"
container_name: "traefik"
command:
...
- "--accesslog=true"
- "--accesslog.filePath=/logs/access.log"
...
volumes:
...
- "./logs/:/logs/"
...
FAQ
Was ist ein Reverse-Proxy?
Ein Reverse Proxy nimmt Webanfragen entgegen und leitet diese an dahinterliegende Webservices weiter. Damit die Anfragen an den richtigen Webservice weitergeleitet werden, verwenden die meisten Reverse-Proxys den Host-Header (=Domainnamen). Ein Reverse-Proxy kann zusätzliche Funktionen für einen besseren Schutz, einen optimierten Datenverkehr oder eine zusätzliche Authentifizierung bieten.
Wozu wird ein Reverse-Proxy verwendet?
Ein Reverse Proxy wird überwiegend für den Webzugriff auf einen oder mehrere Webserver vorgeschaltet. Neben dem Verteilen der Anfragen auf mehrere Webservices, wird ein Reverse Proxy häufig für ein sogenanntes SSL-Offloading verwendet. Der Reverse-Proxy kümmert sich dabei um das Bereitstellen der Webservices über HTTPS und übernimmt neben dem Verschlüsseln und Entschlüsseln des Datenverkehrs auch häufig das Zertifikatsmanagement.
Was ist der Unterschied zwischen einem Reverse-Proxy und einem Load Balancer?
Die Aufgabe eines Load Balancer ist primär die Last zwischen verschiedenen Servern oder Geräten zu verteilen. Ein Reverse-Proxy kann neben einem Lastausgleich auch noch andere Funktionen wie SSL-Offloading oder Caching und wird überwiegend für Webserver (HTTP) eingesetzt. Ein Load Balancer hingegen kann auch für andere Protokolle verwendet werden.
In der Praxis: Schritt für Schritt - YouTube-Video
Repräsentativ für andere Webservices wird in dem Video zusätzlich zu dem hier vorgestellten Setup Home Assistant in Form eines Containers veröffentlicht.
Webservices, Beispiele
Hier eine Liste von Webservices, die ich bisher getestet habe:
{{percentage}} % positiv
DANKE für deine Bewertung!
Fragen / Kommentare
(sortiert nach Bewertung / Datum) [alle Kommentare(neueste zuerst)]
Hallo, schöne Anleitung! Ich habe den Docker Stack auf einer Portainer Instanz auf einem QNAP ausprobiert. Dabei habe ich immer wieder den Fehler "port is missing" bekommen: level=error msg="service \"traefik-traefik\" error: port is missing" providerName=docker container=traefik-traefik.... Mit dem zusätzlichen Label - "traefik.http.services.traefik.loadbalancer.server.port=80" startet Traefik ohne Fehlermeldung. Allerdings hab ich es bis jetzt nicht geschafft das Traefik Dashboard aufzurufen. Irgendwas an der Portweiterleitung klemmt. Firewalls hab ich schon ausgeschlossen. Es könnte noch an belegten Ports auf dem QNAP liegen (QNAP: 8080, 443, Portainer: 8443) oder daran, dass ich ein VLAN betreibe. Letzteres schliesse ich eigentlich aus, da ich aus der Shell im Traefik Container z.B. google.de anpingen kann. Für Hinweise bin ich dankbar. Vielleicht ist auch das zusätzliche Label noch nicht korrekt. Beste Grüße Christian
Hallo, nach längerer Suche funktioniert das Traefik Dashboard mit basicAuth jetzt and auch der Traefik/whoami ist erreichbar. BasicAuth / Dashboard: Damit ist eigentlich alles in Ordnung. Das Problem liegt bei der Einstellung "expose: 8080" bzw. dem fehlenden loadbalancer. Zunächst zu expose. Da ich noch ziemlich neu in der Docker-Welt bin, ging ich irrtümlich davon aus, dass dies eine Traefik spezifische Einstellung ist und dass das Dashboard über den angegebenen Port 8080 erreichbar sein sollte. Tatsächlich sagt die docker compose reference dazu: "Expose ports without publishing them to the host machine - they’ll only be accessible to linked services. Only the internal port can be specified." Das Dashboard ist natürlich ganz normal über den Namen in der Host rule (im Beispiel traefik.domain.tld) erreichbar. Die Frage ist also, für was ist die Einstellung "expose: 8080" gut? Erschwerend kam hinzu, dass ohne den loadbalancer ein Fehler auftritt, der die Erzeugung der Middleware für die basicAuth verhindert. Theoretisch sollte Traefik unter Docker automatisch den Port des Servers verwenden, wenn es nur einen gibt: "Port detection works as follows: If a container exposes a single port, then Traefik uses this port for private communication. If a container exposes multiple ports, or does not expose any port, then you must manually specify which port Traefik should use for communication by using the label traefik.http.services.<service_name>.loadbalancer.server.port (Read more on this label in the dedicated section in routing)." Das scheint nicht ganz zu funktionieren, da jedenfalls bei mir der Fehler auftritt. Traefik/whoami: Standardmässig startet Traefik/whoami auf Port 80. Ich habe aber versucht, den Server auf Port 443 anzusprechen. Das kann natürlich nicht funktionieren. Hoffe, das hilft dem ein oder anderen. Beste Grüße Christian
Beitrag erstellt von anonym