Single Sign On in einer Organisation wird wahrscheinlich nicht ohne eine zentrale Instanz gehen, die Authentifizierungsanfrage an das jeweilige bestandsführende System weiterreicht. In diesem Posting beschreibe ich das Zusammenspiel zwischen einem zentralen Identity Broker und einem Identity Provider. Für die Umsetzung habe ich die Keycloak
für sowohl den Broker, als auch den Provider eingesetzt.
- Identity Provider:
Ist eine Anwendung, oder System, das Informationen zu einer Identität verwaltet. Es ist das bestandsführende System für Benutzerinformationen in einem bestimmten Zusammenhang. Dort können Identitäten angelegt und verwaltet werden. Löschen ist prinzipiell möglich, aber für Revisionsfähigkeit und Sperrlisten nicht zielführend.
Vor allem Social Identity Provider kommen immer mehr zum Einsatz. Das sind zum Beispiel facebook, GitHub, google, Microsoft, um nur einige zu nennen. - Identity Broker:
Ein Intermediär, der die Verbindung zu dem jeweiligen Identity Provider in dem bestehenden Kontext herstellt.
Der Intermediär, Vermittler, kümmert sich um die Authentifizierung, oder Autorisierung, indem er einen vom Benutzer ausgewählten Identity Provider verwendet. Der Benutzer kann die im Broker hinterlegten Ziele in diesem definierten Zustand, Kontext, verwenden. Eine wiederholte Anmeldung durch den Benutzer muss nicht mehr erfolgen, darum kümmert sich der Intermediär. Der Benutzer muss sich solange nicht neu ausweisen, authentifizieren, solange die vom Benutzer verwendete Identität über die entsprechenden Berechtigungen in dem aktuellen Sitzungszustand verfügt.
Konkreter Anwendungsfall
Zwei lokale Instanzen von Keycloak, eine Instanz für externe Benutzer und eine Instanz für interne Benutzer. Interne Benutzer sollen auf externe Ressourcen zugreifen können, ohne dafür ein neues Konto anlegen zu müssen. In diesem Anwendungsfall wird die externe Instanz zum Identity Broker und die interne Instanz zum Identity Provider.
Konfiguration
Generell
- Die externe Keycloak Instanz ist auf den lokalen Netzwerkadapter und Anschluss 8081 gebunden – 127.0.0.1:8081
- Die interne Keycloak Instanz ist ebenfalls auf den lokalen Netzwerkadapter gebunden, aber auf den Anschluss 8080 – 127.0.0.1:8080
Keycloak Extern
Alle Anschlüsse werden um den Faktor eins erhöht.
<socket-binding-group name=“standard-sockets“ default-interface=“public“ port-offset=“${jboss.socket.binding.port-offset:0}“> | |
<socket-binding name=“management-http“ interface=“management“ port=“${jboss.management.http.port:9991}“/> | |
<socket-binding name=“management-https“ interface=“management“ port=“${jboss.management.https.port:9994}“/> | |
<socket-binding name=“ajp“ port=“${jboss.ajp.port:8010}“/> | |
<socket-binding name=“http“ port=“${jboss.http.port:8081}“/> | |
<socket-binding name=“https“ port=“${jboss.https.port:8444}“/> | |
<socket-binding name=“txn-recovery-environment“ port=“4713“/> | |
<socket-binding name=“txn-status-manager“ port=“4714“/> | |
<outbound-socket-binding name=“mail-smtp“> | |
<remote-destination host=“localhost“ port=“25″/> | |
</outbound-socket-binding> | |
</socket-binding-group> |
Keycloak Intern
Behält die Standardkonfiguration.
Nginx
Für die Kommunikation und Erreichbarkeit sorgt eine lokale Nginx Instanz. In der Konfiguration wurde folgende Änderung vorgenommen.
location /extern/ {
proxy_pass http://localhost:8081/auth/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port 80;
}
location /intern/ {
proxy_pass http://localhost:8080/auth/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port 80;
}
Keycloak Startup Script Interne Instanz
Um den jeweiligen Kontext mitzugeben – woher kommt der Benutzer – gebe ich einer Keycloak Instanz in einem Startskript eine eigene Kontextdefinition mit. Die zweite Instanz, ohne Startskript, kommt ohne eigene Kontextdefinition aus und ist die Standard Instanz für Berechtigungen. Das Skript selber kommt in das Verzeichnis /opt/jboss/startup-scripts und in diesem Fall hat es den Namen webContext.cli.
set WEB_CONTEXT=${env.WEB_CONTEXT:auth}
set KEYCLOAK_CONFIG_FILE=${env.KEYCLOAK_CONFIG_FILE:standalone-ha.xml}
set JBOSS_HOME=${env.JBOSS_HOME}
echo Setting web-context to $WEB_CONTEXT in $JBOSS_HOME/\
standalone/configuration/$KEYCLOAK_CONFIG_FILE
embed-server --server-config=$KEYCLOAK_CONFIG_FILE —std-out=echo/
\subsystem=keycloak-server/:write-attribute(name=web-
\context,value=$WEB_CONTEXT)
stop-embedded-server
Bereichsdefinitionen (aka Realm)
- Realm: „keycloak-external-broker“ auf der Instanz Extern (127.0.0.1:8081)
- Realm: „keycloak-internal-provider“ auf der Instanz Intern (127.0.0.1:8080)
Identity Provider
Auf der externen Instanz wird ein Identity Provider registriert. Hierzu reicht es einen Alias und einen Anzeigennamen zu vergeben und die OpenID Verbindung einzurichten. Identity Provider ist als eigener Menüpunkt in der Seitenleiste aufgeführt.
- Alias: keycloak-internal
- Anzeigename: Interner Login
- OpenID Authorization URL: http://127.0.0.1:8080/auth/realms/keycloak-internal-provider/protocol/openid-connect/auth
- Client ID: Broker
- Client Secret kopieren wir von der internen Instanz. (Am besten das Fenster einfach offen lassen und für die interne Instanz ein neues Fenster öffnen und dann kopieren …)
Speichern und zur internen Instanz wechseln.
Broker Client
Auf der internen Instanz den Menüpunkt Clients auswählen. Das Feld „Valid Redirect URL“ anpassen. Das Schema ist:
http://127.0.0.1:8081/auth/realms/broker/keycloak-external-broker/broker/keycloak-internal-provider/endpoint. Auf dem zweiten Reiter, Credentials, kann das „Shared Secret“ erstellt und kopiert werden für die externen Identity Provider.
Auf der internen Instanz muss zumindest ein Benutzer noch angelegt werden.
Damit ist alles erledigt. Bei einem Anruf der Login Seite der externen Instanz – 127.0.0.1:8081 – ist das Login Fenster nun zweigeteilt. Es gibt einen eigenen Abschnitt „Interner Login“. Wenn der interne Login aufgerufen wird, dann wird zum Internen Identity Provider weitergeleitet und dort kann sich der Benutzer dann anmelden.
Deshalb wurde auch die Lösung über einen Reverse Proxy gewählt, der alle externen Requests abhandelt. Damit ist die Angriffsfläche kleiner und die Anfragen an sich können, da unverschlüsselt, sehr einfach gefiltert werden. Nach aussen hin kann mit einer Verschlüsselung gearbeitet werden. Nicht besprochen wurde hier das Thema der Namensauflösung. Die muss nebenbei auch noch beachtet werden.