Files
axion1337.chat-gitops/docs/deployment-guides/09-wiki-forward-auth.md
T
Thore Cimbal 310bb65b98 docs(wiki-auth): decide hostname axionwiki.lab (#0024), detail steps 2-3
Records the #0024 decision (axionwiki.lab) and flags it as a development-time
arrangement: the wiki still has to move into the ThreadNet Server Suite, and
surface alternatives beyond BookStack get re-examined afterwards. Expands the
Authentik outpost/token steps (version pinning, exact UI path, where the token
goes) and the Overmind/Traefik side (shared network, redirect-loop caveat, full
authResponseHeaders, request walk-through).
2026-08-11 12:00:00 +00:00

12 KiB

Docusaurus-Wiki hinter Authentik (Forward-Auth)

Status: vorbereitet, nicht ausgerollt · Host: axionwiki.lab (#0024 entschieden 2026-08-12) · Entscheidung: bei Docusaurus bleiben, Zugang per Authentik (DOC-03/#0020)

Docusaurus ist ein statischer Site-Generator — kein Nutzermodell, kein Login. Der Zugang wird deshalb im Reverse-Proxy davor erzwungen: Traefik fragt bei jedem Request einen Authentik-Outpost, ob eine gültige Sitzung vorliegt, und leitet sonst zu auth.axion1337.chat um. Erst nach Login gibt Traefik die statischen Seiten frei. Es ist ein Alles-oder-nichts-Tor (wer in der Gruppe wiki-zugang ist, sieht das ganze Wiki; sonst nichts) — für Bereichs-Rechte bräuchte es BookStack (ADR-0007), das war aber ausdrücklich nicht gewollt.

⚠️ Nur für die Entwicklung — das hier ist ein Zwischenstand

Diese Fassung (Docusaurus auf Overmind unter axionwiki.lab, Forward-Auth über den Prod-Authentik) trägt während der Entwicklung. Zwei Dinge stehen bewusst noch aus und dürfen dabei nicht in Vergessenheit geraten:

  1. Das Wiki zieht in die ThreadNet Server Suite um. Es soll Teil des reproduzierbaren Stacks werden (Vision „reproduzierbar für Dritte"), nicht ein Einzelstück auf dem Lab-Host. Dann ändern sich Host, Proxy und ggf. die Auth-Anbindung erneut. → [management-Issue: Wiki in die ThreadNet Server Suite umziehen]
  2. Danach werden Oberflächen-Alternativen über BookStack hinaus geprüft. Die Docusaurus-Entscheidung gilt für jetzt; die breitere Evaluation (nicht nur Docusaurus vs. BookStack) kommt nach dem Umzug. → [management-Issue: Wiki-Oberfläche über BookStack hinaus prüfen]

Alles unten ist deshalb so gebaut, dass es jetzt funktioniert und beim Umzug sauber ablösbar ist (eigener Outpost, keine Verdrahtung in fremde Stacks).

Topologie

Das Wiki läuft als Dokploy-Stack auf Overmind (git.lab/homelab/wiki, axionwiki.lab), Authentik im K3s-Cluster auf Hetzner. Deshalb ein eigener Proxy-Outpost als Container auf Overmind, statt des eingebetteten Outposts in Hetzner: So bleiben die Auth-Subrequests lokal auf Overmind — nur die Login-Umleitung quert den Site-to-Site-VPN zu auth.axion1337.chat. Das passt zur Leitlinie „das Lab hängt nicht am Prod-Host": ist eine Sitzung erst gesetzt, liest sich das Wiki im Lab auch ohne ständige Rückfrage nach Hetzner.

Browser ──▶ Traefik (Overmind) ──forwardAuth──▶ authentik-proxy (Overmind, :9000)
   │                                                    │ Sitzung? nein ──▶ 302
   └────────────── Login-Redirect ─────────────▶ auth.axion1337.chat (Hetzner, via VPN)

Teil 1 — Authentik-Blueprint (deklarativ)

Als neues Dokument in apps/authentik/authentik-blueprints.yaml einfügen (gleiche ConfigMap wie matrix-oidc-provider.yaml). Host ist entschieden (axionwiki.lab), also apply-fertig — trotzdem erst mit Teil 2/3 zusammen scharf schalten.

  wiki-forward-auth.yaml: |
    # yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json
    version: 1
    metadata:
      name: wiki-forward-auth
      labels:
        blueprints.goauthentik.io/instantiate: "true"
    entries:
      # Proxy-Provider im Forward-Auth-Modus. Kein internal_host (das wäre der
      # Proxy-Modus) — forward_single reicht Traefik nur das Ja/Nein zurück.
      - model: authentik_providers_proxy.proxyprovider
        state: present
        identifiers:
          name: ThreadNet Wiki
        id: wiki_proxy_provider
        attrs:
          mode: forward_single
          external_host: https://axionwiki.lab
          authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]]
          invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
          access_token_validity: hours=24

      - model: authentik_core.application
        state: present
        identifiers:
          slug: wiki
        id: wiki_app
        attrs:
          name: ThreadNet Wiki
          provider: !KeyOf wiki_proxy_provider
          meta_description: Internes Docusaurus-Wiki, nur Gruppe wiki-zugang
          policy_engine_mode: any
          open_in_new_tab: true

      # Zugangsbeschränkung: nur Mitglieder dieser Gruppe passieren das Tor.
      - model: authentik_core.group
        state: present
        identifiers:
          name: wiki-zugang
        id: wiki_group

      - model: authentik_policies.policybinding
        state: present
        identifiers:
          target: !KeyOf wiki_app
          group: !KeyOf wiki_group
          order: 0
        attrs:
          enabled: true
          negate: false

⚠️ Vor dem Merge von Hand verifizieren, nicht auf Flux vertrauen — ein Blueprint-Fehler scheitert still (Lehre aus dem matrix-recovery-Fix). Nach dem Einspielen prüfen, dass der Lauf successful ist:

kubectl exec -n authentik authentik-postgresql-0 -- sh -c \
  'PGPASSWORD="$(cat "$POSTGRES_PASSWORD_FILE")" psql -U authentik -d authentik -At -c \
   "SELECT name, status FROM authentik_blueprints_blueprintinstance WHERE name='"'"'wiki-forward-auth'"'"'"'

Teil 2 — Outpost + Token in Authentik (sorb, Schritt für Schritt)

Der Outpost ist der Prozess, den Traefik fragt; sein Token ist ein Credential und kommt nicht ins Repo.

2.1 — Laufende Authentik-Version feststellen (der Outpost-Container muss exakt dieselbe Version tragen, sonst verweigert Authentik die Anmeldung des Outposts):

kubectl -n authentik get deploy authentik-server \
  -o jsonpath='{.spec.template.spec.containers[0].image}'; echo
# -> ghcr.io/goauthentik/server:2024.x.y  --> merke dir 2024.x.y

2.2 — Outpost anlegen: Authentik-Admin → Applications → Outposts → Create.

  • Name: wiki-forward-auth
  • Type: Proxy
  • Integration: leer lassen (kein Docker-/K8s-Connector — der Container läuft extern auf Overmind, den deployen wir in Teil 3 selbst).
  • Applications/Providers: ThreadNet Wiki auswählen.
  • Advanced settings → authentik_host: https://auth.axion1337.chat (die URL, die der Container und der Browser fürs Login erreichen).

2.3 — Token abgreifen: beim neuen Outpost auf View Deployment Info (bzw. Directory → Tokens, Eintrag ak-outpost-<id>-api) → Token kopieren. Dieser Wert wird in Teil 3 als WIKI_OUTPOST_TOKEN gesetzt — sonst nirgends hin, nicht loggen, nicht committen.

2.4 — Wer rein darf: Directory → Groups → wiki-zugang (vom Blueprint angelegt) → die internen Nutzer hinzufügen. Wer nicht drin ist, bekommt nach dem Login 403.

Teil 3 — Overmind: Outpost-Container + Traefik (git.lab/homelab/wiki)

Beides gehört in den Dokploy-Stack des Wikis. Kernpunkte zuerst, dann das Fragment:

  • Gemeinsames Netz. Outpost- und Wiki-Container müssen im selben von Traefik beobachteten Docker-Netz liegen (bei Dokploy i. d. R. dokploy-network) — sonst findet die forwardAuth-Adresse den Outpost nicht.
  • Version pinnen auf die aus 2.1 ermittelte (proxy:2024.x.y == server).
  • AUTHENTIK_INSECURE: "false"auth.axion1337.chat hat ein gültiges öffentliches Zertifikat; kein Lab-CA-Trust nötig, weil der Outpost nach Hetzner spricht, nicht ins Lab.
  • Der /outpost.goauthentik.io/-Router bekommt die Auth-Middleware NICHT — sonst schützt sich der Login-Callback selbst aus und es entsteht eine Redirect-Schleife.
  • Das Token kommt als Dokploy-Environment/Secret WIKI_OUTPOST_TOKEN, nicht im Klartext in die committete Compose-Datei.
services:
  # 1) Der Proxy-Outpost — er beantwortet Traefiks forwardAuth-Frage.
  authentik-proxy:
    image: ghcr.io/goauthentik/proxy:2024.x.y   # == laufende Authentik-Version (2.1)
    restart: unless-stopped
    environment:
      AUTHENTIK_HOST: https://auth.axion1337.chat
      AUTHENTIK_INSECURE: "false"
      AUTHENTIK_TOKEN: ${WIKI_OUTPOST_TOKEN}     # aus Teil 2.3 (Dokploy-Secret)
    networks: [dokploy-network]
    labels:
      traefik.enable: "true"
      traefik.docker.network: dokploy-network
      # Router NUR für den Outpost-Callback-Pfad — OHNE Auth-Middleware:
      traefik.http.routers.wiki-authentik.rule: "Host(`axionwiki.lab`) && PathPrefix(`/outpost.goauthentik.io/`)"
      traefik.http.routers.wiki-authentik.entrypoints: websecure
      traefik.http.routers.wiki-authentik.tls: "true"
      traefik.http.routers.wiki-authentik.service: wiki-authentik
      traefik.http.services.wiki-authentik.loadbalancer.server.port: "9000"

  # 2) Der bestehende Docusaurus-Service — nur um die Auth-Middleware erweitert.
  wiki:
    # ... bestehendes image/build/volumes ...
    networks: [dokploy-network]
    labels:
      traefik.enable: "true"
      traefik.docker.network: dokploy-network
      traefik.http.routers.wiki.rule: "Host(`axionwiki.lab`)"
      traefik.http.routers.wiki.entrypoints: websecure
      traefik.http.routers.wiki.tls: "true"
      traefik.http.routers.wiki.middlewares: "wiki-auth@docker"
      # Die forwardAuth-Middleware:
      traefik.http.middlewares.wiki-auth.forwardauth.address: "http://authentik-proxy:9000/outpost.goauthentik.io/auth/traefik"
      traefik.http.middlewares.wiki-auth.forwardauth.trustForwardHeader: "true"
      traefik.http.middlewares.wiki-auth.forwardauth.authResponseHeaders: "X-authentik-username,X-authentik-groups,X-authentik-entitlements,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version"

networks:
  dokploy-network:
    external: true

Ablauf einer Anfrage (zum Nachvollziehen beim Debuggen):

  1. Browser → axionwiki.lab; Traefik ruft wiki-authauthentik-proxy:9000/.../auth/traefik.
  2. Keine Sitzung → Outpost antwortet 302 auf auth.axion1337.chat (Login).
  3. Nach Login kommt der Browser auf axionwiki.lab/outpost.goauthentik.io/callback zurück (der Router aus Service 1, ohne Middleware), Outpost setzt das Cookie.
  4. Erneuter Request trägt das Cookie → auth/traefik gibt 200 + die X-authentik-*-Header → Traefik reicht an Docusaurus durch.

Abhängigkeiten

  • #0024 (Wiki-Hostname)entschieden: axionwiki.lab (2026-08-12). external_host, der Traefik-Host() und der Cookie-Scope zeigen alle darauf.
  • #0018 (Wiki-Rollout) — sollte abgeschlossen sein, bevor das Tor davor kommt.
  • Site-to-Site-VPN — die Login-Umleitung und die Outpost-Konfigsync brauchen auth.axion1337.chat erreichbar; interne Nutzer im Lab/über VPN erreichen beides.

Verifikation

  1. curl -sI https://axionwiki.lab (unauthentifiziert) → 302 auf auth.axion1337.chat.
  2. Login als wiki-zugang-Mitglied → Wiki lädt.
  3. Login als Nicht-Mitglied → 403 (Policy greift).
  4. Outpost-Gesundheit: im Authentik-Admin zeigt der Outpost wiki-forward-auth grün/last seen und die passende Version.
  5. Resilienz-Gegenprobe: auth.axion1337.chat kurz nicht erreichbar → bestehende Sitzung liest weiter (lokaler Outpost), nur neuer Login blockiert.

Was hier bewusst offen bleibt

  • Nichts ist live geschaltet — der Blueprint liegt als Vorlage hier, nicht in der angewandten ConfigMap; der Outpost-Token ist sorbs Schritt.
  • Kein Bereichs-Schutz — Grenze von Docusaurus + Forward-Auth.
  • Zwischenstand — siehe „Nur für die Entwicklung" oben: Umzug in die ThreadNet Server Suite und die breitere Oberflächen-Evaluation stehen noch aus.