Skip to content

Latest commit

 

History

History
65 lines (51 loc) · 5.88 KB

File metadata and controls

65 lines (51 loc) · 5.88 KB

Session Memo

2026-08-20 — Plane v1.2.0 → v1.4.1 + Gitea SSO end-to-end (DONE)

Outcome

  • Plane upgraded to v1.4.1; Gitea SSO login works at https://plane.ebruno.fr/ (button only exists in ≥1.4.0 frontends — v1.2.0 sign-in page had no gitea option).
  • App plane Synced/Healthy at chart plane-ce targetRevision 1.6.1 (see trap below).
  • Deleted orphaned PVC data-plane-postgresql-0 (ns plane, 41d, pre-CNPG postgres; data already migrated + dumped).

What broke and how it was fixed (Gitea 1.26 legacy-row migration gaps)

Gitea was upgraded to 1.26.4 while its Postgres (ns gitea, pod gitea-postgresql-0, user/db gitea) kept rows from older Gitea. 1.26 changed two column semantics without a data migration:

  1. oauth2_application.redirect_uris is now a JSON column (xorm JSON TEXT). Plane row (id=6) was plain text → 500 on authorize: GetOAuth2ApplicationByClientID: invalid character 'h'. Backstage row (id=4) was [url] unquoted → same latent break. Fixed: wrapped values in proper JSON arrays.
  2. oauth2_application.client_secret is validated with bcrypt (ValidateClientSecretbcrypt.CompareHashAndPassword). Plane row held plaintext (32-hex) → token exchange 400 unauthorized_client: invalid client secret → Plane ?error_code=5123&error_message=GITEA_OAUTH_PROVIDER_ERROR loop. Fixed: stored bcrypt hash of the existing secret (value from k8s secret plane-gitea-oauth, ns planeno secret rotation).
  • Healthy 1.26 row for comparison: woodpecker (id=5) has $2b$10$… + JSON-array redirect_uris.
  • If Gitea DB is ever restored/rebuilt, re-apply both fixes.

ArgoCD / chart traps hit during the upgrade

  • Plane chart migrator Job: plane-api-migrate-{{.Release.Revision}}; ArgoCD helm render pins Revision=1 → on version bump the completed Job is immutable → sync op fails → api loops on wait_for_migrations. Fix: delete stale Job (one-off kubectl) and change targetRevision (ArgoCD v3.5.1 memoizes failed syncs per chart revision and never auto-retries the same one).
  • ArgoCD admin session REST API unusable here (password login disabled); argocd-server svc REST is port 80→8080 (probe: POST /api/v1/session 401 "no credentials supplied").
  • Chart plane-ce 1.6.1 vs 1.6.2: templates are almost identical, but helm.sh/chart + app.kubernetes.io/version labels are rendered from the chart version into every pod/job spec → bumping chart version alone = full rollout + migrator-Job immutability failure with zero functional gain (image is pinned in values). 1.6.2 tried 2026-08-20, reverted; 1.6.1 is the settled state — do not bump the chart version.
  • Plane v1.4.1 GET /api/instances/ (unauthenticated) → nested shape {config: {is_gitea_enabled, …}, instance: {current_version, latest_version, …}}.
  • Plane CE 1.4.1 API tokens: X-Api-Key header, /api/v1/ surface only (/api/users/me/ is session-only → 401). Verified: /api/v1/users/me/ → 200.

Rollback / open items (plane-only)

  • Cutover dump /tmp/opencode/plane-cutover.dump deleted 2026-08-20 — schema v1.4.1 is now forward-only (no local rollback path).
  • Woodpecker CI wiring — done 2026-08-20: token in Vault secret/data/plane (apiToken) → ExternalSecret plane-api-token (ns ci, new kubernetes/ci/ dir added to appset-manifests) → verify-plane step in .woodpecker-ci/ci.yaml (token auth + version drift guard PLANE_EXPECTED_VERSION=1.4.1). Remaining: user must add Woodpecker secret plane_api_token in the Woodpecker UI (repo settings → secrets) — not headless (Gitea-OAuth login). Also fixed pre-existing invalid YAML in validate-realm step (multi-line plain scalar → | literal block; was making the whole pipeline unparseable).

2026-07-23 — Traefik Cross-Namespace Auth Proxy

Date Updated

2026-07-23

Current State

  • Root cause identified: Traefik blocks cross-namespace service references. Services in nexus and sonarqube namespaces cannot route to oauth2-proxy in the oauth2-proxy namespace.
  • Solution: Create a local Envoy proxy Deployment in each app namespace (nexus, sonarqube) that forwards to the real oauth2-proxy service via kube-dns resolution. This sidesteps Traefik's cross-namespace restriction.

Files Modified

  • kubernetes/ingress/nexus-ingress.yaml — Uses oauth2-proxy-public local Service; fwd-auth middleware calls /oauth2/start?rd=
  • kubernetes/ingress/sonarqube-ingress.yaml — Updated to use oauth2-proxy-public local Service
  • kubernetes/ingress/sonarqube-middleware.yaml — Updated fwd-auth to use /oauth2/start?rd=https://sonarqube.ebruno.fr/
  • kubernetes/ingress/nexus-oauth2-ext-svc.yaml — Envoy proxy Deployment + Service + ConfigMap
  • kubernetes/ingress/sonarqube-oauth2-ext-svc.yaml — Envoy proxy Deployment + Service + ConfigMap
  • kubernetes/oauth2-proxy/oauth2-proxy.yaml — Removed duplicate --cookie-secure and --redirect-url flags

Still Needs Attention

  • PactBroker middleware (kubernetes/ingress/pact-broker-middleware.yaml) still uses /oauth2/auth — update to /oauth2/start
  • The oauth2-proxy ingress file (kubernetes/oauth2-proxy/oauth2proxy-ingress.yaml) is unused (no DNS record)
  • Clean up unused ScaffoldStripprefix middleware files

Testing Status

  • curl https://nexus.ebruno.fr/oauth2/start → 302 to Keycloak + CSRF cookie ✅
  • curl https://sonarqube.ebruno.fr/oauth2/start → 302 to Keycloak + CSRF cookie ✅
  • Full browser flow untested — pending manual verification

Architecture

Browser → Traefik (IngressRoute in app namespace) → Envoy Proxy (local, same namespace) → oauth2-proxy (oauth2-proxy namespace, via kube-dns) → Keycloak

Each app namespace gets its own Envoy sidecar:

  • Nexus: nexus-oauth2-ext-svc.yaml
  • SonarQube: sonarqube-oauth2-ext-svc.yaml

Traefik only sees a same-namespace Service (oauth2-proxy-public), satisfying its routing constraints.