Keycloak

Keycloak ist eine Open Source IAM-Lösung:

  • Identity Management: Provisionierung, Administration und Deprovisionierung von Benutzern

  • Access Management: Authentication (AuthN: Identifikation) und Authorization (AuthZ: Berechtigungen)

Seit Keycloak 17 (2022) läuft Keycloak auf dem Quarkus-Stack und startet pro Instanz mit minimal 512 MiB RAM; produktiv empfiehlt Red Hat mindestens 2 GiB Heap. Die frühere WildFly-basierte Variante ist seit Keycloak 20 EOL.

Begriffe
  • Client: Applikation, die Keycloak für Authentifizierung nutzt (z.B. eine Webapp, ein Reverse-Proxy wie oauth2-proxy, eine Native-App, eine CLI). Jeder Client hat eine eigene Client-ID und optional ein Client-Secret.

  • Client Scope: Satz wiederverwendbarer Protokoll-Mapper und Role-Mappings, der Clients zugewiesen werden kann - verhindert, dass man bei jedem Client die gleichen Claims einzeln pflegt.

  • Flow (Authentication Flow): anpassbarer Ablauf von Authenticators (Passwort, OTP, WebAuthn, Cookie, Identity-Brokering, …). Standard-Flows lassen sich per Copy-&-Customize an eigene Anforderungen anpassen.

  • IdP Broker (Identity Provider): ein anderer, externer IdP (Google, Microsoft Entra, LDAP, ein weiteres Keycloak), dessen Benutzer über Keycloak durchgereicht werden.

  • Realm: isolierter Mandant in Keycloak mit eigenen Benutzern, Rollen, Clients, Themes und Flows. Der master-Realm ist für Keycloak-Admin-Zugänge reserviert - Applikationen sollten nie im master-Realm leben.

  • Role: Berechtigung - entweder Realm-weit (manage-users) oder Client-bezogen (admin, editor, viewer). Rollen können verschachtelt (Composite Roles) und Benutzern oder Gruppen zugewiesen werden.

  • User Federation: externe Benutzerquelle (LDAP, Kerberos, SSSD, eigenes SPI- Plugin). Keycloak spiegelt Benutzer nur in der eigenen DB, wenn es sinnvoll ist (Edit Mode).

Wer in Applikationen keine Benutzerschicht implementieren möchte, nutzt Keycloak. Keycloak bietet

  • Benutzer- und Gruppenmanagement

  • Session Management

  • Brute Force Detection

  • Passwort-Policies, Verify email, …

  • User Registration

  • User Self Service (Forgot Password, Edit User Data etc.)

  • User Consent

  • 2-Factor-Auth (z.B. mit OTP)

  • Identity Federation/Brokering (OpenID Connect, also OAuth 2.0 plus Login- und Profil-Informationen, oder SAML 2.0): Verbindung zu externen Identity Providern, die SAML oder OIDC unterstützen, wie Google, GitHub, Twitter, Facebook, …

  • User Storage Federation über Kerberos (AD) oder LDAP

  • Templating (Layout, E-Mails etc.)

Container

Keycloak als Container im Developer Mode starten:

podman run \
    --detach \
    --name=keycloak \
    --publish=8080:8080 \
    --env=KC_BOOTSTRAP_ADMIN_USERNAME=admin \
    --env=KC_BOOTSTRAP_ADMIN_PASSWORD=linuxfabrik \
    quay.io/keycloak/keycloak:latest start-dev

Benutzer

Besonders interessant für die Konfiguration:

  • Authentication > Policies

  • Authentication > Required Actions

  • Users > Credentials > Reset Password

  • Users > Role Mappings

  • Users > Details: Required User Actions

  • Realm settings > Login

MariaDB/PostgreSQL statt H2: Eine SQL-Datenbank wie MariaDB oder PostgreSQL ist nur bei grösseren Installationen oder bei einem Keycloak-Cluster (dann zwingend und dediziert) nötig, ansonsten verwendet Keycloak intern die Java SQL H2-Datenbank. In diesem Fall liegen die DB-Dateien in standalone/data/. Die H2-DB lässt sich per java -jar modules/system/layers/base/com/h2database/h2/main/h2-*.jar administrieren (auf der lokalen Admin-Maschine per sshfs ausführen - es wird ein Browser gestartet).

