Keycloak SSO with OIDC and SAML for Admin Portals - 夜莺博客

Keycloak SSO with OIDC and SAML for Admin Portals

Infrastructure portals — monitoring, IPAM, automation runners, virtualisation consoles — tend to grow their own user databases until nobody can answer who has access to what. Keycloak centralises that with OpenID Connect and SAML, and adds group-based authorisation, LDAP federation and MFA without touching each application's code. This guide covers a working deployment, the client configuration for both protocols, and the role mapping that turns directory groups into application permissions.

Deployment with containers

docker run -d --name keycloak \
  -p 8443:8443 -p 8080:8080 \
  -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD='<pw>' \
  -e KC_DB=postgres -e KC_DB_URL=jdbc:postgresql://pg01:5432/keycloak \
  -e KC_DB_USERNAME=keycloak -e KC_DB_PASSWORD='<pw>' \
  -e KC_HOSTNAME=auth.example.net \
  quay.io/keycloak/keycloak:latest start --optimized

# verify
curl -s https://auth.example.net/realms/master/.well-known/openid-configuration | jq '.issuer, .end_session_endpoint'

Use a real database in production. The dev-file database loses every realm on restart and is the reason many pilots never make it past testing.

Realm and client setup

# Realm: corp-infra
# Client: grafana        Protocol: OpenID Connect   Access type: Confidential
#   Valid redirect URIs: https://grafana.example.net/login/generic_oauth
#   Web origins:         https://grafana.example.net
# Client: netbox          Protocol: OpenID Connect   (helper: netbox)
# Client: jenkins         Protocol: SAML              (ACS URL from Jenkins plugin)

One realm per security domain — infrastructure, corporate users and lab are separate realms rather than separate role hierarchies inside one. It removes a whole class of accidental access.

Groups to roles, roles to applications

# Directory groups federate in and map to realm roles
Groups:  /netops   -> area: create vlan, port-channel, ssh to switches
         /secops   -> area: policy set management, read-only network
         /dba      -> area: database console, backup restore

# Mapper on an OIDC client
Client scope mapper: "realm roles"
  Mapper type: User Realm Role
  Token claim name: realm_access.roles  (default)
  Add to ID token: on   Access token: on

# Group membership mapper for app-side groups
Mapper type: Group Membership   Token claim name: groups   Full group path: off
# Example application-side check (Grafana role_attribute_path)
role_attribute_path = contains(groups[*], 'netops') && 'Admin' || contains(groups[*], 'dba') && 'Viewer' || 'None'

LDAP federation and MFA

User federation > LDAP
  Vendor: Active Directory / Other
  Connection URL: ldaps://dc01.corp.example.net:636
  Users DN: OU=Users,DC=corp,DC=example,DC=net
  Username LDAP attribute: sAMAccountName
  Edit mode: READ_ONLY
  Sync: periodic, 12h  (plus on-demand sync for new users)

Authentication > Required actions  -> enable "Configure OTP"
Authentication > Policies         -> OTP policy: SHA1/TOTP, 6 digits, 30s
# then require it per role or per group via a conditional flow

Keep LDAP read-only: passwords and lifecycle belong in the directory, policy and application roles belong in Keycloak. Mixing the two creates users who exist in both systems with different meanings.

Verify the token before blaming the app

curl -s -X POST https://auth.example.net/realms/corp-infra/protocol/openid-connect/token \
  -d grant_type=password -d client_id=grafana -d client_secret='<secret>' \
  -d username=opsuser -d password='<pw>' | jq -r '.access_token' \
| cut -d. -f2 | base64 -d 2>/dev/null | jq '{groups: .groups, roles: .realm_access.roles, exp: .exp}'

If the roles claim is missing, the mapper is attached to the wrong client scope; if the signature fails at the application, check clock skew and the realm's key rotation before touching client secrets.

Related reading: FreeIPA LDAP and Kerberos for Linux authentication, Cloudflare Access with Entra ID as identity provider, and Linux SSSD Active Directory realm join troubleshooting.

原文链接:https://www.keycloak.org/docs/latest/server_admin/