Skip to main content

Keycloak (per region)

Keycloak admin console password (per-region)

The Bitnami Keycloak chart reads its admin console password from a Kubernetes Secret (keycloak-admin-secret), materialized by ExternalSecrets from {prefix}/admin/keycloak in AWS Secrets Manager. Normally Terraform generates this (random_password, gated behind create_keycloak_admin_secret in infra/region-deployments/{env}/main.tf) before the first tofu apply for the region.

If Keycloak needs to come up before that Terraform flag has been flipped and applied, create the secret by hand instead, in the same shape Terraform would have written:

PASS=$(openssl rand -base64 24 | tr -d '/+=' | head -c 24)
jq -n --arg password "$PASS" '{password: $password}' | aws secretsmanager create-secret \
--name cogrion-{region}-sgp/admin/keycloak \
--description "Keycloak admin console password" \
--profile cogrion-{region}-sgp \
--region ap-southeast-1 \
--secret-string file:///dev/stdin

Point the region's keycloak-admin-secret ExternalSecret (argocd/apps/{cluster}/external-secrets.values.yaml) at this same key rather than a separate bootstrap secret, so the chart's console password and cplane's KEYCLOAK_ADMIN_PASSWORD never diverge. Once Terraform's flag is flipped and applied for real, either import this secret into Terraform state or rotate it and let Terraform take ownership going forward.

Create a permanent Keycloak admin account

1. Create the New User

  1. Log into your Keycloak Admin Console using your current temporary bootstrap credentials.
  2. Ensure you are in the master realm (check the dropdown menu in the top-left corner).
  3. Click Users in the left sidebar navigation.
  4. Click Create new user.
  5. Enter a Username (e.g., kcadmin) and fill in any other desired details.
  6. Click Create.

2. Assign the Admin Role

  1. Inside the new user's management page, navigate to the Role mapping tab.
  2. Click Assign role.
  3. Change the filter dropdown from Filter by clients to Filter by realm roles.
  4. Select the admin role from the list.
  5. Click Assign.

3. Set a Permanent Password

  1. Switch to the Credentials tab for that user.
  2. Click Set password.
  3. Enter your secure password as we see in Secrets Manager.
  4. Turn off the Temporary toggle switch so it is disabled.
  5. Click Save to confirm.

4. Clean Up Temporary Accounts

  1. Log out of your temporary admin session.
  2. Log back into the console using your newly created permanent admin credentials.
  3. Go back to Users in the master realm.
  4. Locate the temporary bootstrap user account and Delete it to clear the security warning banner.

Sources:

  1. YouTube — Keycloak admin setup
  2. Server Fault — Permanent admin account in Keycloak 26.0.0
  3. YouTube — Keycloak walkthrough
  4. GitHub Discussion #33803
  5. Stack Overflow — Create admin user in a realm
  6. Eggplant Docs — New user
  7. Cloudron Forum — Add a permanent admin
  8. HPE Support Docs
  9. GitHub Issue #34768

Keycloak realm + CA

control-plane/server/src/seeds/seed.keycloak.ts declaratively seeds the Keycloak realm cplane depends on — idempotent, safe to re-run. The realm is named cogrion (REALM_NAME/DISPLAY_NAME = cogrion/Cogrion), and its public clients (cogrion, controlplane) are the successors to an earlier saas-ux client that no longer exists.

Running the seed via the cplane CLI pod

TODO

Each Keycloak client's directAccessGrantsEnabled (ROPC) flag is set per-client rather than hardcoded true platform-wide: backend/service clients keep it enabled, but the public cogrion/controlplane clients set it false and get PKCE (S256) enforced instead — a scripted password-grant request can no longer mint a platform_admin-scoped token with no browser, no PKCE, and no MFA (previously CWE-287).

Open gap: the realm still needs seeding in dev-sgp. seed.keycloak.ts is correct, but running it against dev-sgp's live Keycloak — creating the cogrion realm, its clients, roles, and admin user — hasn't happened yet. The script's SMTP configuration will need to be skipped or adapted, pending the M365 decision in sparqd/project-management#276.

Superseded — manual Admin Console UI checklist (not used, kept for reference)

Login: https://sso.sgp.dev.cogrion.com (dev-sgp — pattern is sso.{region}.{env}.cogrion.com), admin credentials from the keycloak-admin-secret Kubernetes Secret (materialized via ESO from the untracked cogrion-dev-sgp/keycloak-admin AWS secret — see cogrion-terraform#78).

  • Create a new realm, name cogrion, display name "Cogrion"
  • SMTP — skip, blocked on M365 (#276)
  • Create account-level clients (controlplane, saas-ux), both public
  • Create the realm roles listed in declarative-realm-template.ts's realmRoles
  • Create an admin user, assign platform_admin, grant realm-admin rights

OpenBao ↔ Keycloak OIDC trust (per-region)

Every per-customer OpenBao namespace gets a jwt auth mount bound to that customer's Keycloak realm (OpenBaoClient.createNamespace, server/src/clients/openbao.client.ts), and every tenant cluster gets a separate jwt-cluster mount bound to that cluster's own OIDC issuer (OpenBaoClient.createClusterAuthMount). Configuring either mount makes OpenBao itself fetch the issuer's .well-known/openid-configuration document — so OpenBao, not control-plane, is the TLS client that needs to trust Keycloak's (or the tenant cluster's) cert.

On every region today, both issuers use publicly-trusted certs (Keycloak sits behind the same ALB as the rest of Cogrion's services, ACM-issued), so OpenBao's own system trust store validates them with no extra configuration — oidc_discovery_ca_pem should simply be omitted from the POST /auth/{mount}/config call.

Gotcha, already hit once (prod-sgp): it's tempting to reuse config.vault.caBundlePath (the CA bundle described in Trusting OpenBao's cert from control-plane, used for control-plane's own trust of OpenBao) as the value for oidc_discovery_ca_pem too — same env var, conveniently already plumbed through. Don't: that bundle holds OpenBao's own cert, which has nothing to do with Keycloak's or a tenant cluster's cert. Passing it as oidc_discovery_ca_pem makes OpenBao pin exclusively to that CA instead of falling back to its system trust store, so it rejects Keycloak's real (publicly-trusted) cert outright — error checking oidc discovery URL: ... x509: certificate signed by unknown authority in OpenBao's own logs, surfacing as a 400 on provisionOpenBaoNamespace and failing account provisioning. Root-caused and fixed in sparqd/control-plane#346.

If a future region ever puts Keycloak (or a tenant cluster's OIDC issuer) behind a private/self-signed cert, that needs its own explicitly-named CA config — not a reuse of caBundlePath.