Damit eine benutzerdefinierte Datenbank angebunden werden kann, ist die Entwicklung eines User Storage SPI-Plugins notwendig (Service Provider Interfave). Das Plugin kommuniziert zwischen Keycloak und der benutzerdefinierten Datenbank, und kümmert sich um die Abfrage und das Updaten der Benutzerdaten, Authentication und Access Control. Diese Plugins werden in Java geschrieben.

Clients

Mit „Client“ ist immer die Applikation gemeint, nicht die Benutzer.

Im Fall von OpenID Connect/OAuth wird die Applikation durch einen OAuth-Client geschützt, indem dieser bei nicht vorhandener Session den Keycloak-Server um Authentifizierung bittet. Der Authentication Flow ist dabei wie folgt (und wird in OAuth vs. OpenID Connect (OIDC) vs. SAML genauer beschrieben):

  1. Der Client (genauer: ein OAuth-Client) leitet den Benutzer auf Keylcoak um.

  2. Der Benutzer authentifiziert sich mit seinen Keycloak-Credentials. Keycloak generiert eine neue Session-ID, die als Cookie im Browser des Benutzers gespeichert wird.

  3. Der Benutzer wird mit einem Token auf den Client umgeleitet.

  4. Der Client bekommt einen Access-Token zugewiesen.

  5. Mit Hilfe des Access-Tokens kann der Client Zugriff gewähren.

Der Client kann selbst noch eigene Cookies anlegen, um den Status seines Benutzers zu speichern. Die Client-Session und die Keycloak-Session sind also immer voneinander getrennt, der Authentication-State ist aber über Keycloak und alle Clients identisch, wobei der Authentication-State immer auf dem Keycloak-Server gespeichert ist. So wird SSO möglich.

Entweder bringt der Client einen OAuth-Client/die passende Library mit (wie z.B. Grafana), man nutzt einen speziellen OAuth-Proxy (z.B. oauth2-proxy), oder man installiert einen im Webserver (z.B. für Apache mod_auth_openidc). Die verschiedenen Arten in der Übersicht:

              :443            :443         :443        :443
           oauth2-proxy    AnyRevProxy    Apache    AnyRevProxy
                |               |        mod_auth        |
                |               |            |           |
                |             :4180          |       Web-App +
                |          oauth2-proxy      |      OAuth-Client
                |               |            |           |
Keycloak <------+---------------+------------+-----------+
                |               |            |
             Web-App         Web-App      Web-App

SSO

Integrationsmethoden/Protokolle für SSO:

  • OpenID Connect (OIDC)

  • SAML 2.0

Bei Applikationen, die lokal beim Nutzer im Source Code laufen, können Authorization Code, die Client-ID und der das Client-Secret nicht hinterlegt werden. Daher unterscheidet Keycloak zwei Zugriffstypen für Applikationen:

  • Confidential (in Keycloak „Client authentication“ auf „on“): Für traditionelle Webapplikationen, deren Source Code auf dem Server gespeichert ist.

  • Public (in Keycloak „Client authentication“ auf „off“): Für Applikationen, deren Source Code lokal einsehbar ist. Hier wird PKCE (Proof-Key for Code Exchange, sprich „pixi“) verwendet, um sich gegenüber Keycloak zu authentifizieren. Von PKCE können auch Confidential-Applikationen profitieren, da damit eine zusätzliche Authentifizierungsmassnahme umgsetzt wird. OAuth 2.1 schreibt daher die Nutzung von PKCE vor.

Applikationstypen, die per Keycloak SSO-fähig gemacht werden können:

  • Traditionelle Webapplikationen, die auf dem Server gespeichert und abgearbeitet werden. Hier wird mit Hilfe der Redirects auf Basis von OIDC oder SAML gearbeitet.

  • Single Web Page Applikationen (SPA) auf Basis eines Javascript-Frontends wie Angular oder React, die komplett im Browser im Source Code ablaufen. Es kommt OIDC mit PKCE zum Einsatz. Zudem ist der Ablauf, um ein Access Token zu erhalten, erweitert:

    • Die SPA erzeugt einen zufälligen Wert. Dieser wird als hashed Value zusammen mit dem Redirect-Request als „code_verifier“ an Keycloak gesendet.

    • Die SPA verwendet den Autz-Code plus code_verifier, um ein Access Token zu erhalten (siehe OAuth vs. OpenID Connect (OIDC) vs. SAML). Damit wird die SPA als Inhaber des Authz-Codes indentifiziert.

  • Native Applications, z.B. Mobile Applications. Diese sind ähnlich wie SPA zu konfigurieren. Die Native Application muss sich bei der Authentifizierung allerdings auf einen Browser verlassen.

