Zum Inhalt springen
← Alle Beiträge
· 7 Min. Lesezeit· Von

Authelia oder Authentik: SSO per Forward-Auth vor selbstgehostete Dienste stellen

Was Forward-Auth ist, wann Authelia und wann authentik das richtige Werkzeug ist – plus ein vollständiges Minimalbeispiel mit Authelia 4.39.20 hinter Traefik v3.7, inklusive Cookie-Domain-, Middleware- und Session-Stolperfallen.

AutheliaAuthentikSSOForward AuthTraefikDockerSelf-HostingDevOpsIT-Sicherheit

Viele selbstgehostete Dienste bringen gar keine Benutzerverwaltung mit: ein Prometheus, ein internes Monitoring-Frontend, ein Traefik-Dashboard, ein schnell hingestelltes Admin-Tool. Solange das nur im LAN läuft, ist das verschmerzbar. Sobald der Dienst über eine Domain aus dem Internet erreichbar ist, brauchen Sie eine Türsteher-Schicht davor – und zwar eine, die Sie nicht für jeden Dienst neu bauen. Genau das leistet Forward-Auth. Dieser Beitrag erklärt die Kernidee, vergleicht kurz und ehrlich die beiden verbreiteten Kandidaten Authelia und authentik und stellt dann ein vollständiges, lauffähiges Minimalbeispiel mit Authelia hinter Traefik auf. Den Traefik-Grundaufbau setze ich dabei voraus.

Was Forward-Auth macht – und die Kernidee

Forward-Auth ist ein Middleware-Muster im Reverse Proxy. Trifft eine Anfrage ein, schickt Traefik sie nicht sofort ans Backend, sondern stellt zuerst eine Sub-Anfrage an einen Auth-Endpunkt. Antwortet dieser mit einem 2xx-Status, wird die Originalanfrage durchgereicht; jede andere Antwort geht unverändert an den Client zurück – in der Praxis meist eine Weiterleitung auf die Login-Seite. Genau so beschreibt es die Traefik-Dokumentation zur forwardAuth-Middleware.

Traefik übergibt dem Auth-Dienst dabei den Kontext der Originalanfrage in X-Forwarded--Headern: X-Forwarded-Method, X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Uri und X-Forwarded-For. Aus Host und Pfad leitet der Auth-Dienst ab, welche Zugriffsregel greift. Umgekehrt darf er im Erfolgsfall Header zurückgeben, die Traefik per authResponseHeaders an das Backend weiterreicht – bei Authelia sind das Remote-User, Remote-Groups, Remote-Email und Remote-Name. Anwendungen, die Header-Authentifizierung unterstützen, kennen den angemeldeten Benutzer damit ohne eigenen Login.

Der entscheidende Punkt: Die geschützte Anwendung wird nicht angefasst. Sie muss weder umgebaut noch überhaupt informiert werden.

   Client                 Traefik                Authelia            app
     |  GET app.example.com  |                       |                |
     |---------------------->|  /api/authz/forward-auth               |
     |                       |---------------------->|                |
     |                       |  302 auth.example.com |                |
     |  302 -> Login-Seite   |<----------------------|                |
     |<----------------------|                       |                |
     |     ... Login, Session-Cookie fuer .example.com ...             |
     |  GET app.example.com  |                       |                |
     |---------------------->|---------------------->|                |
     |                       |  200 + Remote-User    |                |
     |                       |<----------------------|                |
     |                       |-------------------------------------->|
     |     200 Inhalt        |<--------------------------------------|
     |<----------------------|                       |                |

Authelia oder authentik?

Beide beherrschen Forward-Auth mit Traefik. Sie unterscheiden sich aber nicht im Detail, sondern im Anspruch.

