10. September 2020 | 9 min lesezeit

Secured Microservices mit MicroProfile und Keycloak

Euer Service soll nur authentifizierte Anfragen beantworten oder nur Aufträge von vertrauenswürdiger Stelle annehmen? – Was in gängigen Monolithen mit „einfachen“ Zugangsbeschränkungen gelöst werden konnte, muss im Microservice Umfeld anders und feingranularer gedacht werden. Das neue MicroProfile 3.3 bietet hier Ansätze zum Thema verteile Sicherheit von Microservices, die wir Dir im Folgenden praktisch näherbringen wollen.

Was bedeutet Sicherheit?

Generell unterteilen wir Sicherheit in drei Bereiche, die Identitätsprüfung, die Feststellung der Berechtigungen und die Zugriffskontrolle.

Mit der Identitätsprüfung wollen wir feststellen, wer der aktuelle Nutzer ist und ob er der ist, der er angibt zu sein. Die Überprüfung erfolgt in der Regel mit der Übermittlung von Nutzername und Passwort (ggf. ergänzt um einen zweiten Faktor) oder einem Secret.

Mit der Feststellung der Berechtigungen wird ermittelt, was der Nutzer darf: Also welchen Gruppen oder Rollen gehört er an oder welche Rechte besitzt der Nutzer?

Die Zugriffskontrolle steuert, ob der Nutzer die Anwendung benutzen darf, oder welche Teile der Anwendung erlaubt sind. Dies schließt die einzelnen Methoden der verfügbaren Microservices mit ein.

REST (eigentlich der Http-Status) kennt den Unterschied zwischen Identitätsprüfung und Feststellung der Berechtigungen: 401 Unauthorized bedeutet, dass die Prüfung der Identität nicht erfolgreich war. 403 Forbidden sagt aus, dass der Nutzer nicht die benötigten Berechtigungen besitzt.

Was bedeutet Single Sign-on (SSO)?

Üblicherweise werden Anwendungen seit geraumer Zeit über eine einheitliche Anmeldung angebunden, so dass nicht jede Anwendung selbst die Überprüfungen durchführen muss. Dazu leitet die Anwendung den Nutzer beim ersten Aufruf an einen Identitätsprovider weiter, bei dem durch z.B. Anmeldung mit Nutzername und Passwort die Überprüfung erfolgt. Ist die Überprüfung erfolgreich, wird der Nutzer wieder zur Anwendung zurückgeschickt, diesmal allerdings mit Überprüfungsmerkmal.

Im Detail gibt es bei der Art der Überprüfung unterschiedliche Ansätze. Wir betrachten aber nur die Ansätze von SAML, OAuth und OpenID Connect, die in Enterprise Anwendungen weit verbreitet sind, und erläutern sie im Folgenden kurz:

Das XML basierte Framework SAML stellt mit Hilfe von X.509 Zertifikaten sicher, dass die Authentifizierungs- und Autorisierungsinformationen signiert und wenn gewünscht auch verschlüsselt übermittelt werden können. Der Identitätsprovider kann nur von definierten Services (Serviceprovider) angesprochen werden. Da die Menge der Provider bekannt ist, wird jeder über eine Abmeldung des Nutzers informiert.

OAuth 2.0 ist ein tokenbasiertes Protokoll. Dazu erhält der Nutzer nach der Identitätsprüfung einen Zugangstoken, mit dem die Anwendung überprüfen kann, ob und wozu der Nutzer berechtigt ist. Weiter gibt es einen zweiten Token, den Erneuerungstoken. Dieser hat eine längere Gültigkeit und ermöglicht nach Ablauf des Zugangstoken einen weiteren anzufragen und das ohne erneute Identitätsprüfung.

Die Authentifizierungsschicht OpenID ist als Aufsatz auf dem OAuth Protokoll zu verstehen, das HTTP-Programmierschnittstellen mit REST-Mechanismen im JSON Datenformat spezifiziert. Als Token wird der JSON Web Token genutzt, in dessen frei definierbaren Datenfeldern die Nutzlast zu finden ist. Diese wird als Base64 verpackt, kann aber zusätzlich vorher signiert und verschlüsselt werden. Das String-Ergebnis besteht aus drei Bereichen, die jeweils durch einen Punkt getrennt sind. Der erste Teil definiert unter anderem die genutzten Signatur- und Verschlüsselungsmethoden, der zweite Bereich bildet die signierte und ggf. verschlüsselte Nutzlast und der letzte Teil enthält die zur Signatur- und Verschlüsselungsprüfung genutzten öffentlichen Schlüsseldaten. Der Token wird im Header-Feld Authorization des Http-Requests übertragen und sieht wie folgt aus: Bearer<Leerzeichen><Token als Base64>.