SAML unterstützt nur traditionelle Webapplikationen; bei SPA und Native Applications muss OIDC verwendet werden.

Logout:

  • Aus Sicht des Clients: Der Client erhält vom Keycloak-Server in der Authentication Response den Parameter session_state. Der enthaltene String-Value zeigt an, ob sich der Benutzer von Keycloak abgemeldet hat. Der Client sendet daher periodische Anfragen an den Keycloak-Server, um den Session-State zu ermitteln. Wurde der Benutzer ausgeloggt, sendet der Client den Benutzer zurück zur Keycloak Login-Seite.

  • Aus Keycloak-Sicht: Kennt die SSO-Sessions für jeden Benutzer (Realm > Sessions, Realm > Realm settings > Sessions), als auch die Tokens für die Clients (Realm > Realm settings > Tokens).

URLs und Ports

Wichtige Keycloak-URLs - System:

  • User Account Console: /auth/realms/$REALM/account

Wichtige Keycloak-URLs - OpenID:

  • OpenID-Config: /realms/$REALM/.well-known/openid-configuration

  • Authorization-URL (Login): /auth/realms/$REALM/protocol/openid-connect/auth

  • Token-URL (Redeem): /auth/realms/$REALM/protocol/openid-connect/token

  • Introspection-URL: /auth/realms/$REALM/protocol/openid-connect/token/introspect

  • UserInfo-URL (Profile): /auth/realms/$REALM/protocol/openid-connect/userinfo

  • End-Session-URL: /auth/realms/$REALM/protocol/openid-connect/logout

  • JWKS-URI: /auth/realms/$REALM/protocol/openid-connect/certs

  • Check-Session-iFrame: /auth/realms/$REALM/protocol/openid-connect/login-status-iframe.html

Ports:

  • 8080/tcp

  • Admin-Console: 9990/tcp

User Federation

Externe IdPs

Stichwort: Identity Brokering, Federated Authentication

Benutzer können in externen und/oder Unternehmens-IdPs gespeichert und von Keycloak verwendet werden. Anfragen an Keycloak werden nach der Konfiguration eines Trust Relationship von diesem per Redirect an den externen IdP weitergeleitet. Der Ablauf:

  • Alice ruft eine Webseite auf, die eine Trust Relationship zu Keycloak als Identity Provider konfiguriert hat.

  • Alice gehört aber zu einer Firma, die ihren eigenen IdP verwendet, und die die Benutzerinformationen dort verwaltet.

  • Beim Aufruf sendet die Webseite Alices Authentication Request an Keycloak weiter, der ihre Credentials nicht kennt.

  • Keycloak leitet an Alices Firmen-IdP (den „Federated Identity Provider“) einen Sign-in-Request weiter, um sie dort zu authentifizieren.

  • Damit das funktioniert, wurde vorher eine Trust Relationship zwischen Keycloak und dem externen IdP in Keycloak konfiguriert.

  • Alice loggt sich am externen IdP ein.

  • Der externe IdP sendet ein von ihm signiertes Token1 an Keycloak. Keycloak prüft die Signatur von Token1.

  • Keycloak leitet mit einem von Keycloak erstellten Token2 zur Webseite um. Die Webseite prüft die Signatur von Token2.

Token1 und Token2 haben nichts miteinander zu tun. Token1 beinhaltet die Claims, um Alice zu identifizieren, und Token2 die Claims, damit die Webseite die korrekten Authentisierungs-Informationen erhält. Für die Übersetzung werden „Mapping Claims“ (bei SAML: „Mapping Assertions“) konfiguriert. Claims wie beispielsweise „mail“ und „user“ in Token1 werden konkret in Claims wie „username“ und „email“ in Token2 übersetzt (gilt auch für Roles).

LDAP per FreeIPA

