diff --git a/docs/deployment-guides/09-wiki-forward-auth.md b/docs/deployment-guides/09-wiki-forward-auth.md index a7f6729..6f2611f 100644 --- a/docs/deployment-guides/09-wiki-forward-auth.md +++ b/docs/deployment-guides/09-wiki-forward-auth.md @@ -1,7 +1,8 @@ # Docusaurus-Wiki hinter Authentik (Forward-Auth) -**Status**: vorbereitet, **nicht** ausgerollt · Entscheidung: bei Docusaurus -bleiben, Zugang per Authentik einschränken (DOC-03/#0020, bei Docusaurus) +**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 @@ -11,6 +12,25 @@ 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`, @@ -30,8 +50,9 @@ Browser ──▶ Traefik (Overmind) ──forwardAuth──▶ authentik-proxy ## Teil 1 — Authentik-Blueprint (deklarativ) Als neues Dokument in `apps/authentik/authentik-blueprints.yaml` einfügen -(gleiche ConfigMap wie `matrix-oidc-provider.yaml`). **Erst nach Klärung von -#0024 einfügen** — `external_host` muss exakt dem echten Wiki-Host entsprechen. +(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: | @@ -51,7 +72,6 @@ Als neues Dokument in `apps/authentik/authentik-blueprints.yaml` einfügen id: wiki_proxy_provider attrs: mode: forward_single - # MUSS dem echten Wiki-Host entsprechen (#0024: axionwiki.lab vs wiki.lab). 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]] @@ -88,77 +108,139 @@ Als neues Dokument in `apps/authentik/authentik-blueprints.yaml` einfügen ``` ⚠️ **Vor dem Merge von Hand verifizieren**, nicht auf Flux vertrauen — ein -Blueprint-Fehler scheitert still (Lehre aus dem `matrix-recovery`-Fix): +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 deploy/authentik-server -- \ - ak apply_blueprint /blueprints/... # bzw. den ConfigMap-Pfad; auf Fehlerausgabe achten +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 (Authentik-UI, **sorb**) +## Teil 2 — Outpost + Token in Authentik (**sorb**, Schritt für Schritt) -Der Outpost trägt ein **Token** — das ist ein Credential, kommt nicht ins Repo. +Der Outpost ist der Prozess, den Traefik fragt; sein **Token** ist ein Credential +und kommt **nicht** ins Repo. -1. *Authentik → Applications → Outposts → Create*: Typ **Proxy**, Name - `wiki-forward-auth`, Provider **ThreadNet Wiki** zuweisen. -2. Das erzeugte **Outpost-Token** kopieren (View-Token) — es geht gleich in den - Overmind-Stack (Teil 3), **nirgends sonst hin**. -3. *Directory → Groups → wiki-zugang*: die internen Nutzer aufnehmen, die das Wiki - sehen dürfen. +**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`) -Neben den Docusaurus-Service in den Dokploy-/Compose-Stack. Image-Tag **auf die -laufende Authentik-Version pinnen** (Outpost- und Server-Version müssen passen). +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 # == laufende Authentik-Version + 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, als Dokploy-Secret + 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" - # bestehender Docusaurus-Service — nur die Auth-Middleware ergänzen: + # 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-email,X-authentik-name,X-authentik-uid" + 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 ``` -Beide Services müssen im selben Traefik-Netz liegen. Der `/outpost.goauthentik.io/`- -Router **ohne** die Auth-Middleware (sonst Redirect-Schleife). +**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)** — blockierend: `external_host`, der Traefik-`Host()` - und der Cookie-Scope müssen alle auf denselben Namen zeigen. +- **#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 braucht `auth.axion1337.chat` vom - Browser aus erreichbar; interne Nutzer im Lab/über VPN erreichen beides. +- **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. Unauthentifiziert `https://axionwiki.lab` → **302** auf `auth.axion1337.chat`. +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. Gegenprobe: `auth.axion1337.chat` per Netzausfall kurz nicht erreichbar → - bestehende Sitzung liest weiter (lokaler Outpost), nur neuer Login blockiert. +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** — das ist die Grenze von Docusaurus + Forward-Auth; wenn - je granulare Rechte nötig werden, ist das der Punkt, an dem ADR-0007 (BookStack) - wieder aufgemacht gehört. +- **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.