Træfik und OAuth
Mein Setup mehrerer Docker-Container für verschiedene Anwendungen hatte eine Schwäche: Um die Anwendungen verwenden zu können, habe ich sie per Nginx Proxy zugänglich gemacht. Einzeln, und von Hand. Das Verfahren war hinderlich. Jetzt verwende ich Traefik dafür, und OAuth zur Anmeldung. Wie das geht, zeige ich hier.
Traefik Proxy ist ein dynamischer Reverse-Proxy für containerisierte Anwendungen. Traefik arbeitet nahtlos mit Containerwerkzeugen wie Docker Compose, Swarm oder Kubernetes zusammen, und kann selbständig hinzukommende Anwendungen nach Regeln verfügbar machen, oder gestoppte Anwendungen entfernen. Traefik enthält unter anderem Load Balancing und ist damit geeignet, viele Instanzen einer Anwendung hochverfügbar zu machen.
Ich hatte in der Vergangenheit einen ähnlichen Zweck mit manueller Arbeit verfolgt: Sorgfältig gewählte TCP-Ports auf Localhost habe ich mit einer manuellen nginx-Konfiguration an Server verbunden, Hostnamen gesetzt, Lets-Encrypt-Zertifikate eingerichtet und mehr. Viel Arbeit. Mit Traefik ist das einfacher: Anstatt Container und Proxy separat voneinander zu pflegen, setzt man Attribute (sog. Labels) am Container, welche Traefik auswertet. Schauen wir uns das mal an:
Das ist die Datei docker-compose.yml für Traefik selbst. Durch sie wird der Service traefik
definiert, und sein api@internal
genanntes Dashboard auch gleich bekannt gemacht. Dazu dienen die labels
-Zeilen, die man übrigens in Version 3 des docker-compose-Files auch als Dictionary formulieren darf.1
Der Eintrag traefik-forward-auth
dient der OAuth Integration, dazu weiter unten mehr.
Neben der Datei docker-compose.yml
lege ich ein Verzeichnis data
an, dieses enthält zwei weitere Dateien:
Diese Datei enthält die sog. statische Konfiguration von Traefik. Die hier gesetzten Werte könnten auch per Kommandozeile oder Umgebungsvariable gesetzt werden. Diese Datei legt grundlegende entryPoints
fest, aktiviert den Lets-Encrypt-Support und das Dashboard.
Im Gegensatz dazu bildet die zweite Datei die dynamische Konfiguration. Diese nennt man dynamisch, weil sie hauptsächlich zur Laufzeit von den Container-Attributen definiert wird - nur zusätzlich kann man auch zB eine Datei verwenden.
Anwendungsbeispiel
Das ist dann die docker-compose Definition einer Anwendung mit Traefik-Labels. Tatsächlich werden hier schon zwei Traefik-“Services” definiert:
traefik.http.routers.swb
hat nur die Aufgabe, eingehende http-Requests entgegenzunehmen und auf https umzuleiten.traefik.http.routers.swb-secure
bildet die SSL-Konfiguration und regelt die LetsEncrypt Integration über den in der Dateidata/traefik.yml
(oben) konfigurierten Resolver namenshttp
.
Wichtig ist, dass alle nach außen sichtbaren Anwendungen auf das passende Netzwerk konfiguriert wurden, hier: web
.
Debugging Tipps
Um die Logausgaben eines systemd-Services zu beobachten, kann man diesen Befehl verwenden:
journalctl -u dc@traefik.service -f
Mit -u
gibt man den Namen der zu beobachtenden Unit an, mit -f
folgt die Ausgabe neuen Zeilen. Das funktioniert gut, ist aber unhandlich, wenn man ständig Änderungen macht und diese bewerten möchte. Wenn ich Änderungen am Service mache und deren Auswirkung beobachten will, dann
- stoppe ich den Service mit
service <name> stop
- starte ich den Dienst von Hand mit
docker-compose up --remove-orphans
und lasse das im Terminal laufen - beende ich den Dienst bei Bedarf mit
strg-c
und starte ihn ggfs neu - setze ich am Ende den Dienst wieder automatisch fort:
service <name> start
OAuth zur Anmeldung
Das gefällt mir besonders gut: Dienste wie zB das Dashboard benötigen eine Anmeldung. Ich will aber nicht viele separate Datenbanken mit Usernamen und Passworten pflegen. Da kommt es gelegen, dass das von mir bereits verwendete Gitea auch als OAuth-Provider fungieren kann: Die Anmeldung erfolgt dann in Gitea mit den dort konfigurierten Benutzern und Passworten, angeschlossene Anwendungen wie das Dashboard sehen das Passwort nie.
Dazu gibt es eine praktische Software namens traefik-forward-auth
, die man als Middleware in Traefik einbinden kann. Eine solche Middleware wird aktiviert, bevor auf die eigentliche Zielanwendung zugegriffen wird. In der Datei docker-compose.yml
oben definiere ich dazu in Zeile 13 die Middleware namens traefik-forward-auth
und verwende sie in Zeile 46:
traefik.http.routers.traefik-secure.middlewares=traefik-forward-auth
Damit weise ich Traefik an, beim Zugriff auf den Service traefik-secure
die Middleware traefik-forward-auth
einzubinden, welche die Anmeldung an den konfigurierten OAuth Service delegiert. Die angegebene Datei server.env
befindet sich absichtlich nicht im Git und schaut schematisch so aus:
PROVIDERS_GENERIC_OAUTH_AUTH_URL=<url>/login/oauth/authorize
PROVIDERS_GENERIC_OAUTH_TOKEN_URL=<url>/login/oauth/access_token
PROVIDERS_GENERIC_OAUTH_USER_URL=<url>/api/v1/user
PROVIDERS_GENERIC_OAUTH_CLIENT_ID=12345678901234567890
PROVIDERS_GENERIC_OAUTH_CLIENT_SECRET=123456abcdef123456abcdef123456a
PROVIDERS_GENERIC_OAUTH_TOKEN_STYLE=query
SECRET=<secret>
Siehe dazu auch die Dokumentation für die Gitea Einbindung.