Benutzer aus dem FreeIPA-LDAP synchronisieren. Im passenden Realm auf User Federation > Add provider > LDAP klicken. Dann:

  • Console Display Name: FreeIPA LDAP

  • Edit Mode: WRITABLE (falls die Benutzer ihr Passwort o.ä. per Keycloak ändern dürfen)

  • Vendor: Red Hat Directory Server

    • Username LDAP attribute: uid

    • RDN LDAP attribute: uid

    • UUID LDAP attribute: nsuniqueid

    • User Object Classes: inetOrgPerson, organizationalPerson

  • Connection URL: ldap://freeipa:389

  • Users DN: cn=users,cn=accounts,dc=linuxfabrik,dc=it

  • Bind Type: simple

  • Bind DN: cn=Directory Manager

Im Abschnitt „Sync Settings“ lässt sich die Art (Full vs. Partial) sowie die Häufigkeit der Synchronisation konfigurieren.

Wer LDAP-Gruppenzugehörigkeit auf Roles in Keycloak mappen möchte, erstellt unter „User Federation > LDAP > Mappers“ einen „role-ldap-mapper“, z.B. „mygroup-role-ldap-mapper“:

  • Name: mygroup-role-ldap-mapper

  • Mapper Type: role-ldap-mapper

  • LDAP Roles DN: cn=groups,cn=accounts,dc=linuxfabrik,dc=it (dort finden sich unter „member“ die zur Gruppe gehörenden Benutzer)

  • LDAP Filter: (cn=mygroup)

  • Nach dem Klick auf „Sync LDAP Groups To Keycloak“ taucht die gewünschte Gruppe im Keycloak-Realm in der obersten Ebene unter „Groups“ auf.

Realms

Realm einrichten

Ein Realm ist eine logische Gruppierung von

  • Benutzern

  • Clients (also Applikationen wie beispielsweise Nextcloud, Grafana usw.)

  • Rollen

  • UI-Themes

  • Identity Providers (SAML / OIDC)

  • User Storage Federations (LDAP / Kerberos / Custom DB)

Die Screenshots zeigen die Einrichtung eines Realms „example“ für eine zu schützende PHP-Applikation ohne eigene Authentifizierungs-Mechanismen (Client), die sich auf den OAuth-Client im Apache und Keycloak verlässt, und anschliessend die von Keycloak gelieferten Daten ausliest. In den Screenshots wird dem Realm die UUID „3323f2c6-5389-4bc3-9a25-a8d8ae64c6d4“ zugewiesen, die so auch in der Webserver/Reverse Proxy Konfiguration verwendet werden muss.

test.php
<h1>MyApp</h1>
<p>
    <?php
        echo 'Welcome, ' . $_SERVER['OIDC_CLAIM_email'];
        echo '<pre>';
        print_r($_SERVER);
        echo '</pre>';
    ?>
</p>
Neuen Realm hinzufügen:
../_images/keycloak-realm-01.png
../_images/keycloak-realm-02.png
../_images/keycloak-realm-03.png
../_images/keycloak-realm-04.png
Client (zu schützende Applikation) einrichten:
../_images/keycloak-realm-05.png

Der Tab „Credentials“ erscheint nur, wenn „Access Type: confidential“ gewählt wurde. Bedeutet, dass der OAuth-Client eine Client-ID und ein Secret verwenden muss.

../_images/keycloak-realm-06.png

„Secret“ notieren, wird für die Einrichtung der Webserver-vHosts benötigt.

Authentication Policies für die Benutzer festlegen:
../_images/keycloak-realm-07.png
../_images/keycloak-realm-08.png
../_images/keycloak-realm-09.png
Benutzer anlegen (sofern sie nicht aus einem LDAP stammen):
../_images/keycloak-realm-10.png
../_images/keycloak-realm-11.png
Event-Logging konfigurieren:
../_images/keycloak-realm-12.png

Testen eines Realms

Was liefert Keycloak auf Anfragen zurück? Kann auf jedem beliebigen Host ausgeführt werden:

# Setttings
KEYCLOAK_HOST=https://idp.example.com
KEYCLOAK_USERNAME=<Keycloak username>
KEYCLOAK_PASSWORD=<Keycloak password>
KEYCLOAK_REALM=<Keycloak realm name>
KEYCLOAK_CLIENT_SECRET=<Keycloak client secret>
KEYCLOAK_CLIENT_ID=<Client ID>

