diff --git a/.gitea/workflows/momentrum-ci.yml b/.gitea/workflows/momentrum-ci.yml index 1c6a351..91ccc90 100644 --- a/.gitea/workflows/momentrum-ci.yml +++ b/.gitea/workflows/momentrum-ci.yml @@ -1,11 +1,14 @@ -# Momentrum CI (Contabo-Gitea) — build · test · push · deploy(dev) +# Momentrum CI (Contabo-Gitea) — build · test · push · deploy(prod) # Zielt auf das Gitea auf momentrum-k3s-01 (org: momentrum). # -# Umgebungs-Modell fuer ibo (bewusst NUR dev+prod, keine PR-Previews): -# - dev = deployt AUTOMATISCH bei jedem main-Push (dieser Deploy-Step bumpt -# apps/internal/ibo/dev.yaml im CD-Repo -> ArgoCD synct dev). -# - prod = wird MANUELL promotet (Tag aus dev.yaml -> app.yaml committen, -# siehe infra/RUNBOOK-ibo.md). Die CI fasst prod NICHT an. +# Umgebungs-Modell fuer ibo (wie momentrum-kanban: PR-Preview-dev + prod): +# - dev = EPHEMERE PR-Previews. Kein Deploy-Step noetig: das ArgoCD- +# ApplicationSet (momentrum-deploy apps/internal/ibo/preview.yaml) liest +# den Tag pr-- direkt aus dem PR -> https://ibo-pr-.dev.momentrum.de. +# PR braucht das Gitea-Label "preview" (Opt-in-Gate). +# - prod = deployt AUTOMATISCH bei jedem main-Push (Job "deploy" bumpt +# apps/internal/ibo/app.yaml im CD-Repo -> ArgoCD synct prod). +# Rollback = git revert im CD-Repo (siehe infra/RUNBOOK-ibo.md). # # Image-Tag ist immutable pro Commit: # - Pull Request: pr-- @@ -102,26 +105,26 @@ jobs: helm lint "${{ env.CHART_PATH }}" helm template ibo "${{ env.CHART_PATH }}" >/dev/null - # CD-Write-back: nur auf main. Bumpt den Image-Tag in apps/internal/ibo/dev.yaml - # im CD-Repo -> ArgoCD deployt DEV automatisch. Prod bleibt unberuehrt (Promote - # ist ein bewusster manueller Commit, siehe RUNBOOK). DEPLOY_TOKEN = Gitea-Token - # mit Write auf momentrum-deploy. - deploy-dev: - name: Deploy DEV (Image-Tag -> CD-Repo) + # CD-Write-back: nur auf main (= PR-Merge). Bumpt den Image-Tag in + # apps/internal/ibo/app.yaml im CD-Repo -> ArgoCD deployt PROD automatisch. + # PR-Previews deployen NICHT hierueber (die treibt das ApplicationSet direkt aus + # dem PR). DEPLOY_TOKEN = Gitea-Token mit Write auf momentrum-deploy. + deploy: + name: Deploy PROD (Image-Tag -> CD-Repo) runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' needs: [version, build-push, helm-validate] steps: - - name: Write-back image.tag nach momentrum-deploy (dev.yaml) + - name: Write-back image.tag nach momentrum-deploy (app.yaml) run: | TAG="${{ needs.version.outputs.tag }}" git clone -q "http://ci:${{ secrets.DEPLOY_TOKEN }}@100.111.27.99:3000/momentrum/momentrum-deploy.git" /tmp/deploy cd /tmp/deploy - APP_FILE="apps/internal/ibo/dev.yaml" + APP_FILE="apps/internal/ibo/app.yaml" grep -qE '^ tag: ' "$APP_FILE" || { echo "FEHLER: 'tag:'-Zeile nicht in $APP_FILE gefunden"; exit 1; } sed -i "s|^ tag: .*| tag: ${TAG}|" "$APP_FILE" git config user.email "ci@momentrum.local" git config user.name "Momentrum CI" - git commit -am "ibo: deploy dev ${TAG}" || { echo "kein Change - Tag identisch"; exit 0; } + git commit -am "ibo: deploy prod ${TAG}" || { echo "kein Change - Tag identisch"; exit 0; } git push origin HEAD:main - echo "Deployed dev tag ${TAG} -> momentrum-deploy" + echo "Deployed prod tag ${TAG} -> momentrum-deploy" diff --git a/infra/RUNBOOK-ibo.md b/infra/RUNBOOK-ibo.md index b557614..c4826ce 100644 --- a/infra/RUNBOOK-ibo.md +++ b/infra/RUNBOOK-ibo.md @@ -1,19 +1,17 @@ -# Runbook — ibo (Immo-Bot) containerisieren, deployen, Zitadel-SSO davorschalten +# Runbook — ibo (Immo-Bot): CI/CD, PR-Preview-dev + prod, Zitadel-SSO -Immo-Bot = Flask-Web-App + Selenium-Chrome-Sidecar (noVNC-Live-Ansicht). Zwei -feste Umgebungen (KEINE PR-Previews): +Immo-Bot = Flask-Web-App + Selenium-Chrome-Sidecar (noVNC-Live-Ansicht). +Umgebungs-Modell **wie momentrum-kanban**: ephemere PR-Previews als „dev" + eine +stehende prod. Beide hinter Zitadel-SSO (die App selbst hat KEINE eigene Auth). -| Umgebung | Host | TLS-Secret | Deploy | -|----------|------------------------|--------------|------------------------------------------| -| dev | `dev.ibo.momentrum.de` | `ibo-dev-tls`| **automatisch** bei jedem main-Push (CI) | -| prod | `ibo.momentrum.de` | `ibo-tls` | **manuell promotet** (Tag-Copy, s. u.) | +| Umgebung | Host | SSO-Proxy | Deploy | +|----------|----------------------------------|-------------------|-------------------------------------------------| +| dev | `ibo-pr-.dev.momentrum.de` | generisches `dev-sso` | ephemer pro PR mit Label `preview` (ApplicationSet) | +| prod | `ibo.momentrum.de` | per-App `ibo-sso` | **automatisch** bei jedem main-Merge (CI → CD-Repo) | -Beide hinter demselben Zitadel-oauth2-proxy `ibo-sso` -(`cookie-domain .ibo.momentrum.de` deckt dev **und** prod ab). - -Die Muster spiegeln kanban (CI/Chart/CD) und luxly-sso/dev-sso (oauth2-proxy). -Cluster-, Zitadel- und DNS-Schritte sind **manuell** — dieses Runbook ist die -Anleitung. Reihenfolge unten einhalten. +Die Muster spiegeln kanban/luxly (CI/Chart/CD/ApplicationSet) und dev-sso/ibo-sso +(oauth2-proxy). Cluster-, Zitadel- und DNS-Schritte sind **manuell** — dieses +Runbook ist die Anleitung. Reihenfolge unten einhalten. --- @@ -21,116 +19,130 @@ Anleitung. Reihenfolge unten einhalten. ```sh cd projekte/ibo -git init && git add -A && git commit -m "ibo: containerize + CI + infra" +git add -A && git commit -m "ibo: PR-Preview-dev + prod, Zitadel-SSO" git remote add origin momentrum-gitea:momentrum/ibo.git # SSH-Alias wie andere Repos -git push -u origin main # push-create legt momentrum/ibo an, falls aktiviert +git push -u origin main ``` -**Gitea-Actions-Secrets** am Repo `momentrum/ibo` setzen (Settings → Actions → Secrets): +**Gitea-Actions-Secrets** am Repo `momentrum/ibo` (Settings → Actions → Secrets): - `REGISTRY_USER`, `REGISTRY_TOKEN` — Push in die Gitea-Registry. - `DEPLOY_TOKEN` — Gitea-Token mit **Write auf `momentrum/momentrum-deploy`** - (Write-back des dev-Image-Tags). + (Write-back des prod-Image-Tags bei main-Merge). --- -## 1. Zitadel: Redirect-URI ergaenzen (KEIN neuer Client) +## 1. Zitadel: Redirect-URIs (KEIN neuer Client) -`ibo-sso` nutzt — wie `luxly-sso` — den **bestehenden geteilten** Zitadel-Client -(K8s-Secret `oauth2-proxy-dev` in ns `iam`). Es ist **kein neuer Client** und -**kein neues Secret** noetig. Nur am vorhandenen Client im Zitadel-Console -(`https://auth.momentrum.de`) die Redirect-URI ergaenzen: +`ibo-sso` und `dev-sso` nutzen — wie `luxly-sso` — den **bestehenden geteilten** +Zitadel-Client (K8s-Secret `oauth2-proxy-dev` in ns `iam`). **Kein neuer Client, +kein neues Secret.** Nur am vorhandenen Client (`https://auth.momentrum.de`) die +Redirect-URI ergaenzen: -- Redirect-URI **hinzufuegen**: `https://sso.ibo.momentrum.de/oauth2/callback` - -(EIN Login-Host `sso.ibo.momentrum.de` deckt per `cookie-domain .ibo.momentrum.de` -dev **und** prod ab. Wer prod spaeter mit eigenem Blast-Radius trennen will: -eigenen Client + eigenes SealedSecret anlegen und im `ibo-sso`-Deployment -referenzieren — die `oauth2-proxy-dev`-Secret-Refs dort ersetzen.) +- prod: `https://sso.ibo.momentrum.de/oauth2/callback` ← nur diese ist ibo-spezifisch +- dev (Previews): `https://sso.dev.momentrum.de/oauth2/callback` ← existiert i. d. R. + schon (kanban/luxly-Previews nutzen dasselbe dev-sso). Nichts zu tun, falls vorhanden. --- -## 2. oauth2-proxy `ibo-sso` (contabo-infra) — deployen +## 2. Cluster-Voraussetzungen (contabo-infra) — muessen laufen -Manifeste liegen in `contabo-infra/platform/manifests/ibo-sso/` -(Deployment/Service, Middlewares `oauth2-ibo-auth`/`oauth2-ibo-errors`, Ingress -`sso.ibo.momentrum.de`, oauth2-Passthrough-Route). Sie referenzieren das -bestehende Secret `oauth2-proxy-dev` + die ConfigMap `oauth2-proxy-dev-templates` -— **nichts einzusealen**. +Fuer **prod** (`platform/manifests/ibo-sso/`, ArgoCD-App `platform/apps/ibo-sso.yaml`): +- oauth2-proxy `ibo-sso` (Deployment/Service, ns `iam`). +- Middlewares `iam-oauth2-ibo-auth@kubernetescrd` / `iam-oauth2-ibo-errors@kubernetescrd`. +- Ingress `sso.ibo.momentrum.de` (Login-Host, ohne Auth davor). +- **Geteilte** stripPrefix-Middleware `iam-strip-novnc@kubernetescrd` (`strip-novnc.yaml`) + — die Previews referenzieren sie cross-namespace (s. §4). -Die ArgoCD-App `platform/apps/ibo-sso.yaml` wird vom platform-App-of-Apps -automatisch aufgenommen (gleiche Ablage wie `luxly-sso.yaml`) — committen & -pushen genuegt; alternativ einmalig `kubectl apply -f platform/manifests/ibo-sso/`. -Ausserdem ist das ibo-Repo in `platform/manifests/argocd-projects/internal.yaml` -(sourceRepos) bereits ergaenzt — sonst lehnt ArgoCD die App-Source ab. +Fuer **dev/Previews** (bereits vorhanden, von kanban/luxly geteilt): +- `dev-sso` (generischer oauth2-proxy `*.dev.momentrum.de`, Middlewares + `iam-oauth2-auth`/`iam-oauth2-errors`, Login `sso.dev.momentrum.de`). +- SealedSecret `gitea-appset-token` (ns `argocd`) — Gitea-Read fuer den PR-Generator. +- AppProject `previews` — erlaubt Source `momentrum/*` (deckt ibo ab) und + begrenzt Previews auf `preview-*`-Namespaces. **Blockt bewusst + traefik.io/Middleware im Preview-ns** → deshalb die geteilte `strip-novnc` in §2. -Ergebnis: -- Middlewares `iam-oauth2-ibo-auth@kubernetescrd` / `iam-oauth2-ibo-errors@kubernetescrd` - stehen bereit (die App-Ingresses referenzieren sie). -- `https://sso.ibo.momentrum.de` liefert die Login-Seite (kein Auth davor). +App-of-Apps nimmt `ibo-sso.yaml` automatisch auf — committen & pushen genuegt; +alternativ einmalig `kubectl apply -f platform/manifests/ibo-sso/`. Das ibo-Repo +ist in `platform/manifests/argocd-projects/internal.yaml` (sourceRepos) bereits +erlaubt (sonst lehnt ArgoCD die prod-App-Source ab). --- ## 3. DNS -A/AAAA-Records auf die Cluster-Ingress-IP zeigen lassen: -- `ibo.momentrum.de` -- `dev.ibo.momentrum.de` -- `sso.ibo.momentrum.de` +A/AAAA-Records auf die Cluster-Ingress-IP: +- `ibo.momentrum.de` (prod) +- `sso.ibo.momentrum.de` (prod-Login) -cert-manager (`letsencrypt-prod`, HTTP-01) stellt die Zertifikate `ibo-tls`, -`ibo-dev-tls`, `ibo-sso-tls` automatisch aus, sobald DNS steht. +Previews brauchen **keinen eigenen Record** — sie laufen unter dem bestehenden +Wildcard `*.dev.momentrum.de` (wie kanban/luxly). cert-manager +(`letsencrypt-prod`, HTTP-01) stellt `ibo-tls`, `ibo-sso-tls` und die per-Preview- +Certs (`ibo-pr--tls`) automatisch aus, sobald DNS/Ingress stehen. --- -## 4. Deploy-Reihenfolge +## 4. CD-Apps im `momentrum-deploy` (bereits angelegt) -1. Repo gepusht (Schritt 0) → CI baut das erste Image und bumpt - `apps/internal/ibo/dev.yaml` (`image.tag`). -2. `ibo-sso` laeuft (Schritt 2), DNS steht (Schritt 3). -3. CD-Apps im `momentrum-deploy` committen: `apps/internal/ibo/dev.yaml` + - `apps/internal/ibo/app.yaml`. ArgoCD legt Namespace `apps-ibo` an und synct. -4. **Erstes Prod-Promote:** den von der CI in `dev.yaml` gesetzten `image.tag` - nach `app.yaml` kopieren (siehe unten) — sonst hat prod noch `tag: latest`. +- `apps/internal/ibo/app.yaml` — prod-Application (`ibo.momentrum.de`, + ns `apps-internal`, SSO `iam-oauth2-ibo-*`). Auto-Sync; `image.tag` bumpt die CI. +- `apps/internal/ibo/preview.yaml` — PR-Preview-ApplicationSet (Gitea-PR-Generator, + Label-Gate `preview`). Rendert ibos Chart aus dem **PR-Branch** und setzt pro PR: + Host `ibo-pr-.dev.momentrum.de`, `priorityClassName: preview`, gedeckelte + Resources (preview-ns-Quota: ≤2 CPU / 1536Mi Limits gesamt), + `ssoMiddlewares: iam-oauth2-errors,iam-oauth2-auth` (dev-sso) und + `novncStripPrefixRef: iam-strip-novnc@kubernetescrd` (statt eigener Middleware). + +committen & pushen → ArgoCD legt die prod-App + das ApplicationSet an. --- -## 5. Prod promoten (Routine) +## 5. Deploy-Flows -Prod bekommt bewusst NUR den Tag, den man freigibt: +**dev / Preview (pro PR):** +1. PR gegen `main` oeffnen → CI baut `pr--` (Job build-push). +2. Am PR das Gitea-Label **`preview`** setzen (Opt-in-Gate = Concurrency-Deckel). +3. ArgoCD-ApplicationSet (Polling 120 s) legt `ibo-pr-` + ns + `preview-ibo-pr-` an → `https://ibo-pr-.dev.momentrum.de`. +4. PR schliessen/mergen oder Label entfernen → Application + Namespace kaskadieren + weg (finalizer + gemanagter Namespace, KEIN CreateNamespace). -```sh -cd momentrum-deploy -DEVTAG=$(grep -E '^ tag: ' apps/internal/ibo/dev.yaml | awk '{print $2}') -sed -i "s|^ tag: .*| tag: ${DEVTAG}|" apps/internal/ibo/app.yaml -git commit -am "ibo: promote ${DEVTAG} -> prod" && git push -``` +**prod (bei main-Merge):** +- CI-Job `deploy` schreibt den neuen `image.tag` in `apps/internal/ibo/app.yaml` + zurueck → ArgoCD synct prod automatisch. Kein manueller Schritt. +- **Rollback:** `git revert` des CI-Commits im `momentrum-deploy` (ArgoCD synct + zurueck). Alternativ `image.tag` in `app.yaml` auf einen aelteren Tag setzen. -ArgoCD synct prod auf den neuen Tag. Rollback = `git revert` dieses Commits. +> Wer prod bewusst manuell freigeben will statt Auto-Deploy: den `deploy`-Job in +> `.gitea/workflows/momentrum-ci.yml` entfernen und den Tag per Hand nach +> `app.yaml` committen. Aktuell ist Auto-Deploy aktiv (wie kanban). --- ## 6. Browser-Live-Ansicht (noVNC) Der Automations-Chrome laeuft im `selenium`-Sidecar; sein Bild wird per noVNC -gestreamt und in der Web-App eingebettet (Button „👁️ Browser live ansehen", -oeffnet auch automatisch beim Consent-Browser). Routing: +gestreamt und in der Web-App eingebettet (Button „👁️ Browser live ansehen"). +Routing: - App-Container → Chrome via `SELENIUM_REMOTE_URL=http://localhost:4444`. - Client-Browser → `https:///novnc/…` → Ingress-Pfad `/novnc` → stripPrefix-Middleware → Sidecar-Port 7900 (noVNC/websockify). -- Steuerbar durch die SSO-Cookie (gleicher Host, gleiche Middlewares). + - prod: chart-eigene Middleware `-ibo-novnc` (App-ns). + - Preview: geteilte `iam-strip-novnc` (weil das `previews`-Project eigene + Middlewares im Preview-ns blockt). +- Zugriff durch die SSO-Cookie (gleicher Host, gleiche Middleware-Kette). Der Browser laeuft **serverseitig** im Container — es wird NICHT der lokale -Browser des Nutzers ferngesteuert (das ginge von einer gehosteten App aus nicht). +Browser des Nutzers ferngesteuert. --- ## 7. Break-glass (Notzugang) Faellt Zitadel aus: die SSO-Middlewares temporaer aus `ingress.ssoMiddlewares` -im jeweiligen CD-App-YAML entfernen (die App selbst hat keine eigene Auth → -sie ist dann OFFEN). Nur kurz, danach sofort zuruecksetzen. Nie ungeschuetzt -lassen. +in `apps/internal/ibo/app.yaml` (prod) entfernen (die App selbst hat keine eigene +Auth → sie ist dann OFFEN). Nur kurz, danach sofort zuruecksetzen. Nie +ungeschuetzt lassen. --- @@ -139,10 +151,11 @@ lassen. - [ ] `docker compose up -d` lokal → `http://localhost:5000` UI, „Browser live ansehen" zeigt den Chrome im iframe (localhost:7900). - [ ] `curl -s localhost:5000/healthz` → `{"ok":true}`. -- [ ] main-Push → CI gruen (version→test→build-push→helm-validate→deploy-dev), - `dev.yaml`-Tag gebumpt, ArgoCD rollt dev. -- [ ] `https://dev.ibo.momentrum.de` → Redirect zu `auth.momentrum.de`, nach - Login landet man in der App. -- [ ] In der App eingebettetes `/novnc/` zeigt den Live-Browser. -- [ ] `https://ibo.momentrum.de` nach Promote ebenso hinter Zitadel. +- [ ] PR + Label `preview` → CI gruen (version→test→build-push→helm-validate), + ArgoCD legt `ibo-pr-` an, `https://ibo-pr-.dev.momentrum.de` + redirected zu `auth.momentrum.de`, nach Login landet man in der App. +- [ ] Im Preview eingebettetes `/novnc/` zeigt den Live-Browser. +- [ ] PR schliessen → Preview + Namespace verschwinden (ArgoCD prune). +- [ ] main-Merge → CI-Job `deploy` bumpt `app.yaml`, ArgoCD rollt prod, + `https://ibo.momentrum.de` hinter Zitadel erreichbar. - [ ] Ohne SSO-Session → Login-Redirect (kein rohes 401). diff --git a/infra/k8s/base/ibo/templates/_helpers.tpl b/infra/k8s/base/ibo/templates/_helpers.tpl index 1039bbf..32ca95b 100644 --- a/infra/k8s/base/ibo/templates/_helpers.tpl +++ b/infra/k8s/base/ibo/templates/_helpers.tpl @@ -15,12 +15,19 @@ app.kubernetes.io/instance: {{ .Release.Name }} {{- /* Middleware-Kette fuer die Ingress-Annotation: erst die SSO-Middlewares aus dem -CD-Repo (errors VOR auth), dann die same-namespace stripPrefix fuer /novnc. -Traefik-Ingress-Refs sind "-@kubernetescrd". +CD-Repo (errors VOR auth), dann die stripPrefix fuer /novnc. Traefik-Ingress-Refs +sind "-@kubernetescrd". +Die stripPrefix-Middleware ist normalerweise die vom Chart im Release-Namespace +angelegte; ist novncStripPrefixRef gesetzt (Previews), wird stattdessen diese +EXTERNE (geteilte) Middleware referenziert und das Chart legt keine eigene an. */ -}} {{- define "ibo.middlewares" -}} {{- $mw := list -}} {{- if .Values.ingress.ssoMiddlewares -}}{{- $mw = append $mw .Values.ingress.ssoMiddlewares -}}{{- end -}} +{{- if .Values.ingress.novncStripPrefixRef -}} +{{- $mw = append $mw .Values.ingress.novncStripPrefixRef -}} +{{- else -}} {{- $mw = append $mw (printf "%s-%s-novnc@kubernetescrd" .Release.Namespace (include "ibo.name" .)) -}} +{{- end -}} {{- join "," $mw -}} {{- end -}} diff --git a/infra/k8s/base/ibo/templates/deployment.yaml b/infra/k8s/base/ibo/templates/deployment.yaml index 3d4f2f0..76a8c84 100644 --- a/infra/k8s/base/ibo/templates/deployment.yaml +++ b/infra/k8s/base/ibo/templates/deployment.yaml @@ -17,6 +17,9 @@ spec: labels: {{- include "ibo.selectorLabels" . | nindent 8 }} spec: + {{- with .Values.priorityClassName }} + priorityClassName: {{ . }} + {{- end }} containers: # --- Flask-Web-App --------------------------------------------------- - name: app diff --git a/infra/k8s/base/ibo/templates/middleware.yaml b/infra/k8s/base/ibo/templates/middleware.yaml index 79a144b..e032b55 100644 --- a/infra/k8s/base/ibo/templates/middleware.yaml +++ b/infra/k8s/base/ibo/templates/middleware.yaml @@ -1,8 +1,11 @@ -{{- if .Values.ingress.enabled }} +{{- if and .Values.ingress.enabled (not .Values.ingress.novncStripPrefixRef) }} # Strippt den /novnc-Prefix, bevor der Request an den Selenium-noVNC (7900) geht: # der Ingress-Pfad /novnc/vnc.html -> selenium sieht /vnc.html, /novnc/websockify # -> /websockify. Same-namespace, wird in _helpers.tpl an die Middleware-Kette # der Ingress-Annotation angehaengt. +# NUR wenn ingress.novncStripPrefixRef leer ist — Previews referenzieren eine +# geteilte externe Middleware (iam-strip-novnc), weil das "previews"-AppProject +# das Anlegen von traefik.io/Middleware im Preview-ns blockt. apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: diff --git a/infra/k8s/base/ibo/values.yaml b/infra/k8s/base/ibo/values.yaml index f8d3412..7654db9 100644 --- a/infra/k8s/base/ibo/values.yaml +++ b/infra/k8s/base/ibo/values.yaml @@ -12,6 +12,11 @@ image: # offener Consent-Browser) — mehrere Replicas wuerden ihn nicht teilen. replicaCount: 1 +# Previews setzen "preview" (niedrige PriorityClass, definiert in contabo-infra): +# unter Memory-Pressure auf der 12-GB-Box sterben Preview-Pods zuerst, nie Prod. +# Prod laesst das leer. +priorityClassName: "" + service: # Nach aussen (ClusterIP) sichtbare Ports; targetPorts sind die Container-Ports. appPort: 80 @@ -41,12 +46,18 @@ ingress: secretName: ibo-tls annotations: cert-manager.io/cluster-issuer: letsencrypt-prod - # Zitadel-SSO davor (oauth2-proxy "ibo-sso" in contabo-infra). Reihenfolge: - # errors VOR auth, damit 401 zum Login-Redirect wird. Die stripPrefix-Middleware - # fuer /novnc haengt das Chart selbst hinten an (siehe _helpers.tpl). - # Vom CD-Repo gesetzt, z.B.: - # "iam-oauth2-ibo-errors@kubernetescrd,iam-oauth2-ibo-auth@kubernetescrd" + # Zitadel-SSO davor. Reihenfolge: errors VOR auth, damit 401 zum Login-Redirect + # wird. Die stripPrefix-Middleware fuer /novnc haengt das Chart hinten an + # (siehe _helpers.tpl). Vom CD-Repo gesetzt: + # prod -> "iam-oauth2-ibo-errors@kubernetescrd,iam-oauth2-ibo-auth@kubernetescrd" (per-App ibo-sso) + # preview -> "iam-oauth2-errors@kubernetescrd,iam-oauth2-auth@kubernetescrd" (generisches dev-sso) ssoMiddlewares: "" + # Referenz auf eine EXTERNE /novnc-stripPrefix-Middleware ("-@kubernetescrd"). + # Leer = das Chart legt die Middleware selbst im Release-Namespace an (Prod/Default). + # Gesetzt = Chart legt KEINE eigene an, haengt stattdessen diese Referenz an die Kette. + # PR-Previews MUESSEN das setzen: das ArgoCD-"previews"-Project blockt das Anlegen von + # traefik.io/Middleware im Preview-ns (Anti-Auth-Faelschung) -> geteilte iam-strip-novnc. + novncStripPrefixRef: "" resources: app: