84cea6267eb7f977ef4409f404cc6b48de4fdf9f
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0074a1bff3 |
feat(k8s): optionally auto-fill the medewerker OTP step for the public demo (refs #177) (#181)
CI / k8s (push) Successful in 8s
CI / build (push) Successful in 1m38s
CI / lint (push) Successful in 1m55s
CI / mutation (push) Canceled after 0s
CI / verify-stack (push) Canceled after 0s
CI / frontend (push) Canceled after 12s
CI / unit (push) Canceled after 18s
Deploy to Talos / deploy (push) Successful in 2m30s
## What & why For the public demo on `big-behandel` / `big-beheer`, visitors should see MFA being enforced without needing an authenticator app. This adds an opt-in Keycloak theme that fills in and submits the medewerker OTP code itself. - **Theme as real files in `infra/keycloak/themes/big-demo/`**, next to the realms: - `login/theme.properties`: `keycloak.v2` plus `scripts=js/otp-autofill.js`. I checked the 26.1 source: `keycloak.v2` loads theme `scripts` and sets none of its own. - `login/resources/js/otp-autofill.js`: on the OTP page, computes the code (RFC 6238, Keycloak's default policy) from the fixture secret `BIGMEDEWERKEROTPSEED` and submits it. - `account`, `admin`, `email`: plain children of Keycloak 26's defaults. Without them the account console returns 500 (see notes). - **Seeded like every other file input:** `infra/helm/seed-configmaps.sh` creates the `rr-kc-theme` ConfigMap, and the chart mounts it as a directory. The podspec gains `items` so flat ConfigMap keys map to theme paths. Keycloak runs `start-dev` (no theme cache), so edits show up about a minute after a reseed. - **Switch:** `demo.otpAutofill` only decides whether `KC_SPI_THEME_DEFAULT=big-demo` is set. `big.env` now skips env values that render empty, and no existing env var is empty. **Off, the render is identical to main except for that one missing variable,** so Keycloak keeps its stock theme. The realm JSONs are untouched, so compose and the e2e tests still require a code. - **Single-use codes:** a second login in the same 30 s window spends the next counter, as `nextUnusedCounter` does in the e2e. Past that it only fills in the field and doesn't submit, so a rejected code can't loop. - **Deploy workflow:** repo variable `OTP_AUTOFILL=true` → `--set demo.otpAutofill=true`. Flipping it changes the pod's env, so Keycloak restarts. Refs #177 ## Definition of Done - [x] Linked Gitea issue (above). - [ ] Failing test committed before the implementation. *(Not done; checks below.)* - [x] Conventional Commits referencing the issue (`refs #NN`). - [ ] CI green - [x] `docker compose up` unaffected (chart only). - [x] Docs updated (Talos runbook, "Publishing through the labs Caddy"). - [ ] ADR. The fixture-secret trade-off is ADR-0031's; this only automates typing it in. ## Notes for reviewers - **Tested on the live cluster.** I patched the running Keycloak with the rendered theme (autofill on) and ran real headless Chromium logins against the public hosts: - `merel-behandelaar` on big-behandel: only username and password typed. The OTP page loaded the script, submitted by itself, and the user landed in the Werkbak. - `jan-burger` on big-mijn still logs in (regression check). - `/realms/medewerker/account/` returns 200. - **Account console 500, found live and fixed in the second commit.** `KC_SPI_THEME_DEFAULT` applies to every theme type, and Keycloak does *not* fall back for a type the theme lacks (`NullPointerException ... "theme" is null`). `big-demo` now declares login, account, admin and email, each a plain child of Keycloak 26's default. It's one ConfigMap mounted as a directory; the podspec gains `items` for that. - **Keycloak restarts cause about 5 minutes of BFF 401s.** This is not caused by this PR, but you'll see it whenever Keycloak restarts. Dev-mode Keycloak makes new signing keys on each boot, and the BFF refreshes its cached keys at most every 5 minutes. Seen live: 401 right after the restart, 204 about 4½ minutes later. Flipping `OTP_AUTOFILL` restarts Keycloak, so expect this briefly. - `make k8s-lint` and `make k8s-drift` pass. The rendered script's code matches `infra/keycloak/check_realms.py otp`. - **Security:** with it on, the public behandel and beheer portals are protected only by the committed password `test123`. That's intentional for synthetic demo data. Never enable it anywhere real. 🤖 Generated with [Claude Code](https://claude.com/claude-code)Reviewed-on: #181 |
||
|
|
804031eeb8 |
feat(k8s): publish the portals through the labs Caddy (refs #177) (#179)
CI / k8s (push) Successful in 14s
CI / build (push) Successful in 2m2s
CI / lint (push) Successful in 2m32s
CI / unit (push) Successful in 1m36s
CI / frontend (push) Successful in 3m3s
Deploy to Talos / deploy (push) Successful in 3m39s
CI / mutation (push) Successful in 5m31s
CI / verify-stack (push) Failing after 17m26s
## What & why Makes the portals reachable on real hostnames through the Caddy that already fronts `*.labs.respellion.tech`, instead of five SSH port-forwards: | URL | Service | |---|---| | `https://big-register.labs.respellion.tech` | openbaar | | `https://big-mijn.labs.respellion.tech` | self-service | | `https://big-behandel.labs.respellion.tech` | behandel | | `https://big-beheer.labs.respellion.tech` | beheer | | `https://big-auth.labs.respellion.tech` | Keycloak (`/admin` blocked) | Chain: browser → labs Caddy (TLS) → `openssh-server` container → reverse SSH tunnel → Fedora host → Talos NodePorts. The Caddy routes and the tunnel unit are already on `main` in the Infra repo (`infra/development/`). This repo's part: - **Chart:** a `keycloakUrl` value. When set it replaces `host` + Keycloak's NodePort as the pinned issuer (`KC_HOSTNAME`) and the portals' OIDC authority. Both now come from one helper, `big.keycloakUrl`, so they can't drift apart (ADR-0010). Empty = rendered output identical to today. - **Deploy workflow:** passes the `KEYCLOAK_URL` repo variable as `--set keycloakUrl=…`. - **Runbook:** new section "Publishing through the labs Caddy". Refs #177 ## Definition of Done - [x] Linked Gitea issue (above). - [ ] Failing test committed before the implementation. *(Infra/config change, no test added.)* - [x] Implementation makes the test pass; refactor commit if structure improved. - [x] Conventional Commits referencing the issue (`refs #NN`). - [ ] CI green — all Gitea Actions jobs (or `make ci` green while no runner exists). - [x] `docker compose up` from a fresh clone reaches green health checks within 3 minutes. *(Compose untouched.)* - [x] Docs updated if behaviour, contracts, or operations changed. - [ ] ADR added in `docs/architecture/` if a non-obvious decision was made. - [ ] Demo note in `docs/demo-script.md` if user-visible. ## Notes for reviewers - **This takes the option #177 rejects.** #177 proposes an in-cluster Caddy edge (branch `feat/177-public-tls-edge`). This PR uses the existing labs Caddy instead, because it already holds 80/443 and the wildcard certificate. So it only *refs* #177. If we go this way, #177's ADR should record the host-Caddy option instead. - `make k8s-lint` and `infra/check-docs-nav.py` pass. I rendered the chart with and without `keycloakUrl`: empty gives the same output as before; set, it gives `https://big-auth.labs.respellion.tech` for both the issuer and the authority. - Once `KEYCLOAK_URL` is set, the `localhost` port-forward workflow (runbook §5) no longer logs in, because the issuer is a single string. - The portals are public, with no Azure `authorize` in front of them the way `marketing` has one. The test users use `test123`. - Rollout after merge: install `big-portals-tunnel.service` on the Fedora host, run `docker compose up -d caddy` on the labs server, then set the `KEYCLOAK_URL` variable. 🤖 Generated with [Claude Code](https://claude.com/claude-code)Reviewed-on: #179 |
||
|
|
6cfcc4cf83 |
ci(deploy): deploy the stack to Talos on merge to main (closes #175) (#176)
CI / k8s (push) Successful in 6s
CI / unit (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / mutation (push) Canceled after 0s
CI / verify-stack (push) Canceled after 0s
CI / build (push) Canceled after 9s
CI / lint (push) Canceled after 24s
Deploy to Talos / deploy (push) Successful in 5m1s
## What & why The chart has been deployable by hand since #25 and linted in CI since #168. This makes a merged PR actually ship it to the Talos VM on the lab server. `.gitea/workflows/deploy.yaml` runs on a push to `main` (a squash-merged PR) and on manual dispatch: 1. **Tunnel** — neither the Kubernetes API nor the in-cluster registry is publicly reachable, so 6443, 30500 and 30141 are forwarded over the same SSH hop into the Fedora host that the Gitea-runner pipeline uses (`ssh -p 6667 user@labs.respellion.tech`). 2. **Images** — `make k8s-images K8S_REGISTRY=localhost:30500`, pushed *through* the tunnel. 3. **Deploy** — `make k8s-reseed TALOS_HOST=… K8S_REGISTRY=<vm-ip>:30500`, pulled by the node from its own NodePort. 4. **Roll** — `rollout restart` + `rollout status` on the nine repo deployments. 5. **Smoke** — `GET /openbaar/register` through the openbaar portal. Three decisions worth the review: - **One registry, two names.** The push target (`localhost:30500`, the tunnel) and the pull target (`<vm-ip>:30500`, the node's own NodePort) address the same store. The pull name has to be the one in the node's registry-mirror patch, which is what makes plain HTTP acceptable. - **`k8s-reseed`, not `k8s-up`.** A Job's pod template is immutable, so a chart change to any bootstrap Job would otherwise fail the upgrade with `cannot patch … with kind Job`. The Jobs are idempotent by design, so re-running them every deploy is safe and removes that whole class of failure. Cost: a few minutes per deploy, and `seed-zaaktype` needs egress from the VM. - **No re-run of the checks.** PR CI is the merge gate, so `main` is green by construction. Deploys **queue** (`cancel-in-progress: false`) — a `helm upgrade` killed half-way leaves the release in `pending-upgrade` and has to be unwedged by hand. Settings on the repo (already added): secrets `TALOS_SSH_KEY` and `TALOS_KUBECONFIG` (base64, and its `server:` must be `https://127.0.0.1:6443` — Talos puts `127.0.0.1` in the apiserver cert SANs, so TLS still verifies through the tunnel); variables `TALOS_VM_IP` (default `192.168.122.173`) and `TALOS_HOST` (default `localhost`). Closes #175 ## Definition of Done - [x] Linked Gitea issue (above). - [ ] Failing test committed before the implementation — **n/a**: this is a deployment workflow with no unit under test. Its check is the run itself: `rollout status` and the public-register smoke both have to pass or the job fails. `make k8s-lint` / `make k8s-drift` (#168) already gate the chart it deploys. - [x] Implementation — one workflow file, no production code touched. - [x] Conventional Commits referencing the issue (`refs #175`). - [ ] CI green — awaiting the run on this PR. - [x] `docker compose up` unaffected — no service, image or compose file is touched. - [x] Docs updated — `docs/runbooks/kubernetes-talos.md` §9 (the tunnel, the two registry names, the secrets table, the smoke) and a pointer from `docs/runbooks/ci.md`. - [x] No ADR needed: no new dependency (kubectl/helm/crane are already prerequisites of the `k8s-*` targets), no service boundary moved, no CLAUDE.md §8 rule bent. - [ ] Demo note — not user-visible. ## Notes for reviewers - **The first deploy is the real test.** It cannot be dry-run: the tunnel, the secrets and the registry only exist on the lab server. Merging is how we find out; `Pods on failure` dumps `get pods,jobs` if it doesn't. - **Known gap — the portals still aren't browsable.** PKCE needs a secure context, so a NodePort on an IP can't serve them (runbook §5); they need `make k8s-portals` or an SSH forward. Giving the server a hostname + TLS is the follow-up, and is where `TALOS_HOST` stops defaulting to `localhost`. - **Databases are `emptyDir`.** Any change to a database pod's template wipes it; the `k8s-reseed` in the deploy re-runs the bootstrap, so the stack recovers, but submitted registrations do not. Persistence is runbook §6. 🤖 Generated with [Claude Code](https://claude.com/claude-code)Reviewed-on: #176 |