# Get token
TOKEN=$(curl --silent \
    --data="client_id=$KEYCLOAK_CLIENT_ID" \
    --data="client_secret=$KEYCLOAK_CLIENT_SECRET" \
    --data="username=$KEYCLOAK_USERNAME" \
    --data="password=$KEYCLOAK_PASSWORD" \
    --data="grant_type=password" \
    "$KEYCLOAK_HOST/auth/realms/$KEYCLOAK_REALM/protocol/openid-connect/token" | jq -r '.access_token')

# Use token to get userinfo
curl --silent \
    --header="Authorization: bearer $TOKEN" \
    $KEYCLOAK_HOST/auth/realms/$KEYCLOAK_REALM/protocol/openid-connect/userinfo | jq

2FA: One Time Passwords (OTP)

Konfiguriert sich ein Benutzer ein OTP (Nutzung per FreeOTP oder Google Authenticator) in auth/realms/$REALM/account/totp, codiert der QR-Code u.a.

  • TOTP (Time-based OTP)

  • Realm-Name

  • Benutzername

  • gemeinsames Secret: 32 stellig, alphanumerisch, wechselt bei jedem Seiten-Reload

  • OTP-Länge: genau 6 Zeichen

  • Algorithmus: SHA1

  • period - Wechsel des OTP nach: 30 Sekunden

Diese Default-Vorgaben lassen sich in Authentication > OTP Policy ändern. Unter Authentication > Required Actions > Configure OTP lässt sich einstellen, dass alle neuen Benutzer ein OTP verwenden müssen.

Hinweis: die Google Authenticator App auf Android hat Stand 2020-02 mit 8 statt 6 Zeichen und SHA256 statt SHA1 ein Problem; auf dem iPhone dagegen funktioniert der Google Authenticator tadellos. Mit FreeOTP klappt es dagegen immer und auf beiden Plattformen.

API

Keycloak ist eine „API-first“ Applikation, und kann daher vollautomatisiert werden.

Realm erstellen:

POST /auth/admin/realms/$REALM/clients HTTP/1.1
Host: $KEYCLOAK_HOST
Authorization: Bearer $ACCESS_TOKEN
Content-Type: application/json

{
    "clientId": "my-client"
    "enabled": true,
    "protocol": "openid-connect"
}

CLI

Mit einem Service-Client gegen das Admin REST-API authentifizieren. Wir verwenden hier keinen User, da TOTP auf der Kommandozeile eine Automatisierung verhindert:

/opt/keycloak/bin/kcadm.sh config credentials \
    --client keycloak-client \
    --realm master \
    --server http://localhost:8080

Client erstellen:

/opt/keycloak/bin/kcadm.sh create clients
    --target-realm $REALM \
    --set clientId=$CLIENT_ID \
    --set enabled=true \ll /etc/
    --set protocol=$PROTOCOL

Settings auslesen (im GUI finden sich diese zum Beispiel unter den Realm Settings > Sessions):

/opt/keycloak/bin/kcadm.sh get realms/$REALM \
    --fields sslRequired,resetPasswordAllowed,verifyEmail,revokeRefreshToken,ssoSessionIdleTimeout,ssoSessionMaxLifespan,browserSecurityHeaders.contentSecurityPolicy \
    --target-realm $REALM

Setting ändern, konfigurieren:

/opt/keycloak/bin/kcadm.sh update realms/$REALM \
    --target-realm $REALM \
    --set browserSecurityHeaders.contentSecurityPolicy="frame-src 'self'; frame-ancestors 'self'; object-src 'none';"

Realm Export:

/opt/keycloak/bin/kc.sh export --file /tmp/master.kc.json --realm master

Realm Importieren:

/opt/keycloak/bin/kc.sh import --file /tmp/realm.kc.json
systemctl restart keycloak

GUI

Realm Importieren:

Create realm > (Resource file) Browse… > Create

Upgrading