Wie geht das jetzt mit MicroProfile Microservices und Keycloak?

Die Authentifizierung und Autorisierung im MicroProfile ist auf Basis von OpenID Connect mit JSON Web Token spezifiziert. Der Token muss im Standard auch signiert werden, dabei wird das Signierungs- und Verschlüsselungsverfahren RSASSA-PKCS1-v1_5 SHA-256 (kurz: RS256) erwartet, ebenso ist ein Teil der Datenfelder festgelegt (siehe folgende Aufzählung), die um beliebige eigene Felder ergänzt werden können.

Minimal erwartete MP-JWT Pflichtfelder:

Feld Beschreibung
typ Tokenformat
alg Signierungs- und Verschlüsselungsverfahren
kid Schlüsselidentifikator
iss Tokenaussteller
sub Nutzeridentifikator
exp Ablaufzeitpunkt
iat Ausstellzeitpunkt
jti Tokenidentifikator
upn “user principal name” für das Interface “java.security.Principal”
groups Rollen des Nutzers

Im Feld „groups“ werden die Rollen des Nutzers aufgelistet, die für die rollenbasierte Zugangskontrolle benötigt werden.

Vorgehen im Keycloak:

Im Keycloak wird ein Nutzerkreis (Realm) angelegt und darin die erlaubte Anwendung konfiguriert (Client). Für die Anwendung muss noch ein sogenannter Token-Mapper angelegt werden, der die Gruppen, Realm-Rollen oder Client-Rollen des Nutzers in das Feld „groups“ des Zugangstoken legt. Weitere benutzerdefinierte Felder können über entsprechende Token-Mapper befüllt werden. In diesem Fall müssen die benutzerdefinierten Felder bei der Anlage/Bearbeitung des Nutzers hinzugefügt werden.

Vorgehen im MicroProfile Microservice:

Im Microservice wird in der Rest-Application Implementierung die Annotation @LoginConfig(authMethod = „MP-JWT“) angefügt. Zusätzlich müssen folgende Konfigurationen in der „microprofile-config.properties“ angelegt werden:

