Keycloak
Siehe auch
Apache: mod_auth_openidc
OAuth2-Proxy: https://github.com/oauth2-proxy/oauth2-proxy, https://oauth2-proxy.github.io/oauth2-proxy/docs/
- Ansible-Rolle Keycloak:
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 FreeOTP)
Identity Providers (OpenID Connect oder SAML 2.0): GitHub, Twitter, Facebook, …
User Federation über Kerberos (AD) oder LDAP
Templating (Layout, E-Mails etc.)
Wird hier als Standalone Server beschrieben, nicht in der geclusterten Variante. Benötigt ca. 300 MB auf der Festplatte.
Die sogenannte „Overlay“ Download-Variante von Keycloak kommt ohne WildFly-Server daher und wird auf einem bestehenden als Add-On installiert. Von der Community nicht supportet, falls auf diesem noch weitere Applikationen laufen.
Keycloak beherrscht
OIDC (OpenID Connect, also OAuth 2.0 plus Login- und Profil-Informationen)
SAML 2.0
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:
Die Web-App (genauer: ein OAuth-Client) leitet den Benutzer auf Keylcoak um.
Der Benutzer authentifiziert sich mit seinen Keycloak-Credentials.
Der Benutzer wird mit einem Token auf die Web-App umgeleitet.
Die Web-App bekommt einen Access-Token zugewiesen.
Mit Hilfe des Access-Tokens kann die Web-App weitere Benutzerdaten abfragen.
Entweder bringt die Web-App einen OAuth-Client mit (wie z.B. Grafana - bevorzugter Lösungsansatz), 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 HAProxy Apache AnyRevProxy
| | | |
| :4180 | Web-App +
| oauth2-proxy | OAuth-Client
| | | |
Keycloak <------+---------------+------------+-----------+
| | |
Web-App Web-App Web-App
Wichtige Keycloak-URLs - System:
User Account Console: /auth/realms/$REALM/account
Wichtige Keycloak-URLs - OpenID:
OpenID-Config: /auth/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
Breaking Changes:
Keycloak 17+: Der mitgelieferte Applikationsserver ist jetzt nicht mehr WildFly/Undertow, sondern Quarkus/Vertx, was logischerweise eine Reihe von Änderungen bei der Konfiguration von Keycloak und der Bereitstellung von benutzerdefinierten Providern mit sich bringt. Der Support für WildFly-basierte Keycloak-Systeme endet im Juni 2022.
Keycloak 16.0.0: Kommt mit WildFly 25. WildFly 25 wirft das alte Sicherheitssubsystem über Bord, das unter anderem für die Konfiguration von TLS verwendet wurde. Aufgrund des Umfangs der Änderungen gibt es nur den Weg, mit der Standardkonfiguration von Keycloak 16 zu beginnen und die relevanten Änderungen vorzunehmen, anstatt die Konfigurationsdateien von früheren Versionen von Keycloak zu kopieren.
- Links
Installation bis v16 (WildFly-based)
yum -y install wget java
VER=16.0.0
cd /tmp
wget https://github.com/keycloak/keycloak/releases/download/$VER/keycloak-$VER.tar.gz
tar xvzf keycloak-$VER.tar.gz
mv keycloak-$VER /opt
cd /opt
ln -s keycloak-$VER keycloak
Keycloak soll auf jedem Interface hören, und Node-ID ändern:
cp /opt/keycloak/standalone/configuration/standalone.xml /opt/keycloak/standalone/configuration/standalone.xml.orig
sed --in-place --expression 's,inet-address value="${jboss.bind.address.management:127.0.0.1}",any-address,g' /opt/keycloak/standalone/configuration/standalone.xml
sed --in-place --expression 's,inet-address value="${jboss.bind.address:127.0.0.1}",any-address,g' /opt/keycloak/standalone/configuration/standalone.xml
NODE_ID=$(( ( RANDOM % 100000 ) + 1 ))
sed --in-place --expression "s,jboss.tx.node.id:1,jboss.tx.node.id:$NODE_ID,g" /opt/keycloak/standalone/configuration/standalone.xml
Benutzer anlegen, unter dem später der integrierte WildFly-Server laufen soll:
useradd --shell /sbin/nologin keycloak
chown -R keycloak:keycloak /opt/keycloak*
Bemerkung
Die WildFly-Konfigurationsdatei sollte nicht im laufenden Betrieb geändert werden - WildFly könnte sie überschreiben. Statt dessen verwendet man zur Laufzeit dessen CLI.
Test-Start:
cd /opt/keycloak
bin/standalone.sh
Systemd-Unit-File:
[Unit]
Description=The Keycloak Server
After=syslog.target network.target
Before=httpd.service
[Service]
Type=idle
User=keycloak
Group=keycloak
ExecStart=/opt/keycloak/bin/standalone.sh -b 0.0.0.0
TimeoutStartSec=180
TimeoutStopSec=180
# Java programs sometimes don't send back the expected exit status when shutting down in response to SIGTERM.
# Adding the following fixes the problem:
SuccessExitStatus=143
LimitNOFILE=102642
StandardOutput=null
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable keycloak
systemctl start keycloak
Admin-Account im Keycloak (nicht im WildFly) für den Zugriff auf die Weboberfläche (Default: http://keycloak:8080/auth) anlegen:
cd /opt/keycloak
bin/add-user-keycloak.sh --user keycloak-admin
systemctl restart keycloak
Bei Bedarf noch Benutzer für den WildFly (nicht in Keycloak) anlegen, um beispielsweise WildFly direkt überwachen zu können:
cd /opt/keycloak
bin/add-user.sh
systemctl restart keycloak
Anschliessend http://keycloak:8080/auth aufrufen, in der „Administration Console“ einloggen und das eigene Profil des Admin-Accounts konfigurieren.
Upgrade
Achtung
Keycloak 16 kommt mit WildFly 25, bei dem sich fundamental Änderungen in der Konfiguration ergeben haben. Ein Upgrade von Keycloak x auf Keycloak 16 muss daher nach https://www.keycloak.org/docs/latest/upgrading/#wildfly-25-upgrade durchgeführt werden: We recommend that rather than copying configuration files from previous versions of Keycloak that you start with the default configuration files provided in Keycloak 16 and apply the relevant changes.
Die Datenbankstrukturen werden automatisch aktualisiert, sofern in der standalone.xml
der Wert migrationStrategy
auf update
steht (was dem Default-Verhalten entspricht).
OLD=16.0.0
NEW=16.1.0
cd /tmp
wget https://github.com/keycloak/keycloak/releases/download/$NEW/keycloak-$NEW.tar.gz
tar xvzf keycloak-$NEW.tar.gz
mv keycloak-$NEW /opt
systemctl stop keycloak
rm -rf /opt/keycloak/standalone/data/tx-object-store/*
unlink /opt/keycloak
cd /opt
ln -s keycloak-$NEW keycloak
\cp -a /opt/keycloak-$OLD/standalone/* /opt/keycloak/standalone/
cd /opt/keycloak
# for running DB migration and seeing output:
bin/jboss-cli.sh --file=bin/migrate-standalone.cli
bin/standalone.sh
systemctl start keycloak
Siehe http://www.keycloak.org/docs/latest/upgrading/index.html
Keycloak hinter einem Reverse Proxy
Der Reverse Proxy muss das ursprünglich angeforderte Protokoll durchreichen.
Nicht vergessen:
im Apache vHost:
RequestHeader set X-Forwarded-Proto "https"
auf dem HAProxy: im Keycloak-Backend
http-request add-header X-Forwarded-Proto https
Keycloak selbst muss wie folgt konfiguriert werden:
<subsystem xmlns="urn:jboss:domain:undertow:12.0" ...>
...
<server name="default-server">
...
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"
proxy-address-forwarding="true"/>
...
</server>
...
</subsystem>
SQL-Datenbank Backend
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 de lokalen Admin-Maschine per sshfs ausführen - es wird ein Browser gestartet).
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 bezeichnet in der Regel eine Organisation (z.B. Linuxfabrik), in der verschiedene Clients (also Applikationen wie beispielsweise Nextcloud, Grafana usw.) mit unterschiedlichen Methoden (SAML, OIDC) konfiguriert werden. Ein Realm verwaltet zudem eine User-Base.
Die Screenshots zeigen die Einrichtung eines Realms „example“ für eine zu schützende Client-Applikation (PHP), die sich auf den OAuth-Client im Apache verlässt. 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.
Im Beispiel stellvertretend für eine Applikation ohne eigene Authentifizierungs-Mechanismen ein „dummes“ PHP-Skript, welches sich bei der Authentifizierung auf Keycloak verlässt, aber die von Keycloak gelieferten Daten ausliest:
<h1>MyApp</h1>
<p>
<?php
echo 'Welcome, ' . $_SERVER['OIDC_CLAIM_email'];
echo '<pre>';
print_r($_SERVER);
echo '</pre>';
?>
</p>
- Neuen Realm hinzufügen:
- Client (zu schützende Applikation) einrichten:
- Authentication Policies für die Benutzer festlegen:
- Benutzer anlegen (sofern sie nicht aus einem LDAP stammen):
- Event-Logging konfigurieren:
Testen eines Realms
Was liefert Keycloak auf Anfragen zurück? Kann auf jedem beliebigen Host ausgeführt werden:
# Setttings
KEYCLOAK_HOST=https://idp.linuxfabrik.ch
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 -s \
-d "client_id=$KEYCLOAK_CLIENT_ID" \
-d "client_secret=$KEYCLOAK_CLIENT_SECRET" \
-d "username=$KEYCLOAK_USERNAME" \
-d "password=$KEYCLOAK_PASSWORD" \
-d "grant_type=password" \
"$KEYCLOAK_HOST/auth/realms/$KEYCLOAK_REALM/protocol/openid-connect/token" | jq -r '.access_token')
# Use token to get userinfo
curl --silent \
-H "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.
Troubleshooting
tail -f /opt/keycloak/standalone/log/server.log
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.