Unbedingt https://www.keycloak.org/docs/latest/upgrading/ nach allenfalls zutreffenden Breaking Changes oder anderen manuellen Anpassungen des Versionssprungs durchsuchen. Darüber hinaus ist ein Upgrade recht simpel:

  • Keycloak stoppen.

  • Backup der Datenbank anlegen (z.B. per systemctl start mariadb-dump.service).

  • Backup des conf/-Directory anlegen.

  • Neues Keycloak-Release herunterladen und entpacken. Dies kann entweder manuell oder per Ansible (linuxfabrik.lfops.keycloak-Rolle, Variable keycloak__version anpassen) gemacht werden.

  • Das gesicherte conf/-Directory der alten Installation in die Neue kopieren.

  • Keycloak starten.

  • Keycloak wird (falls nicht anders konfiguriert) automatisch die Datenbank-Schemas migrieren. Ist die Datenbank zu gross, werden einige Indices nicht automatisch angelegt; es gibt dann Warnungen mit den entsprechenden, auszuführenden SQL-Kommandos im Keycloak-Log.

Best Practices

  • Niemals im master-Realm arbeiten. Der master-Realm ist für die Verwaltung von Keycloak selbst reserviert. Für jede Applikation oder Organisation einen eigenen Realm anlegen.

  • HTTPS auf Keycloak erzwingen. In Keycloak 17+ muss KC_HOSTNAME gesetzt sein und KC_HTTP_ENABLED bleibt false (Default). Zugang per unverschlüsseltem HTTP darf nur hinter einem Reverse-Proxy mit TLS laufen (KC_PROXY_HEADERS=xforwarded oder forwarded).

  • Separate Admin-Benutzer pro Person. Das initiale Admin-Konto aus KC_BOOTSTRAP_ADMIN_USERNAME nach dem ersten Login deaktivieren und für jede administrierende Person einen personalisierten Account anlegen - sonst bleibt der Audit-Trail nutzlos.

  • Brute-Force-Detection einschalten. In Authentication Policies Brute Force Detection aktivieren. Default: lock after 30 failures, 15 Minuten.

  • 2FA erzwingen für Admin-Rollen. In der Realm-Settings → Authentication → Required Actions die OTP-Aktivierung erzwingen; Conditional OTP in Flows nutzen, um 2FA nur für privilegierte Nutzer zu verlangen.

  • Realm-Keys rotieren. Realm Settings Keys Providers: neuen Provider mit höherer Priority anlegen, alte Keys nach Ablauf der Token-Lifetime deaktivieren.

  • Backup der Keycloak-DB in regelmässigen Abständen. Die Realm-Konfiguration lebt primär in der Datenbank; das conf/keycloak.conf enthält nur Infrastruktur-Settings. Für zusätzliche Sicherheit kann man Realms zusätzlich als JSON exportieren (kc.sh export --realm <name>).

  • Externe User Federation (LDAP/AD) bevorzugen statt Benutzer direkt in Keycloak anzulegen, wo ein zentrales Directory existiert. Die Keycloak-interne Benutzer-DB ist für kleinere Tenants OK, aber die Synchronisation mit LDAP ist reifer und besser integriert.

RHEL/Debian-Unterschiede

Offizielles Paket

Keycloak wird vom Upstream-Projekt nicht als RPM oder DEB ausgeliefert. Release-Bundles (tar.gz) gibt es auf https://www.keycloak.org/downloads. Für Deployments auf RHEL-Systemen empfiehlt die Linuxfabrik den Container-Weg (Podman/Docker) oder das manuelle Auspacken des Bundles nach /opt/keycloak/. Auf beiden Plattformen praktisch identisch.

Red Hat Build of Keycloak (RHBK)

Red Hat verkauft unter dem Namen „Red Hat Build of Keycloak“ einen kommerziellen Support für Keycloak und liefert dort auch zertifizierte Container-Images über das Red Hat Container Registry aus. Nur auf RHEL verfügbar, subscription-gebunden.

Systemd-Unit

Bei Bare-Metal-Installationen muss auf beiden Plattformen eine eigene systemd-Unit geschrieben werden (Beispiel: siehe Ansible-Rolle linuxfabrik.lfops.keycloak). Keycloak bringt keine distributionsspezifischen Units mit.

Troubleshooting

# Keycloak <= v15 (WildFly, legacy)
tail --follow /opt/keycloak/standalone/log/server.log

# Keycloak v17+ (Quarkus): logs go to stdout by default.
# In a container: `podman logs --follow keycloak` or `journalctl`.