Authelia – aktuell 4.39.20, veröffentlicht am 26. Mai 2026 – ist ein einzelnes Go-Binary mit einer YAML-Konfigurationsdatei. Benutzer kommen entweder aus einer schlichten YAML-Datei oder aus LDAP; als Datenbank genügt eine lokale SQLite-Datei. Eine Verwaltungsoberfläche gibt es nicht, Sie editieren Dateien. Dafür ist der Betriebsaufwand minimal und die gesamte Konfiguration liegt versionierbar im Git-Repo.

authentik – aktuell 2026.5.6, veröffentlicht am 22. Juli 2026 – ist ein vollwertiger Identity Provider mit Weboberfläche, grafischem Flow-Editor, SAML, OIDC, SCIM und LDAP-Outpost. Die Dokumentation nennt als Mindestanforderung einen Host mit zwei CPU-Kernen und 2 GB RAM, dazu PostgreSQL. Immerhin ist die Abhängigkeitsliste zuletzt kürzer geworden: Seit Version 2025.10 entfällt Redis komplett, weil Caching, Tasks, eingebetteter Outpost und WebSockets nach Postgres migriert wurden.

Die Empfehlung ist damit unspektakulär, aber klar: Wenn Sie eine Handvoll selbstgehosteter Dienste vor fremden Augen schützen wollen und Konfiguration als Datei im Repo ohnehin Ihr Arbeitsmodell ist, nehmen Sie Authelia. Sobald echte Föderation ins Spiel kommt – SAML gegen einen Geschäftspartner, SCIM-Provisioning, Self-Service für Endanwender, Zugriffsrichtlinien, die nicht mehr in eine Handvoll YAML-Zeilen passen –, ist authentik das passendere Werkzeug und der zusätzliche Betriebsaufwand gerechtfertigt. Der Rest dieses Beitrags zeigt den Authelia-Weg.

Das Minimalbeispiel: die Konfiguration

Achtung, Authelia hat sein Konfigurationsformat zwischen den Versionen mehrfach geändert – insbesondere die Session- und Secret-Schlüssel. Das folgende Beispiel ist gegen die Konfigurationsvorlage von Authelia 4.39.20 geprüft; ältere Anleitungen im Netz nutzen häufig noch das alte flache session-Format ohne cookies-Liste oder ein jwt_secret auf oberster Ebene. Beides ist seit 4.38 veraltet: Authelia mappt solche Altkonfigurationen beim Start zwar noch automatisch auf das neue Format und protokolliert eine Deprecation-Warnung – wer aber altes und neues session-Format mischt, bekommt einen harten Startfehler.

Die Datei liegt für das Container-Image unter /config/configuration.yml:

log:
  level: 'info'

server:
  address: 'tcp://:9091/'

identity_validation:
  reset_password:
    jwt_secret: '<PLATZHALTER_JWT_SECRET_64_ZEICHEN>'

authentication_backend:
  file:
    path: '/config/users_database.yml'
    watch: true
    password:
      algorithm: 'argon2'

access_control:
  default_policy: 'deny'
  rules:
    - domain: 'app.example.com'
      policy: 'one_factor'

session:
  secret: '<PLATZHALTER_SESSION_SECRET_64_ZEICHEN>'
  cookies:
    - domain: 'example.com'
      authelia_url: 'https://auth.example.com'
      default_redirection_url: 'https://www.example.com'
      name: 'authelia_session'
      same_site: 'lax'
      inactivity: '15m'
      expiration: '8h'
      remember_me: '1M'

regulation:
  max_retries: 3
  find_time: '2m'
  ban_time: '5m'

storage:
  encryption_key: '<PLATZHALTER_STORAGE_ENCRYPTION_KEY_64_ZEICHEN>'
  local:
    path: '/config/db.sqlite3'

notifier:
  filesystem:
    filename: '/config/notification.txt'

Die drei Platzhalter erzeugen Sie mit dem mitgelieferten CLI:

docker run --rm authelia/authelia:4.39.20 authelia crypto rand --length 64 --charset alphanumeric

Die Benutzerdatenbank ist eine zweite YAML-Datei, /config/users_database.yml:

