Files
register-referentie/infra/helm/seed-configmaps.sh
T
not 0074a1bff3
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
feat(k8s): optionally auto-fill the medewerker OTP step for the public demo (refs #177) (#181)
## 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
2026-09-25 11:34:30 +00:00

56 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Turn the repo's config inputs into the ConfigMaps the Helm chart mounts.
#
# This is the Kubernetes sibling of infra/seed-config.sh: the upstream Common
# Ground images are used verbatim and read their config from a mounted directory,
# so the config has to be handed to the platform out-of-band. Compose gets it via
# `docker cp` into external volumes; Kubernetes gets it as ConfigMaps created from
# the files that already live in this repo. Copying those files into the chart
# would fork them from the compose stack, so we don't.
#
# Idempotent: re-run after editing any data.yaml, then `make k8s-reseed`.
#
# Usage: seed-configmaps.sh [namespace] (default: big)
set -euo pipefail
ns="${1:-big}"
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo="$(cd "$here/../.." && pwd)"
kubectl get namespace "$ns" >/dev/null 2>&1 || kubectl create namespace "$ns"
seed() { # name <kubectl --from-file args...>
local name="$1"; shift
kubectl create configmap "$name" -n "$ns" "$@" \
--dry-run=client -o yaml | kubectl apply -f - >/dev/null
echo " seeded configmap/$name"
}
seed rr-oz-config --from-file="$repo/infra/openzaak/setup_configuration/"
seed rr-nrc-config --from-file="$repo/infra/opennotificaties/setup_configuration/"
seed rr-kc-realms --from-file="$repo/infra/keycloak/realms/"
# The big-demo login theme (demo.otpAutofill). ConfigMap keys are flat, so each
# file gets a key here and its path back in the keycloak `files` in values.yaml.
theme="$repo/infra/keycloak/themes/big-demo"
seed rr-kc-theme \
--from-file=login.properties="$theme/login/theme.properties" \
--from-file=otp-autofill.js="$theme/login/resources/js/otp-autofill.js" \
--from-file=account.properties="$theme/account/theme.properties" \
--from-file=admin.properties="$theme/admin/theme.properties" \
--from-file=email.properties="$theme/email/theme.properties"
seed rr-objecttypen-config --from-file="$repo/infra/objecttypen/setup_configuration/"
seed rr-objecten-config --from-file="$repo/infra/objecten/setup_configuration/"
# register.py + the RegisterRecord JSON schema (the __pycache__ dir is skipped:
# kubectl only takes regular files from a --from-file directory).
seed rr-registerrecord-config --from-file="$repo/infra/objecttypen-registerrecord/"
# The BPMN and the DMN are two separate Flowable deployments (S-13, ADR-0016).
seed rr-fl-bpmn \
--from-file="$repo/workflows/registratie.bpmn" \
--from-file="$repo/workflows/diploma-eligibility.dmn"
# The two bootstrap scripts the compose local stack runs as init containers
# (S-B04, ADR-0020). Stdlib-only, so a plain python image can run them.
seed rr-seed-scripts \
--from-file="$repo/infra/openzaak/seed_catalogus.py" \
--from-file="$repo/infra/local/register-abonnement.py"