# 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. ```yaml 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: ```bash 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): ```bash 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--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. ```yaml 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-auth` → `authentik-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.