Ein Gateway statt zehn Logins: Authentik als Forward Auth vor Caddy
Wie sich selbst gehostete Dienste mit einem zentralen Login absichern lassen, auch wenn sie selbst weder SSO noch Zwei-Faktor-Authentifizierung unterstützen.
Mit der Zeit sammeln sich im Homelab einige Dienste an. Fast jeder davon bringt eine eigene Benutzerverwaltung mit, speichert Passwörter auf seine eigene Art und unterstützt im schlechtesten Fall nicht einmal Zwei-Faktor-Authentifizierung.
Man kann natürlich für jeden Dienst einen eigenen Benutzer anlegen und alles im Passwortmanager verwalten. Angenehmer wird es dadurch aber nicht. Vor allem löst es das eigentliche Problem nicht: Die Authentifizierung bleibt auf viele unterschiedliche Anwendungen verteilt.
Eine Alternative ist, die Anmeldung vor die Anwendungen zu setzen. Der Reverse Proxy prüft bei jeder Anfrage, ob der Benutzer bereits angemeldet ist. Falls nicht, wird er auf eine zentrale Loginseite weitergeleitet. Die eigentliche Anwendung erhält die Anfrage erst nach erfolgreicher Authentifizierung und muss selbst nichts von SSO verstehen.
Dieses Verfahren nennt sich Forward Auth. In meinem Homelab verwende ich dafür Caddy als Reverse Proxy und Authentik als Identity Provider.
Das zentrale Caddy-Snippet
Authentik bringt mit dem Embedded Outpost bereits den benötigten Endpunkt mit. Caddy kann dort für jede Anfrage prüfen, ob eine gültige Authentifizierung vorliegt. Ein zusätzlicher Outpost-Container ist für diesen Anwendungsfall nicht erforderlich.
Die Caddy-Konfiguration dafür lässt sich als wiederverwendbares Snippet definieren:
(authentik) {
reverse_proxy /outpost.goauthentik.io/* authentik-server:9000
forward_auth authentik-server:9000 {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid
trusted_proxies private_ranges
}
}
Eine geschützte Anwendung benötigt danach nur noch den Import des Snippets:
dashboard.example.com {
import authentik
reverse_proxy dashboard:7575
}
Einige Teile des Snippets wirken zunächst optional, sind es aber nicht.
Die Outpost-Route
reverse_proxy /outpost.goauthentik.io/* authentik-server:9000
Authentik verwendet für den Login und den anschliessenden Callback einen Pfad unterhalb der ursprünglich aufgerufenen Domain. Der Browser wird also beispielsweise auf folgende Adresse zurückgeleitet:
https://dashboard.example.com/outpost.goauthentik.io/callback
Ohne die zusätzliche Reverse-Proxy-Route landet dieser Request bei der eigentlichen Anwendung und endet dort normalerweise mit einem 404. Das passiert erst nach einer erfolgreichen Anmeldung, weshalb man den Fehler leicht an der falschen Stelle sucht.
Weitergabe der Benutzerinformationen
Mit copy_headers übernimmt Caddy die von Authentik gelieferten Benutzerinformationen und gibt sie an den geschützten Dienst weiter:
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid
Anwendungen mit Unterstützung für Header-basierte Authentifizierung können diese Informationen direkt verwenden. Der Benutzer ist dann nicht nur bei Authentik angemeldet, sondern auch innerhalb der Anwendung automatisch eingeloggt.
Unterstützt eine Anwendung diese Header nicht, werden sie einfach ignoriert. Der Zugriff ist trotzdem geschützt, innerhalb der Anwendung kann aber weiterhin eine zweite Anmeldung notwendig sein.
Vertrauenswürdige Proxys
trusted_proxies private_ranges
Diese Einstellung legt fest, von welchen Netzwerkbereichen Caddy weitergeleitete Clientinformationen akzeptiert. Ohne eine saubere Proxy-Konfiguration können Header wie X-Forwarded-For von nicht vertrauenswürdigen Absendern übernommen werden.
Anwendungen und Berechtigungen in Authentik
Die Caddy-Konfiguration übernimmt nur die technische Prüfung. Welche Anwendung geschützt wird und wer darauf zugreifen darf, wird in Authentik konfiguriert.
Dafür sind im Wesentlichen drei Objekte relevant:
- Der Provider definiert, wie die Authentifizierung erfolgt.
- Die Application repräsentiert den geschützten Dienst.
- Das Policy Binding legt fest, welche Benutzer oder Gruppen Zugriff erhalten.
Diese Objekte können über die Weboberfläche angelegt werden. Bei wenigen Diensten ist das noch überschaubar. Mit zunehmender Anzahl wird allerdings schnell unklar, welche Einstellung an welcher Stelle vorgenommen wurde.
Authentik unterstützt deshalb Blueprints. Damit lässt sich die Konfiguration als YAML-Datei verwalten:
version: 1
metadata:
name: dashboard (proxy forward-auth)
labels:
blueprints.goauthentik.io/instantiate: "true"
entries:
- model: authentik_providers_proxy.proxyprovider
state: present
identifiers:
name: dashboard
id: provider
attrs:
name: dashboard
mode: forward_single
external_host: https://dashboard.example.com
cookie_domain: example.com
authorization_flow: !Find
- authentik_flows.flow
- [slug, default-provider-authorization-implicit-consent]
invalidation_flow: !Find
- authentik_flows.flow
- [slug, default-provider-invalidation-flow]
- model: authentik_core.application
state: present
identifiers:
slug: dashboard
id: app
attrs:
name: Dashboard
slug: dashboard
provider: !KeyOf provider
meta_launch_url: https://dashboard.example.com
- model: authentik_policies.policybinding
state: present
identifiers:
target: !KeyOf app
group: !Find
- authentik_core.group
- [name, admin]
order: 0
attrs:
enabled: true
Die Datei wird im Blueprint-Verzeichnis von Authentik abgelegt und beim Start eingelesen. Authentik erzeugt die beschriebenen Objekte oder gleicht sie mit dem vorhandenen Zustand ab.
Damit liegt die Antwort auf die Frage, wer auf eine bestimmte Anwendung zugreifen darf, nicht mehr nur in der Authentik-Datenbank. Die Konfiguration kann zusammen mit der übrigen Homelab-Konfiguration in Git verwaltet werden. Änderungen sind damit nachvollziehbar und lassen sich bei Bedarf wieder zurücknehmen.
Ein gemeinsames Session-Cookie
Eine wichtige Einstellung im Provider ist cookie_domain:
cookie_domain: example.com
Wird hier die übergeordnete Domain eingetragen, kann das Authentik-Session-Cookie für alle darunterliegenden Subdomains verwendet werden.
Nach der ersten Anmeldung bei dashboard.example.com ist der Benutzer damit auch beim Aufruf von grafana.example.com oder paperless.example.com bereits authentifiziert.
Ohne diese Einstellung kann für jede Anwendung erneut ein Login erforderlich sein, obwohl alle denselben Authentik-Server verwenden.
Ausnahmen für APIs, Apps und Webhooks
Forward Auth funktioniert besonders gut für Anwendungen, die über einen normalen Browser aufgerufen werden. Nicht jeder Client kann allerdings mit einer Loginseite und einer Browser-Session umgehen.
Typische Problemfälle sind:
- Mobile Apps mit eigener Anmeldung
- API-Clients
- Webhooks
- Monitoring-Systeme
- automatisierte Skripte
Ein API-Client erwartet normalerweise eine JSON-Antwort. Wird er von Authentik stattdessen auf eine HTML-Loginseite umgeleitet, kann er damit nichts anfangen.
Solche Endpunkte müssen deshalb entweder von Forward Auth ausgenommen oder über einen separaten Hostnamen bereitgestellt werden.
Ein Beispiel ist eine Anwendung, bei der nur die Administrationsoberfläche geschützt werden soll, nicht aber deren API:
blog.example.com {
@admin {
path /admin /admin/*
not path /admin/api/*
}
forward_auth @admin authentik-server:9000 {
uri /outpost.goauthentik.io/auth/caddy
}
reverse_proxy blog:2368
}
Der ausgenommene Pfad darf dadurch allerdings nicht ungeschützt bleiben. Ein Webhook sollte beispielsweise eine Signatur oder ein Secret prüfen. Eine API kann mit einem eigenen Token arbeiten.
Ein Pfad ohne Forward Auth ist nicht automatisch unsicher. Entscheidend ist, dass der Dienst selbst eine geeignete Authentifizierung oder Signaturprüfung durchführt.
Authentik wird zur zentralen Abhängigkeit
Durch die zentrale Authentifizierung wird Authentik zu einer wichtigen Infrastrukturkomponente. Ist der Dienst nicht verfügbar, können auch die dahinterliegenden Anwendungen nicht mehr aufgerufen werden.
Das ist die logische Folge einer zentralen Zugangskontrolle. Im Homelab ist dieses Risiko meistens vertretbar, sollte aber bei der Planung berücksichtigt werden.
Vor allem die Systeme, die man zur Fehleranalyse benötigt, dürfen nicht ausschliesslich von Authentik abhängen. Dafür sollte es einen alternativen Zugangsweg geben, beispielsweise:
- Zugriff über ein internes Netzwerk oder VPN
- einen separaten Hostnamen mit Basic Auth
- direkten Zugriff über eine Management-Schnittstelle
- einen bewusst nicht über Forward Auth geschützten Dienst
Ein solcher Zugang sollte im normalen Betrieb nicht benötigt werden. Wenn Authentik oder der Reverse Proxy ausfällt, ist man aber froh, ihn zu haben.
Ergebnis
Nach der Umstellung ist nur noch eine Anmeldung pro Session erforderlich. Auch Anwendungen ohne eigene Zwei-Faktor-Authentifizierung profitieren von der zentralen Absicherung über Authentik.
Die Zugriffsrechte werden nicht mehr für jeden Dienst separat gepflegt, sondern über Gruppen und Policies gesteuert. Durch die Blueprints liegt die Konfiguration ausserdem versioniert im Repository.
Für einen neuen Dienst sind auf der Caddy-Seite nur noch zwei Zeilen notwendig:
import authentik
reverse_proxy neuer-dienst:8080
Dazu kommt ein Blueprint mit Provider, Application und der gewünschten Gruppe.
Der grösste Vorteil ist für mich nicht einmal das Single Sign-on selbst. Neue Dienste benötigen keine eigene Sicherheitslösung mehr, bevor sie sinnvoll verwendet werden können. Die eigentliche Frage lautet nur noch, welche Benutzergruppe Zugriff erhalten soll.