Parameter Beschreibung Erforderlich
„mp.jwt.verify.publickey“ Dateiname des öffentlichen Keycloak Zertifikat Schlüssels (z.B. „publicKey.key“) publickey oder publickey.location erforderlich
„mp.jwt.verify.publickey.location“ URL der Keyclaok Zertifikat Schnittstelle (z.B. „http://localhost:8282/auth/realms/MicroProfile/protocol/openid-connect/certs“) publickey oder publickey.location erforderlich
„mp.jwt.verify.issuer“ Die URL der Keycloak Anmeldeschnittstelle (z.B. „http://localhost:8282/auth/realms/MicroProfile“) erforderlich

Werden die Schnittstellen von mehr als einer Webanwendung genutzt, ist darauf zu achten, dass der Microservice nur mit genau einem „Realm“ arbeiten kann. Dürfen einzelne Anwender nur auf eine Anwendung zugreifen, so muss dies auf der Ebene der Anwendung oder im Keycloak sichergestellt werden.

Nun kann an jedem Rest-Endpunkt mit der Annotation @RoleAllowed({„ROLLE“}) die Tokenprüfung (inkl. Rollenzugehörigkeit) aktiviert werden.

Um sicherzustellen, dass keine neue Schnittstelle versehentlich ohne Berechtigung erreichbar ist, sollte an der Resourcen-Klasse die Annotation @DenyAll geschrieben werden, da dann alle Anfragen abgelehnt werden, die nicht explizit per Annotation (z.B. @RoleAllowed({„ROLLE_1“, „ROLLE_2“})) erlaubt sind.

Vorgehen bei der Webanwendung (React-Application):

In der Webanwendung empfiehlt sich ein Keycloak Javascript Client (z.B. für React), der die Abfragen Richtung Keycloak übernimmt. Benötigt wird ein solcher Client allerdings nicht. Nach dem Laden der Anwendung sollte immer geprüft werden, ob ein Token mitgeschickt wurde und dieser noch gültig ist. Ist der Token abgelaufen oder fehlt ganz, so muss der Request zum Keycloak-Server weitergeleitet werden, an dem sich der Nutzer mit seinen Daten anmelden muss. Nach erfolgreicher Anmeldung wird der Anwender mit einem gültigen Token zur Webanwendung zurückgeleitet.

Bei der Verwendung des Keycloak-Clients wird eine keycloak.json Datei benötigt, deren Werte aus dem Keycloak-Server kopiert werden können. Die Datei wird dann im Webanwendungsverzeichnis gespeichert.

Im Token sind alle Rollen enthalten, zu denen der Anwender gehört, so dass eine weitere Prüfung der Berechtigungen gegen ein weiteres System nicht notwendig wird. Ansichten oder Aktionen der Anwendung können nun dem Anwender anhand seiner Berechtigungen zugängig gemacht werden. Das im Token befindliche Ablaufdatum muss natürlich ebenfalls berücksichtigt werden. Sollte der Token abgelaufen sein, so kann er verlängert werden, solange die Nutzer-Session im Keycloak noch nicht abgelaufen ist.

Anfragen der Webanwendung in Richtung Microservices erfolgen immer mit einem gültigen Token.

Unser Beispiel auf GitHub für das bessere Verständnis:

Unter folgendem GitHub-Link (https://github.com/openknowledge/secure-microservice) finden Sie ein einfaches Beispiel, bestehend aus einem Keycloak-Server, einer einfachen React-Webanwendung und einem Microservice (mit zwei rollenbasierten Schnittstellen).

Nach erfolgreichem Checkout aus dem Quellcode-Verzeichnis kann die Anwendung per „start.sh“ oder „start.bat“ gebaut, gepackt und gestartet werden.

Unter folgenden URLs ist die Anwendung erreichbar:

URL Anwendung Zugangsdaten (Nutzername -:- Passwort)
http://localhost:8282/

auth

Keycloak Console admin -:- keycloak
http://localhost:8200/ MailCatcher none
http://localhost:8000/ Webanwendung test.user@domain.tld -:- Test1234,

test.admin@domain.tld -:- Test1234

http://localhost:8080/

secure/message/user

GET – Schnittstelle für User-Message Token: erforderlich

Rolle: USER

http://localhost:8080/

secure/message/admin

GET – Schnittstelle für Admin-Message Token: erforderlich

Rolle: ADMIN

http://localhost:8080/

secure/openapi-ui

Visualisierung der openapi Doku none

Was hat die Anwendung unter der Haube?

Im folgenden Abschnitt wird die Beispiel-Anwendung näher erklären. Vorab: die Anwendung benötig folgende Software auf eurem System: Java 11, Maven 3.6, Node, NPM und Docker (mit Docker-Compose).

Das Beispiel ist so konzipiert, dass alle Komponenten im selben Docker-Kontext untereinander erreichbar sind, so dass die Kommunikation untereinander möglich wird. Wer sich auskennt kann natürlich auch alles im lokalen Kontext per Hand starten.

Keycloak unter der Haube

Um das Beispiel einfach zu halten, wird die Konfiguration des Keycloak-Servers per Skript beim Start der Anwendung durchgeführt. Im Folgenden wird kurz beschrieben, welche Schritte dabei durchlaufen werden und warum. Wer selbst einen Blick riskieren möchte, findet die Keycloak-Oberfläche unter „http://localhost:8282/auth“ und kann sich mit dem Nutzername „admin“ und dem Passwort „keycloak“ anmelden:

Schritt 1: Realm anlegen und konfigurieren

Der Realm im Keycloak beschreibt den Nutzerkreis von Personen, deren Zugriffe verwaltet werden. Zusätzlich werden alle „zentralen“ Einstellungen für diesen Nutzerkreis verwaltet. Dazu gehören folgende Einstellungen:

Die Login-Masken Konfiguration, ob es erlaubt ist, dass der Nutzer sich selbst am Keycloak registrieren kann oder ob die E-Mail-Adresse durch den Keycloak verifiziert werden soll. Die Mail-Server Konfiguration, über die der Keycloak-Server E-Mails versenden kann.

Schritt 2: Rollen anlegen

In diesem Beispiel werden die Keycloak Realm-Rollen dazu genutzt die Nutzerkreise für die einzelnen Clients einzuschränken. Dazu wird eine Rolle mit demselben Namen, wie der Client erzeugt („OK-REACT-DEMO-WEBAPP“).

Schritt 3: Authentication konfigurieren

Um die Realm-Rollen-Zuordnung bei der Anmeldung zu berücksichtigen wird ein neuer Anmeldeablauf konfiguriert, der neben der Abfrage des Nutzernamen und des Passworts zusätzlich die erforderliche Rollenzuordnung prüft.

Dazu wurde eine Extension für den Keycloak-Server verwendet, die mit der Überprüfung der Rollen betraut ist, aber durch einen angepassten Anmeldeablauf eingebunden werden muss. Die Extension ist im Beispiel enthalten und darf gerne übernommen werden.

Schritt 4: Client anlegen und konfigurieren

Ein „Client“ stellt die Konfiguration für eine angebundene Webanwendung da. Dabei empfiehlt es sich, für jede Anwendung einen eigenen Client anzulegen. Neben Einstellungen für valide Redirect-URLs, Web-Origins oder dem Access-Type „public“ wird auch der zuvor erstellte Authentication-Flow gewählt.

In unserem Beispiel werden die Client-Rollen genutzt, um die System-Rollen des Nutzers zu realisieren („USER“ und „ADMIN“). Um den Token für das MicroProfile richtig zu konfigurieren wird der Scope „microprofile-jwt“ als Default benötigt. Über den erstellten Token Mapper werden die Client-Rollen in den Token für das Feld „groups“ übernommen.

Die zuvor beschriebene nötige Konfiguration des Keycloak-Clients (Teil der Webanwendung) kann dem Reiter „Installation“ entnommen werden (Format: „Keycloak OIDC JSON“). Der Download wurde in diesem Beispiel in das Verzeichnis der Anwendung gelegt („keycloak.json“).

Schritt 5: Nutzer anlegen und konfigurieren

Für die Beispiel-Anwendung wurden zwei Nutzer angelegt und die entsprechende Gruppenzuordnung vorgenommen. Zusätzlich wurde das Passwort auf den Wert „Test1234“ gesetzt.

Webanwendung unter der Haube

Die im Beispiel enthaltene Webanwendung ist unter der URL „http://localhost:8000“ erreichbar und öffnet sich, ohne die Anmeldung in Richtung Keycloak-Server zu veranlassen. Dieses Szenario wurde gewählt, um dem Anwender seinen aktuellen Zustand zu visualisieren.

Durch den Klick auf den Login-Knopf wird die Anmeldung in Richtung Keycloak-Server veranlasst.

Nach erfolgreicher Anmeldung des Nutzers leitet der Keycloak-Server den Nutzer mit gültigem Token wieder zur Anwendung zurück.

Der Anmeldetoken verliert nach einer gewissen Zeit seine Gültigkeit und muss erneuert werden. In der Demo-Anwendung wird dafür der Knopf „Refresh“ geklickt.

Abhängig von den Nutzer-Rechten und dem Schalter „Respect Roles“ können die Knöpfe „Access as USER“ oder „Access as ADMIN“ geklickt werden. Durch den Klick wird eine der beiden Schnittstellen des im Hintergrund laufende Microservice angefragt. Ist der Nutzer berechtigt die Schnittstelle zu nutzen, wird eine entsprechende Antwort gesendet. Andernfalls kann entweder der Knopf nicht geklickt werden oder die Schnittstelle antwortet mit der Nachricht, dass der Nutzer nicht berechtigt ist die Schnittstelle zu nutzen.

Microservice unter der Haube

Der Microservice verfügt über keine Oberfläche, so dass die Anfragen und Antworten nicht visualisiert werden können. Allerdings werden alle Anfragen und Antworten in das Log-File geschrieben. Unter der URL „http://localhost:8080/secure/openapi-ui“ kann allerdings die Visualisierung der openapi.yaml angeschaut werden.

Fazit

Die Anbindung von Keycloak an eine Webanwendung und einen Microservice nach MicroProfile Spezifikation erscheint erstmal sehr einfach, die Tücke liegt allerdings im Detail.

Durch die Einschränkung, dass ein Microservice nur genau einem Realm zugeordnet werden kann, muss man bei der Verwendung mehrerer Anwendungen mit unterschiedlichen Nutzerkreisen und Berechtigungen auf eine komplexere Konfiguration des Keycloak-Servers zurückgreifen.

Die sehr einfache Konfiguration im MicroProfile hat leider nur eine Tücke: Bei einer falschen Zertifikats-URL (z.B. innerhalb des Docker-Kontextes) funktioniert der Service einfach nicht und die Fehlermeldung liefert keine Auskunft über die falsche Konfiguration.

Wenn diese kleinen, aber gemeinen Fallen umschifft sind, dann ist das Zusammenspiel aber super einfach zu nutzen und kann ohne weiteres in die Breite getragen werden.

Viel Spaß beim Ausprobieren und Herumspielen. Bei Fragen stehen die open knowledge GmbH und ich Euch gerne zur Verfügung.


Keine Kommentare

Kontakt

OPEN KNOWLEDGE GmbH

Standort Oldenburg:
Poststraße 1, 26122 Oldenburg

Standort Essen:
II. Hagen 7, 45127 Essen