users:
  emre:
    disabled: false
    displayname: 'Emre Yurtbay'
    password: '<PLATZHALTER_ARGON2ID_HASH>'
    email: 'emre@example.com'
    groups:
      - 'admins'

Den Hash erzeugt derselbe Container – Argon2id ist die Voreinstellung:

docker run --rm -it authelia/authelia:4.39.20 authelia crypto hash generate argon2

Die Ausgabe beginnt mit $argon2id$v=19$... und wird komplett in einfachen Anführungszeichen ins Feld password kopiert.

Das Minimalbeispiel: Traefik und der geschützte Dienst

Jetzt der Stack. Traefik bekommt zwei Rollen: Er veröffentlicht das Authelia-Portal unter auth.example.com und definiert die Forward-Auth-Middleware, die anschließend an beliebig viele Dienste gehängt werden kann.

networks:
  proxy:

services:
  traefik:
    image: traefik:v3.7
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=proxy"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--entrypoints.websecure.address=:443"
      # Nur setzen, wenn Traefik selbst hinter einem Proxy steht:
      # - "--entrypoints.websecure.forwardedheaders.trustedips=10.0.0.0/8"
      - "--certificatesresolvers.le.acme.email=admin@example.com"
      - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.le.acme.tlschallenge=true"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./letsencrypt/acme.json:/letsencrypt/acme.json"
    networks: [proxy]
    restart: unless-stopped

  authelia:
    image: authelia/authelia:4.39.20
    volumes:
      - "./authelia:/config"
    networks: [proxy]
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      # Das Portal selbst - eine ganz normale Route, OHNE Auth-Middleware
      - "traefik.http.routers.authelia.rule=Host(`auth.example.com`)"
      - "traefik.http.routers.authelia.entrypoints=websecure"
      - "traefik.http.routers.authelia.tls.certresolver=le"
      - "traefik.http.services.authelia.loadbalancer.server.port=9091"
      # Die Middleware, hier einmalig definiert
      - "traefik.http.middlewares.authelia.forwardAuth.address=http://authelia:9091/api/authz/forward-auth"
      - "traefik.http.middlewares.authelia.forwardAuth.trustForwardHeader=true"
      - "traefik.http.middlewares.authelia.forwardAuth.maxResponseBodySize=8192"
      - "traefik.http.middlewares.authelia.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Email,Remote-Name"

  app:
    image: traefik/whoami
    networks: [proxy]
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.app.rule=Host(`app.example.com`)"
      - "traefik.http.routers.app.entrypoints=websecure"
      - "traefik.http.routers.app.tls.certresolver=le"
      - "traefik.http.routers.app.middlewares=authelia@docker"
      - "traefik.http.services.app.loadbalancer.server.port=80"

Starten:

mkdir -p authelia letsencrypt
touch letsencrypt/acme.json
chmod 600 letsencrypt/acme.json
docker compose up -d

Ein Aufruf von https://app.example.com landet jetzt auf dem Anmeldeformular unter auth.example.com. Nach dem Login erscheint die whoami-Ausgabe – und dort sehen Sie in der Header-Liste Remote-User: emre und Remote-Groups: admins. Das ist der ganze Trick, und traefik/whoami ist genau deshalb der beste Testdienst: Es zeigt Ihnen, was tatsächlich am Backend ankommt.

Drei typische Stolperfallen

1. Cookie-Domain und authelia_url müssen zusammenpassen. Das Session-Cookie wird auf session.cookies[].domain gesetzt. Der Wert in authelia_url muss laut Dokumentation entweder genau diesem Host entsprechen oder – mit . vorangestellt – auf ihn enden. domain: 'example.com' und authelia_url: 'https://auth.example.com' passen also; domain: 'auth.example.com' bei gleichzeitig geschütztem app.example.com passt nicht. Der geschützte Dienst muss ebenfalls unter dieser Domain liegen, sonst schickt der Browser das Cookie nie mit – und Sie landen in einer Endlosschleife aus Login und Redirect. Zweiter Auslöser derselben Schleife: dem Authelia-Router versehentlich die eigene authelia@docker-Middleware zu verpassen. Das Portal muss ungeschützt erreichbar bleiben.

