Files
infra/stacks/keycloak/README.md
znetsixe af354a4b9e feat(cloud): short function-based subdomains + harden keycloak with postgres
Subdomain rename (Versio side keeps original tool-named hostnames)
- nginx vhosts updated:
    grafana   -> dash.wbd-rd.nl
    gitea     -> git.wbd-rd.nl
    keycloak  -> auth.wbd-rd.nl
    node-red  -> flow.wbd-rd.nl
    mlflow    -> ml.wbd-rd.nl
    jupyter   -> hub.wbd-rd.nl
    portainer -> ops.wbd-rd.nl
    rabbitmq  -> mq.wbd-rd.nl
    jenkins   -> ci.wbd-rd.nl
    mqtt      -> mqtt.wbd-rd.nl   (no Versio conflict assumed)
- nginx-proxy README: bootstrap cert -d list + DNS A-record prereqs updated
- cloud/.env.example: GITEA_ROOT_URL, GRAFANA_ROOT_URL, KEYCLOAK_HOSTNAME

Function-based names are tool-agnostic (a Grafana -> Kibana swap leaves
dash.wbd-rd.nl meaningful) and avoid one-off "*2" suffixes.

Keycloak hardening
- Switch backend from bundled file storage to postgres (keycloak DB
  already provisioned by sql/config/init.d/01-databases.sh).
- KC_HOSTNAME=auth.wbd-rd.nl, KC_PROXY_HEADERS=xforwarded for nginx
  reverse-proxy posture; KC_HTTP_ENABLED=true since nginx terminates TLS.
- Added KC_HOSTNAME_STRICT, KC_HEALTH_ENABLED, KC_METRICS_ENABLED.
- Service joins app + mgmt + data networks (data needed for postgres).
- Mounted config/realms/ for realm-as-code (kc.sh import) — TODO to
  populate once realm + clients are designed.
- README documents the recommended realm structure (wbd realm, one
  client per app with redirect URIs) and the oauth2-proxy approach
  for apps without native OIDC (mlflow, portainer-CE).

cloud
- Uncomment keycloak include in cloud/compose.yml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 13:54:57 +02:00

3.0 KiB

keycloak

Identity provider for SSO across all R&D services. Cloud-only for now (edges get their own realms once we cover the edge layer).

  • Public hostname: auth.wbd-rd.nl (reverse-proxied via nginx-proxy at HTTPS → backend HTTP 8080)
  • Networks: app (OIDC endpoints for relying-party apps) + mgmt (admin console) + data (postgres backend)
  • Backend: postgres keycloak database in sql stack — provisioned automatically by sql/config/init.d/01-databases.sh on first start
  • Volume: keycloak-data (themes, providers, realm exports)

First-run bootstrap

KC_BOOTSTRAP_ADMIN_USERNAME + KC_BOOTSTRAP_ADMIN_PASSWORD create the master-realm admin on first start. Change the password immediately after first login via the admin console.

After deployment:

# 1. Bring it up (sql must be running first)
cd /mnt/d/gitea/RnD/infra/cloud
docker compose up -d sql            # if not already up
docker compose up -d keycloak

# 2. Watch logs until you see "Keycloak <version> on JVM started"
docker compose logs -f keycloak

# 3. Browse  https://auth.wbd-rd.nl/  → admin console
#    (until cert is bootstrapped, https://<cloud-host>:9443 portainer can show logs)

Realm + clients (TODO — design before deploy day 2)

Recommended structure: one realm wbd containing all R&D apps as separate clients.

Client ID App Redirect URI Flow
grafana Grafana https://dash.wbd-rd.nl/login/generic_oauth code
gitea Gitea https://git.wbd-rd.nl/user/oauth2/keycloak/callback code
node-red Node-RED https://flow.wbd-rd.nl/auth/strategy/callback/ code
jenkins Jenkins https://ci.wbd-rd.nl/securityRealm/finishLogin code
jupyterhub JupyterHub https://hub.wbd-rd.nl/hub/oauth_callback code
mlflow MLflow (via oauth2-proxy) https://ml.wbd-rd.nl/oauth2/callback code
portainer-ce Portainer (via oauth2-proxy) https://ops.wbd-rd.nl/oauth2/callback code

Apps without native OIDC (mlflow, portainer-CE) sit behind an oauth2-proxy sidecar that nginx auth_requests to. That's a TODO stack we'll add when we wire up mlflow / portainer SSO.

Realm-as-code

Drop exported realm JSON into config/realms/. On first start, Keycloak imports anything in /opt/keycloak/data/import/ if KC_IMPORT_REALM_DIR is set or if you run kc.sh import manually. Recommended workflow:

  1. Configure the realm by hand once in the UI
  2. docker compose exec keycloak /opt/keycloak/bin/kc.sh export --dir /opt/keycloak/data/import --realm wbd
  3. Commit the exported file under stacks/keycloak/config/realms/
  4. Subsequent fresh deploys auto-import

TODO

  • Realm bootstrap script (provision wbd realm + clients above)
  • Theme: WBD branding (logo, colors)
  • User federation (LDAP from corporate AD, if applicable)
  • 2FA policy
  • Session / token lifetimes per client
  • oauth2-proxy stack for apps without native OIDC
  • Realm export → config/realms/wbd.json committed