The 'Regelwerk Bindung resultiert in False' denial after login means the user is not a member of wiki-zugang (step 2.4). Record it plus the redirect-loop and outpost-offline cases so the next person recognises them fast.
269 lines
13 KiB
Markdown
269 lines
13 KiB
Markdown
# 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*** (die
|
|
Liste der Outposts) → **Create**.
|
|
|
|
⚠️ **Nicht „Outpost-Integrationen".** Das ist ein *anderer* Menüpunkt: dort legt
|
|
man eine Docker-/K8s-**Service-Verbindung** an, über die Authentik einen Outpost
|
|
selbst ausrollt — der Dialog „Neue Outpost-Integration" bietet deshalb nur
|
|
*Docker* oder *Kubernetes* und lässt sich nicht leer lassen. **Brauchen wir nicht**
|
|
(Prod würde sonst in Overminds Docker greifen). Wenn du dort gelandet bist:
|
|
Abbrechen und in *Outposts* wechseln.
|
|
|
|
Im **Outpost**-Formular:
|
|
- **Name**: `wiki-forward-auth`
|
|
- **Type**: `Proxy`
|
|
- **Integration**: das **Dropdown auf „No integration"/leer stehen lassen** (der
|
|
Standard) — der Container läuft extern auf Overmind (Teil 3) und meldet sich per
|
|
Token zurück. Nur wenn du dieses Feld auf Docker/K8s stellst, verlangt es eine
|
|
Service-Verbindung.
|
|
- **Applications**: **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.
|
|
|
|
```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.
|
|
|
|
## Fehlerbild
|
|
|
|
- **„Anfrage wurde verweigert — Regelwerk Bindung … resultiert in False"** nach
|
|
dem Login: Du bist **nicht in `wiki-zugang`** (Schritt 2.4 übersprungen). Das
|
|
`policy=None` in der Meldung ist normal — es ist eine *Gruppen*-Bindung. Fix:
|
|
*Directory → Groups → wiki-zugang* → Nutzer hinzufügen, neu einloggen.
|
|
- **Redirect-Schleife**: der `/outpost.goauthentik.io/`-Router hat versehentlich
|
|
die Auth-Middleware (Teil 3) — entfernen.
|
|
- **Outpost bleibt in Authentik „offline"/rot**: Version des `proxy`-Containers
|
|
passt nicht zur Server-Version (2.1) oder Token/`AUTHENTIK_HOST` falsch.
|
|
|
|
## 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.
|