2. Provider-Namensraum und Reihenfolge der Middleware. Eine über Docker-Labels definierte Middleware liegt im Namensraum des Docker-Providers und wird korrekt als authelia@docker referenziert – ohne Suffix funktioniert es nur innerhalb desselben Providers. Wichtiger noch: Traefik wendet Middlewares in der Reihenfolge an, in der sie deklariert sind. Bei middlewares=stripprefix@docker,authelia@docker sieht Authelia also den bereits gekürzten Pfad, bei umgekehrter Reihenfolge den ursprünglichen. Da Ihre access_control-Regeln auf Host und Pfad matchen, entscheidet diese Reihenfolge über Zugriff oder Zugriffsverweigerung. Am Rande, aber relevant: trustForwardHeader ist in Traefik v3.7 als deprecated markiert und fällt in der nächsten Major-Version weg. Setzen Sie den Wert vorerst trotzdem explizit – ohne die Angabe protokolliert Traefik beim Start eine Warnung und fällt auf ein Legacy-Verhalten zurück. Sauber ist die Kombination aus forwardedHeaders.trustedIPs am Entrypoint (unkonfiguriert lassen, wenn Traefik selbst am Netzrand steht: dann gilt kein Client als vertrauenswürdig und alle eingehenden X-Forwarded--Header werden verworfen) und trustForwardHeader: true an der Middleware.

3. Sessions und Storage liegen nicht dort, wo Sie denken. Authelias Standard-Session-Provider ist der Arbeitsspeicher. Ein docker compose restart wirft damit sämtliche angemeldeten Benutzer heraus. Für mehr als einen Testlauf – und zwingend, sobald Sie mehr als eine Authelia-Instanz betreiben – gehört ein Redis-Provider darunter. Genauso wichtig: /config muss ein echtes Volume oder Bind-Mount sein, sonst verschwinden mit dem Container die SQLite-Datei und damit alle TOTP-Registrierungen. Und der storage.encryption_key lässt sich nachträglich nicht einfach in der Datei ändern; er verschlüsselt Spalten in der Datenbank und muss über das CLI (authelia storage encryption change-key) rotiert werden. Legen Sie ihn also einmal richtig an.

Wie es weitergeht

Damit steht ein Zugangsschutz, der vor jedem weiteren Dienst nur noch eine einzige Zeile kostet: traefik.http.routers.<name>.middlewares=authelia@docker. Die naheliegenden nächsten Schritte sind zweiter Faktor per TOTP oder WebAuthn (policy: 'two_factor' statt one_factor), feinere Regeln über subject und networks sowie ein LDAP-Backend statt der YAML-Datei. Tiefer geht es in der Traefik-Integrationsanleitung von Authelia, in der Referenz zur Zugriffssteuerung und in der Traefik-Dokumentation zur ForwardAuth-Middleware. Wenn Sie noch bei der Proxy-Wahl stehen: Der Vergleich Caddy vs. Traefik ordnet ein, wofür sich der Label-Ansatz überhaupt lohnt.

Hinweis zur Veröffentlichung: Dieser Beitrag war für den 6. August 2026 geplant. Wegen einer technischen Störung der automatischen Veröffentlichung ist er erst am 11. August 2026 online gegangen. Alle Angaben wurden vor der Veröffentlichung erneut auf Aktualität geprüft.

Hinweis: Die Beiträge dieses Blogs werden unter Einsatz von KI erstellt und vor der Veröffentlichung redaktionell geprüft. Die redaktionelle Verantwortung trägt Emre Yurtbay (siehe Impressum).

Projekt besprechen