Keycloak
Overview
Section titled “Overview”Keycloak is an open-source identity and access management platform. Unlike Auth0 and Microsoft Entra ID, Keycloak is self-hosted: you run a Keycloak instance (in the same cluster or externally) and the Governance Platform authenticates against it.
The platform requires a dedicated Keycloak realm containing three OAuth clients:
- Frontend client (
governance-platform-frontend) - public SPA client used by Governance Studio for sign-in - Backend client (
governance-platform-backend) - confidential client with a service account, used by the Auth Service for user management - Worker client (
governance-worker) - confidential machine-to-machine client used by the governance worker
The keycloak-bootstrap Helm chart automates all of this: it runs a one-time Kubernetes Job that creates the realm, the three clients, the platform’s custom authorization scopes, and an initial platform-admin user via the Keycloak Admin REST API. Run it before deploying the platform chart - the services need valid client credentials at startup.
Prerequisites
Section titled “Prerequisites”- A running Keycloak instance, or a cluster where one can be deployed (this guide includes an in-cluster option)
- Keycloak admin credentials for the master realm
kubectlandhelmaccess to the deployment namespace- A checkout of the deployment repository for the
keycloak-bootstrapchart
Helm Configuration
Section titled “Helm Configuration”After completing the setup steps below, the platform configuration files will contain:
values.yaml:
auth-service
:
config
:
idp
:
provider
:
"
keycloak
"
issuer
:
"
https://
/realms/governance
"
# Verified in step 6
keycloak
:
realm
:
"
governance
"
adminUrl
:
"
https://
"
# Keycloak base URL
clientId
:
"
governance-platform-frontend
"
enableUserManagement
:
true
tokenExchange
:
enabled
:
true
keyId
:
"
auth-service-001
"
# Identifier for the token exchange key
governance-service
:
config
:
keycloakUrl
:
"
https://
"
keycloakRealm
:
"
governance
"
governance-studio
:
config
:
keycloakUrl
:
"
https://
"
keycloakRealm
:
"
governance
"
keycloakClientId
:
"
governance-platform-frontend
"
# Post-install hook: seeds the governance database with the organization
# and the platform-admin user created by the bootstrap
keycloak
:
createOrganization
:
true
realmName
:
"
governance
"
createPlatformAdmin
:
true
platformAdminEmail
:
""
# Defaults to admin@
secrets.yaml:
global
:
secrets
:
create
:
true
auth
:
provider
:
"
keycloak
"
keycloak
:
secretName
:
"
platform-keycloak
"
values
:
serviceAccountClientId
:
"
governance-platform-backend
"
serviceAccountClientSecret
:
"
"
# Retrieved in step 5
tokenExchangePrivateKey
:
|
# Generate with: openssl genrsa 2048
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
governanceWorker
:
secretName
:
"
platform-governance-worker
"
values
:
encryptionKey
:
"
GENERATE_WORKER_KEY
"
# Generate with: openssl rand -base64 32
clientId
:
"
governance-worker
"
clientSecret
:
"
"
# Retrieved in step 5
Note: the frontend client ID is configured in values.yaml, not secrets.yaml - it is a public client with no secret.
Quick Start (Bootstrap Chart - Recommended)
Section titled “Quick Start (Bootstrap Chart - Recommended)”1. Deploy or Identify a Keycloak Instance
Section titled “1. Deploy or Identify a Keycloak Instance”If you already run Keycloak, note its URL and admin credentials and skip to step 2. Otherwise, deploy Keycloak into the cluster with the Bitnami chart:
# Keycloak server admin password (master realm)
kubectl
create
secret
generic
keycloak-admin
\
--from-literal=password=
"
$(
openssl
rand
-base64
32
)
"
\
--namespace
governance
helm
upgrade
--install
keycloak
bitnami/keycloak
\
--namespace
governance
\
--values
keycloak-values.yaml
\
--wait
\
--timeout
10m
See the deployment repository’s Keycloak guides for complete keycloak-values.yaml examples per cloud provider, including ingress and TLS settings.
Verify Keycloak is healthy before continuing:
kubectl
run
curl-test
--rm
-it
--image=curlimages/curl
--restart=Never
\
--namespace
governance
--
\
curl
-s
-o
/dev/null
-w
"
%{http_code}
"
http://keycloak:9000/keycloak/health/ready
2. Create Pre-Bootstrap Secrets
Section titled “2. Create Pre-Bootstrap Secrets”The bootstrap job authenticates to Keycloak with the keycloak-admin secret (created in step 1, or create it now with your external Keycloak’s admin password) and sets the initial Governance Studio admin user’s password from platform-admin:
# Governance Studio admin user password (governance realm user, not the Keycloak server admin)
kubectl
create
secret
generic
platform-admin
\
--from-literal=password=
"
$(
openssl
rand
-base64
32
)
"
\
--namespace
governance
3. Prepare the Bootstrap Values
Section titled “3. Prepare the Bootstrap Values”From the deployment repository checkout, start from the example values file:
cp
charts/keycloak-bootstrap/examples/values.yaml
bootstrap-values.yaml
Edit bootstrap-values.yaml, replacing the domain placeholders with the platform domain:
clients
:
frontend
:
redirectUris
:
-
"
https://
/*
"
webOrigins
:
-
"
https://
"
backend
:
redirectUris
:
-
"
https://
/authService/*
"
webOrigins
:
-
"
https://
"
users
:
admin
:
email
:
"
admin@
"
If Keycloak is not reachable at the in-cluster default (http://keycloak:8080/keycloak), set its URL:
keycloak
:
url
:
"
https://
"
# External or cross-namespace URL
4. Run the Bootstrap
Section titled “4. Run the Bootstrap”
helm
upgrade
--install
keycloak-bootstrap
./charts/keycloak-bootstrap
\
--namespace
governance
\
--values
bootstrap-values.yaml
\
--wait
\
--timeout
10m
The deployment repository also provides a helper script that validates prerequisites, runs the chart, and monitors the job: ./scripts/keycloak/bootstrap-keycloak.sh -f bootstrap-values.yaml -n governance.
Monitor the job and confirm it completes:
kubectl
get
jobs
-l
app.kubernetes.io/instance=keycloak-bootstrap
\
--namespace
governance
-w
kubectl
logs
job/keycloak-bootstrap
--namespace
governance
-f
The job is idempotent - re-running it skips resources that already exist.
5. Retrieve the Auto-Generated Client Secrets
Section titled “5. Retrieve the Auto-Generated Client Secrets”Keycloak generates the backend and worker client secrets during bootstrap. Retrieve both via the Admin API:
# Port-forward the Keycloak service (skip if Keycloak has an external URL)
kubectl
port-forward
svc/keycloak
8080:8080
--namespace
governance
&
# Get an admin token
ADMIN_PASS
=
$(
kubectl
get
secret
keycloak-admin
--namespace
governance
\
-o
jsonpath=
'
{.data.password}
'
|
base64
-d
)
TOKEN
=
$(
curl
-s
-X
POST
"
http://localhost:8080/keycloak/realms/master/protocol/openid-connect/token
"
\
-d
"
username=admin
"
\
-d
"
password=
$ADMIN_PASS
"
\
-d
"
grant_type=password
"
\
-d
"
client_id=admin-cli
"
|
jq
-r
'
.access_token
'
)
# Backend client secret
curl
-s
-H
"
Authorization: Bearer
$TOKEN
"
\
"
http://localhost:8080/keycloak/admin/realms/governance/clients?clientId=governance-platform-backend
"
\
|
jq
-r
'
.[0].secret
'
# Worker client secret
curl
-s
-H
"
Authorization: Bearer
$TOKEN
"
\
"
http://localhost:8080/keycloak/admin/realms/governance/clients?clientId=governance-worker
"
\
|
jq
-r
'
.[0].secret
'
kill
%1
Alternatively, use the admin console: Clients → governance-platform-backend → Credentials tab (and the same for governance-worker).
6. Update Chart Values
Section titled “6. Update Chart Values”Fill in the Helm Configuration sections:
- The two client secrets from step 5
- A token exchange private key:
openssl genrsa 2048 - The issuer URL, which you can verify with:
curl
-s
https://
/realms/governance/.well-known/openid-configuration
|
jq
'
.issuer
'
Then proceed to the Helm Deployment guide. The platform chart’s post-install hook creates the organization and links the platform-admin user automatically.
Alternative: Manual Setup via Admin Console
Section titled “Alternative: Manual Setup via Admin Console”The bootstrap chart is strongly recommended - it configures the realm, clients, protocol mappers, custom scopes, and service account roles consistently. If you must configure Keycloak manually, replicate what the bootstrap creates:
- Create a
governancerealm with brute force protection enabled - Create the three clients listed in the Overview: frontend as a public client with the platform domain in redirect URIs and web origins; backend and worker as confidential clients with service accounts enabled
- Grant the backend client’s service account the
query-usersandview-usersrealm-management roles - Create the platform’s custom authorization scopes and assign the frontend client’s default scopes (
openid,profile,email,roles,sub) - Create a
platform-adminrealm user
See the keycloak-bootstrap chart README for the complete list of scopes, mappers, and defaults to replicate.
Verification
Section titled “Verification”Confirm the realm’s OIDC discovery endpoint responds and the issuer matches values.yaml:
curl
-s
https://
/realms/governance/.well-known/openid-configuration
|
jq
'
.issuer
'
After deploying the platform, sign in to Governance Studio as platform-admin using the password from the platform-admin secret:
kubectl
get
secret
platform-admin
--namespace
governance
\
-o
jsonpath=
'
{.data.password}
'
|
base64
-d
Security Best Practices
Section titled “Security Best Practices”- Require TLS on all Keycloak endpoints (
sslRequired: externalis the bootstrap default; useallif internal traffic should also be encrypted) - Keep brute force protection enabled on the governance realm (bootstrap default)
- Store the Keycloak admin password, client secrets, and token exchange private key only in Kubernetes secrets or an approved secret manager
- Leave self-registration disabled unless your onboarding process requires it
- Rotate the backend and worker client secrets periodically - regenerate in Keycloak, then update the
platform-keycloakandplatform-governance-workersecrets in the same change window - Keep the Keycloak version patched; it is part of your authentication attack surface