A default Keycloak installation is built to be easy to start, not safe to expose, and many production deployments still carry development habits: a busy master realm, an internet-facing admin console and confidential clients with unrotated secrets. Because Keycloak issues the tokens that every connected application trusts, a weak realm or client setting becomes a weakness in all of them. This guide covers the Keycloak 26.x server and its realms, clients, admin roles, service accounts, events and network exposure, using the option and setting names from the official keycloak.org documentation. It is written for IAM engineers, platform teams and security architects who run Keycloak themselves, whether on virtual machines, containers or the Keycloak Operator on Kubernetes.
Key takeaways
- Keep the master realm for administration only. Create a separate realm for each user population and do not place ordinary users or applications in master, because master admins can manage every realm on the server.
- Never expose the admin console or Admin REST API to the internet. Use a separate admin hostname, block
/admin/at the reverse proxy and keep the management interface on port 9000 internal. - Treat every confidential client and service account as a non-human identity. Prefer Signed JWT or certificate client authentication, rotate secrets with the secret rotation client policy and scope service account roles tightly.
- Turn on the defences that are off by default. Brute force detection is disabled and login and admin events are not stored until you enable them per realm.
- Shorten token and session lifetimes and plan revocation. Short access tokens, revoked refresh tokens and a tested not-before push limit what a stolen token is worth.
How identity and access work in Keycloak
Keycloak is an open source identity and access management server that speaks OpenID Connect, OAuth 2.0 and SAML 2.0. Its security model rests on a few objects, and most hardening decisions are about one of them.
| Object | What it is | Security relevance |
|---|---|---|
| Realm | An isolated namespace of users, clients, roles, keys and policies | The trust boundary. Signing keys, session settings, brute force detection and events are configured per realm. |
| Master realm | The realm created at first start that holds server administrators | Admins here can create and manage every realm. Keycloak documentation advises using it only to manage other realms. |
| Client | An application or service that requests tokens (OIDC) or assertions (SAML) | Public clients (browser, mobile) cannot keep a secret; confidential clients authenticate with a secret, Signed JWT or certificate. |
| Service account | A user automatically linked to a confidential client for the client credentials grant | A machine identity whose roles are often over-granted and rarely reviewed. |
| Roles and client scopes | Realm roles, client roles and scopes that decide which claims reach a token | Role scope mappings are unlimited by default, so tokens can carry far more roles than the client needs. |
| Identity providers and user federation | External IdPs (OIDC, SAML, social) and LDAP or Active Directory federation | Hold bind credentials and client secrets that belong in a vault, and import trust from outside the realm. |
Flows tie these together: an authentication flow decides which authenticators (password, OTP, WebAuthn) a user passes, and client policies can enforce rules such as allowed redirect URIs or client authentication methods for every client in a realm. For the protocol background see the OAuth 2.0 and OpenID Connect Guide, and for IdP-wide controls see the Identity Provider and SSO Security Guide.
Admin roles and privileged access
Keycloak has two layers of administration. In the master realm, the global admin role grants full control of the server and create-realm lets a user create new realms. Each realm also appears in master as a client named <realm>-realm, whose roles grant administration of that realm from master. Inside each realm, the realm-management client carries fine-grained roles.
| realm-management role | What it allows | Hardening note |
|---|---|---|
realm-admin | Every administrative action in that realm | Equivalent to realm owner. Assign to very few named people, never to service accounts used by applications. |
manage-users | Create, edit and delete users, set credentials, assign roles the admin already holds | Keycloak only lets this role assign roles the admin holds, but combined with broad roles it is still a privilege escalation path. |
impersonation | Log in as another user | Highly sensitive. Remove from all accounts that do not need support impersonation and alert on its use. |
manage-clients | Create and change clients, view client secrets | Viewing a confidential client secret requires this role, so it is effectively secret read access. |
manage-realm, manage-identity-providers, manage-events | Realm settings, external IdPs, event configuration | An attacker with these can add a rogue IdP or switch off event storage. |
view-users, view-clients, view-events, query-* | Read-only access | Use for support and audit roles instead of manage roles. |
- Delegated administration: Fine-grained admin permissions (version 2, enabled with the Admin Permissions realm setting) let you grant admins rights over specific users, groups, clients, roles or organisations through the
admin-permissionsclient. Users holdingadminorrealm-adminare not affected, so the feature narrows delegated admins, not super admins. - Admin roles from tokens are ignored: admin roles added through protocol mappers do not grant console rights, so privilege must be assigned explicitly and can be reviewed in role mappings.
- Bootstrap admin: the first admin created with
KC_BOOTSTRAP_ADMIN_USERNAMEandKC_BOOTSTRAP_ADMIN_PASSWORD(default usernametemp-admin) is temporary. Create named, MFA-protected admins, then delete it. Thebootstrap-admincommand creates a recovery admin or service account if all admins are lost; restrict who can run it, because it needs host access to the server. - MFA for admins: require OTP or WebAuthn for the master realm browser flow, or federate master to your workforce IdP with phishing-resistant MFA. See the Phishing-Resistant MFA Guide.
- Break-glass: keep one local master realm admin with a hardware key and a sealed password for when federation fails. See the Break-Glass Account Guide.
Integrations, API tokens and service accounts
Most Keycloak compromise paths that do not start with a user password start with a client credential. Confidential clients, service accounts, federation bind accounts and identity provider secrets are all non-human identities and need the same lifecycle as any other secret.
| Client authentication option | How it works | When to choose it |
|---|---|---|
| Client Id and Secret (default) | Shared secret sent to the token endpoint | Acceptable for low-risk internal services if rotated and stored in a vault. Weakest option: anyone who reads the secret can be the client. |
| Signed JWT | Client signs an assertion with its private key; Keycloak verifies with a registered key or a JWKS URL | Preferred for services that can hold a key. No shared secret is stored in Keycloak, and a JWKS URL lets the client rotate keys itself. |
| Signed JWT with client secret | Assertion signed with HMAC using the secret | Only where the client cannot hold an asymmetric key; it still depends on a shared secret. |
| X509 certificate (mutual TLS) | Client proves possession of a certificate at the TLS layer | High-assurance and FAPI-style deployments; also enables certificate-bound tokens so a stolen token cannot be replayed without the key. |
- Secret rotation: the secret rotation client policy lets a client keep two active secrets during a rotation window, so you can rotate without an outage. Set an expiry and rotate on a schedule and after any staff change on the owning team.
- Service accounts: enable Service accounts roles only on clients that use the client credentials grant, and assign only the client roles needed. The Full Scope Allowed setting puts every role the account holds into its tokens; switch it off and map explicit role scopes. Never give an application service account
realm-adminormanage-usersunless it is a provisioning tool, and then document it. See the Machine-to-Machine OAuth (Client Credentials) Guide. - Admin REST API automation: Terraform providers, CI pipelines and provisioning scripts usually authenticate as a service account in master or the target realm. Give them per-realm roles rather than the global
adminrole, restrict their network origin at the proxy and log their admin events. - Vault for server-side secrets: SMTP passwords, LDAP bind credentials and identity provider client secrets can be referenced from the file-based or PKCS12 keystore vault (
${vault.key}) instead of being stored in the database. See the Secrets Management Guide. - Redirect URIs and public clients: exact redirect URIs (no wildcards), PKCE for public clients and the secure redirect URIs enforcer client policy executor close the most common authorisation code theft paths.
- Offline tokens: remove the
offline_accessscope from clients that do not need long-lived refresh tokens, and set Offline Session Max Limited for those that do.
The OneLogin incident shows why secrets held by an IdP matter: the OneLogin API flaw exposed OIDC client secrets to anyone holding an API key, which is the same class of exposure as an over-granted manage-clients or view-clients role in Keycloak.
Logging and detection
Keycloak does not store events by default: only errors reach the server log. Enable storage per realm under Realm settings, Events.
- User events: turn on Save events with an Expiration that matches your retention policy, and forward them to the SIEM. The
jboss-loggingevent listener writes events to theorg.keycloak.eventslog category, which is the simplest route to a log pipeline. - Admin events: enable admin events and Include representation so you can see what was changed, not just that something changed. Long representations can be truncated; tune
--spi-events-store--jpa--max-field-lengthif you store them in the database. - Health and metrics:
/healthand/metricsare served on the management interface (port 9000). Scrape them internally; never route them through the public proxy.
| Signal | Event or source | What it can indicate |
|---|---|---|
Bursts of LOGIN_ERROR with invalid_user_credentials across many usernames | User events | Password spraying or credential stuffing (ATT&CK T1110) |
| Brute force lockout events for privileged users | User events | Targeted guessing against admins |
CLIENT_LOGIN from a service account at unusual times or from new IP ranges | User events plus proxy logs | Stolen client secret in use (T1528, T1550.001) |
| Admin events creating clients, identity providers or role mappings, or changing authentication flows | Admin events | Persistence or MFA weakening (T1098, T1556.006) |
IMPERSONATE events | User events | Use of the impersonation role; should be rare and ticketed |
Requests to /admin/ paths at the public proxy | Reverse proxy or WAF logs | Exposure mistake or reconnaissance |
Metrics to track: percentage of realms with events and admin events stored; number of confidential clients still using Client Id and Secret; median age of client secrets; number of accounts holding realm-admin, admin or impersonation; percentage of master realm admins with MFA; and time from Keycloak security release to production upgrade. See the SIEM Guide for building the detections.
Hardening baseline
Server and network
- TLS everywhere: run with HTTPS (
https-certificate-fileandhttps-certificate-key-file, or terminate at the proxy and re-encrypt) and leave HTTP disabled. Set each realm's Require SSL toall requests; the default,external requests, allows plain HTTP from private IP addresses. - Hostname: the
hostnameoption is mandatory in production. Do not sethostname-strict=falseon an exposed server, because Keycloak would then derive URLs from request headers. Sethostname-adminto an internal hostname; note that it moves the console but does not block the Admin REST API, so the proxy must do that. - Reverse proxy: set
proxy-headerstoforwardedorxforwardedto match your proxy andproxy-trusted-addressesto the proxy's addresses so spoofed headers from elsewhere are ignored.
| Path | Expose publicly? | Reason |
|---|---|---|
/realms/ | Yes | OIDC and SAML endpoints used by applications and users |
/resources/ | Yes | Static assets for login pages |
/.well-known/ | Yes | Discovery metadata |
/realms/master/ | No | Token endpoint for admin logins; keep internal |
/admin/ | No | Admin console and Admin REST API |
/metrics, /health | No | Served on the management interface, port 9000 |
- Load and availability: set
http-max-queued-requeststo shed load instead of exhausting memory, run at least two instances against a production database, and use/health/readyfor load balancer checks. Cluster traffic between nodes (JGroups) is encrypted with TLS by default in current releases; do not disable it. - Patching: follow Keycloak security advisories and keep to a supported 26.x minor release. Upgrades are also how you inherit changed defaults.
Realm settings
| Setting | Default | Hardened value |
|---|---|---|
| Brute Force Detection (Security Defenses) | Disabled | Enabled; review Max Login Failures (30 when enabled), Wait Increment (1 min), Max Wait (15 min) and Failure Reset Time (12 h). Lower Max Login Failures for most workforce realms. Applies to passwords, OTP and recovery codes. |
| Require SSL | external requests | all requests |
| Events and admin events | Not stored | Stored with expiry and forwarded to SIEM |
| Role scope mappings on clients | Full scope (unlimited) | Full Scope Allowed off; explicit scopes per client |
| Realm keys | Generated at realm creation | Rotate every three to six months, keeping the old key passive until issued tokens expire |
| Revoke Refresh Token | Off | On, so each refresh token is single-use and replay is detectable |
| SSO Session Idle and Max, Access Token Lifespan | Product defaults | Set from your session policy; keep access tokens in minutes, not hours |
For lifetime design see the Token Lifetime Policy Design Guide, and for lockout tuning the Account Lockout and Brute-Force Protection Guide.
Rollout order
- Inventory: list realms, clients (public or confidential, authentication method, secret age), service accounts and their roles, every holder of
admin,realm-adminandimpersonation, and every identity provider and federation link. - Close exposure first: block
/admin/,/realms/master/and port 9000 at the proxy, sethostname,hostname-adminand trusted proxy addresses. Test admin access over the internal path before cutting the public one, and keep the old rule ready to restore. - Turn on visibility: enable events and admin events in every realm and confirm they arrive in the SIEM before changing anything else, so later changes are recorded.
- Secure admins: named admins with MFA, delete the temporary bootstrap admin, remove unused admin and impersonation roles, set up the break-glass account.
- Pilot client changes: on a staging realm export, move one confidential client to Signed JWT, switch off Full Scope Allowed and shorten token lifespans. Watch for
CODE_TO_TOKEN_ERRORandCLIENT_LOGIN_ERRORevents; roll back by restoring the client settings from the realm export. - Enable brute force detection and client policies realm by realm, starting with the workforce realm, then customer realms where lockout affects support volume.
- Make it repeatable: keep realm configuration in code (realm import or a declarative tool), review it in pull requests and schedule key and secret rotation.
Small vs large estates: a single team with one or two realms can rely on a hardened master realm with named admins and MFA. Large or multi-tenant deployments should use fine-grained admin permissions or separate Keycloak clusters per tenant group, because a master realm admin compromise affects every realm on the cluster.
Common misconfigurations
- Admin console on the public hostname. The console and Admin REST API are reachable wherever
/admin/is routed. Settinghostname-adminalone does not stop API calls on the public host. - Applications and users in the master realm. It makes every application user a neighbour of the server admins and puts application flows next to the realm that controls everything.
- Brute force detection never switched on. It is off by default, so password spraying against a customer realm is only limited by your proxy. The Taiwan government SSO incident shows how automated password spraying of predictable passwords leads straight into SSO-connected systems.
- Long-lived client secrets with broad service account roles. A secret copied into a CI variable years ago, attached to a service account with
manage-users, is an administrator credential that never expires. - Wildcard redirect URIs and public clients without PKCE. These allow authorisation codes and tokens to be sent to attacker-controlled locations.
- No event storage. When an incident happens there is no record of which admin changed a flow or which client minted tokens, and investigation relies on proxy logs alone.
Framework mapping
| Control area | NIST SP 800-53 Rev. 5 | NIST CSF 2.0 | CIS Controls v8.1 | ISO/IEC 27001:2022 Annex A |
|---|---|---|---|---|
| Admin roles, least privilege, service accounts | AC-2, AC-6 | PR.AA-05 | Control 5, Control 6 | A.5.15, A.8.2 |
| Admin MFA, client authentication | IA-2, IA-2(1), IA-5 | PR.AA-01, PR.AA-03 | Control 6 | A.5.17, A.8.5 |
| TLS, hostname, proxy and secure configuration | SC-8, CM-6 | PR.PS-01 | Control 4 | A.8.9 |
| Events, admin events and monitoring | AU-2, AU-6 | PR.PS-04, DE.CM-03 | Control 8 | A.8.15, A.8.16 |
Practitioner checklist
- Block
/admin/,/realms/master/,/metricsand/healthat the public proxy and serve the console on an internalhostname-admin. - Set
hostname,proxy-headersandproxy-trusted-addresses; never runhostname-strict=falsein production. - Set Require SSL to all requests in every realm.
- Remove applications and ordinary users from the master realm; use named, MFA-protected admins and delete the temporary bootstrap admin.
- Review every holder of
admin,realm-admin,manage-users,manage-clientsandimpersonationquarterly. - Move confidential clients to Signed JWT or mutual TLS where possible; rotate remaining secrets with the secret rotation policy.
- Switch off Full Scope Allowed and scope service account roles to what each integration needs.
- Enable brute force detection, Revoke Refresh Token and short access token lifespans in each realm.
- Enable user and admin event storage with representations and forward events to the SIEM.
- Rotate realm signing keys every three to six months and keep realm configuration in version control.
Standards and references
- Keycloak: Configuring Keycloak for production
- Keycloak: Configuring a reverse proxy
- Keycloak: Server Administration Guide
- Keycloak: Configuring the hostname
- Keycloak: Configuring the management interface
- Keycloak: Using a vault
- NIST: SP 800-53 Rev. 5 Security and Privacy Controls
- MITRE ATT&CK: T1528 Steal Application Access Token
Related NHI Mgmt Group resources: Identity Provider and SSO Security Guide · OAuth 2.0 and OpenID Connect Guide · Machine-to-Machine OAuth (Client Credentials) Guide · Service Account Security Guide · Duo Hardening Guide