Werden die richtigen Header gesendet, z.B. vom Reverse Proxy? Keycloak beenden und mit nc einen Service auf Port 8080 simulieren, damit man die eingehenden Anfragen an den vermeintlichen Keycloak-Server sieht und auswerten kann.

nc -l 8080

Ergibt z.B.:

GET /auth/realms/example/protocol/openid-connect/auth?response_type=code&scope=openid%20email&client_id=example&state=HcKXBTixVaDEPkm_64sfFr8lvTo&redirect_uri=https%3A%2F%2Fexample.linuxfabrik.ch%2F%2A&nonce=ZdCA4AEt9BmDvPEew_BGCvvcWNwfB1vli77embyRxkw HTTP/1.1
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng, */*;q=0.8
Connection: Keep-Alive
Cookie: AUTH_SESSION_ID=3d0208c1-80...6dd4821b36.myserver; KEYCLOAK_IDENTITY=eyJhbGc...-zYQ; KEYCLOAK_SESSION=example/cb49c45b-...f-460a3504ca02/3d0208c1-80...736dd4821b36
Host: 192.168.122.235:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36
X-Forwarded-For: 192.168.26.1
X-Forwarded-Host: 192.168.122.235:8080
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: 192.168.122.235:8080
Failed to send email javax.mail.MessagingException: Could not convert socket to TLS; nested exception is: javax.net.ssl.SSLException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty

Als erstes versuchen, den Keycloak-Service neuzustarten, danach mit dem Debugging anfangen.

kc.sh export: quarkus.datasource.jdbc.driver is set to 'org.mariadb.jdbc.MySQLDataSource' but it is build time fixed to 'org.h2.jdbcx.JdbcDataSource'.

Als Workaround folgende Environment Variablen setzen:

export KC_DB=mariadb
export KC_DB_URL="jdbc:mariadb://localhost:3306/keycloak"
export KC_DB_USERNAME="root"
export KC_DB_PASSWORD="linuxfabrik"

Falls ein Import nicht funktioniert, kann ein „Dump & Restore“ versucht werden:

# on the old server
systemctl stop keycloak.service
mariadb-dump --user root --password --default-character-set=utf8mb4 keycloak --result-file=/tmp/keycloak_dump.sql
systemctl start keycloak.service

# on the new server, after running a new setup_keycloak ansible playbook
systemctl stop keycloak.service
mariadb --user root --password
mariadb> DROP DATABASE keycloak;
mariadb> CREATE DATABASE keycloak CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
mariadb> exit
mariadb --user root --password --default-character-set=utf8mb4 keycloak
mariadb> SET names 'utf8';
mariadb> SOURCE keycloak_dump.sql;
mariadb> exit
systemctl start keycloak.service

Setup Best Practises

Hostnames konfigurieren („Base URL“):

  • Frontend: Alle Keycloak-Nodes unter einer Public URL gruppieren („Frontend URL“ setzen)

  • Backend Endpoints zwischen Keycloak und Clients: Betrifft Token Endpoints, User info und JWKS (JSON Webkey Set). Müssen ebenfalls Public URLs sein, und können auf der Frontend URL basieren (ist der Default).

  • Administration: Die Admin-Konsole sollte nur intern zugreifbar sein.

Availability:

  • Keycloak-Instanzen als Cluster deployen. Keycloak nutzt Infinispan (analog Redis) als verteilte Cache-Instanz, um die Nodes im Cluster zu synchronisieren.

  • Keycloak sollte nur hinter einem ReverseProxy zugreifbar sein, der zusätzlich als Load Balancer agiert. Der ReverseProxy sollte TLS terminieren, und neu encrypten. Der ReverseProxy muss alle "X-Forwarded-*" überschreiben und an Keycloak weiterleiten. Session Affinity (Sticky Sessions) konfigurieren.

TLS:

  • TLS in Keycloak aktivieren, per Zertifikat in PEM-Format, oder per Java Keystore. Die Kommunikation zwischen ReverseProxy und Keycloak sollte nur verschlüsselt erfolgen.

Datenbank:

  • Die interne H2-Datenbank sollte auf MariaDB, MySQL oder eine andere umgestellt werden.

  • Setting db-pool-max-size in Produktion nach oben anpassen (Default: 100, evtl. 150 oder mehr).

  • Setting db-pool-min-size auf einen Wert wie 10 setzen, da das Erzeugen von DB-Connections teuer ist.