Heutzutage ist es Standard, im eigenen Data Center sowohl mit Cloud- als auch On-Premises-Anwendungen zu arbeiten. Etwas kniffelig gestaltet sich dabei aber die Authentifizierung und Autorisierung der Benutzer, denn oft liegen die Konten über verschiedene Identity-Management-Werkzeuge hinweg verteilt. Wir zeigen in diesem Workshop, wie Sie externe Identity Provider wie Google, Azure oder Keycloak in Kombination mit FreeIPA für die Benutzeranmeldung einsetzen.
Identity-Management-Systeme wie beispielsweise das Active Directory oder FreeIPA [1] setzen primär auf das LDAP- und Kerberos-Protokoll, um Benutzer zu authentifizieren. Für POSIX-Umgebungen stellen beide Verzeichnisdienste Schnittstellen zur Verfügung und geben bei der Anmeldung eines Benutzers diesem beispielsweise eine User-ID oder eine Shell an die Hand. Dies klappt im klassischen Unternehmensumfeld sehr gut, um Benutzern einen Zugriff auf Systemebene zu geben. Userkonten für die Applikationsebene besitzen jedoch zumindest teilweise andere Anforderungen.
Komplizierte Module und Frameworks
Im Open-Source-Umfeld gibt es eine ganze Reihe an Möglichkeiten, um Benutzerkonten aus einem klassischen LDAP-Server auszulesen, zu authentifizieren und applikationsspezifische Attribute weiterzuleiten. Für den Apache-Webserver existiert hierfür beispielsweise das Modul "mod_look-up_identity" und für NGINX gibt es mit "nginx_http_lookup_identity_module" ein ähnliches, das bestimmte Benutzerattribute innerhalb einer Webanwendung zur Verfügung stellt. Diese sind dann eben nicht nur POSIX-spezifisch, sondern können beispielsweise auch die E-Mail-Adresse oder Telefonnummer eines Benutzers aus dem LDAP auslesen und an die Anwendung weiterleiten. Zur Authentifizierung mithilfe von Kerberos/SPNEGO greifen Sie stattdessen auf "mod_auth_gssapi" für Apache beziehungsweise auf "nginx_http_ authnz_pam_module" für NGINX zurück.
Dieser Ansatz kommt allerdings mit einer ganzen Reihe an Problemen daher. So unterstützen beispielsweise einige Module in ihrer Konfiguration lediglich ein einzelnes Backend-System. Große Schwierigkeiten bereiten ferner einige der bekannten Java-Frameworks, wenn es um das Thema Kerberos geht. Die Implementierung in den Frameworks ist teilweise derart veraltet, dass viele Features, die in aktuellen Versionen der MIT- oder auch Heimdal-Kerberos-Bibliotheken zur Verfügung stehen, dort gar nicht bekannt sind.
Identity-Management-Systeme wie beispielsweise das Active Directory oder FreeIPA [1] setzen primär auf das LDAP- und Kerberos-Protokoll, um Benutzer zu authentifizieren. Für POSIX-Umgebungen stellen beide Verzeichnisdienste Schnittstellen zur Verfügung und geben bei der Anmeldung eines Benutzers diesem beispielsweise eine User-ID oder eine Shell an die Hand. Dies klappt im klassischen Unternehmensumfeld sehr gut, um Benutzern einen Zugriff auf Systemebene zu geben. Userkonten für die Applikationsebene besitzen jedoch zumindest teilweise andere Anforderungen.
Komplizierte Module und Frameworks
Im Open-Source-Umfeld gibt es eine ganze Reihe an Möglichkeiten, um Benutzerkonten aus einem klassischen LDAP-Server auszulesen, zu authentifizieren und applikationsspezifische Attribute weiterzuleiten. Für den Apache-Webserver existiert hierfür beispielsweise das Modul "mod_look-up_identity" und für NGINX gibt es mit "nginx_http_lookup_identity_module" ein ähnliches, das bestimmte Benutzerattribute innerhalb einer Webanwendung zur Verfügung stellt. Diese sind dann eben nicht nur POSIX-spezifisch, sondern können beispielsweise auch die E-Mail-Adresse oder Telefonnummer eines Benutzers aus dem LDAP auslesen und an die Anwendung weiterleiten. Zur Authentifizierung mithilfe von Kerberos/SPNEGO greifen Sie stattdessen auf "mod_auth_gssapi" für Apache beziehungsweise auf "nginx_http_ authnz_pam_module" für NGINX zurück.
Dieser Ansatz kommt allerdings mit einer ganzen Reihe an Problemen daher. So unterstützen beispielsweise einige Module in ihrer Konfiguration lediglich ein einzelnes Backend-System. Große Schwierigkeiten bereiten ferner einige der bekannten Java-Frameworks, wenn es um das Thema Kerberos geht. Die Implementierung in den Frameworks ist teilweise derart veraltet, dass viele Features, die in aktuellen Versionen der MIT- oder auch Heimdal-Kerberos-Bibliotheken zur Verfügung stehen, dort gar nicht bekannt sind.
In der Praxis sind deshalb im Laufe der letzten Jahre immer mehr Webservices dazu übergegangen, auf vorhandene Tools zum Identity- und Access-Management (IAM) zurückzugreifen, anstatt eine eigene Lösung für die Anmeldung der Benutzer an einem Service zu entwickeln. Keycloak [2] ist ein bekanntes Beispiel aus dem Open-Source-Bereich. Die Software unterstützt alle gängigen IAM-Protokolle inklusive OAuth 2.0, OpenID und SAML.
In POSIX-Umgebungen sind zumeist die Pluggable-Authentication-Modules (PAM) dafür zuständig, einen Benutzer nicht nur zu authentifizieren sondern ebenfalls zu autorisieren. Kommt als Clientservice der System Security Services Daemon (SSSD) zum Einsatz, kümmert sich die PAM-Bibliothek "pam_sss" um diese Aufgabe.
FreeIPA und OAuth 2.0 für externe Identity-Provider
Ist der SSSD auf einem Clientsystem so konfiguriert, dass im Backend ein Free-IPA-System arbeitet, können neben den dort gespeicherten Benutzern auch Konten von einem Active Directory für die Anmeldung an einem FreeIPA-Client zum Einsatz kommen. Dies gelingt per Cross-Forest-Vertrauensstellung zwischen der Active-Directory-Forest-Root-Domäne und der primären FreeIPA-Domäne.
Besitzen Sie in Ihrer Umgebung einen RADIUS-Server, so können Sie User ebenfalls über das RADIUS-Protokoll an einem Client authentifizieren. In diesem Fall müssen die Benutzerinformationen allerdings im FreeIPA vorhanden sein und lediglich die Authentifizierung erfolgt dann mittels einer PIN und eines Tokens, die der RADIUS-Server verifiziert, um dann den Benutzer zu authentifizieren. Als Ergebnis einer erfolgreichen Authentifizierung bekommen Sie in beiden Fällen ein Kerberos-Ticket ausgestellt, mit dessen Hilfe Sie auf andere Kerberos-basierte Dienste innerhalb der FreeIPA-Umgebung zugreifen können.
Seit der FreeIPA-Version 4.10.1 unterstützt das Framework externe Identity-Provider (IdP) mittels des Protokolls OAuth 2.0 [3]. Um Zugriff auf eine Ressource zu bekommen, also beispielsweise bei der Anmeldung an einem System mittels SSH, schickt der Client hierfür eine Anfrage an einen IdP-Server, um von diesem einen sogenannten Access-Grant ausgestellt zu bekommen. Auch in diesem Fall müssen die Benutzerinformationen bereits in FreeIPA vorliegen, um so als Anker für die Identitäten aus dem externen IdP zu dienen.
Zum Synchronisieren der Benutzerinformationen zwischen dem IdP und dem FreeIPA-System können Sie auf das recht junge Projekt ipa-tuura [4] zurückgreifen. Das Tool stellt eine Cross-domain Identity Management (SCIM) 2.0 REST API [5] zur Verfügung. Die Django-basierte Software lässt sich dann in den externen IdP, beispielsweise Keycloak, einbinden. Erzeugen Sie nun einen Account in Keycloak, steht dieser auch in FreeIPA zur Verfügung und umgekehrt.
Verschachtelte IdPs
Identity Provider, die OAuth 2.0 Device Authorization unterstützen, sind beispielsweise die im Azure AD verankerte Microsoft Identity Platform, Google, GitHub, Keycloak und Okta. Möchten Sie einen IdP einsetzen, der die Protokollerweiterung nicht unterstützt, können Sie immer noch mehrere föderierte Server verwenden. Registrieren Sie also beispielsweise einen Keycloak-Server als IdP-Client in einem GitLab-IdP, können Sie auf GitLab-Konten zur Autorisierung von Benutzern zurückgreifen, obwohl die DevOps-Plattform die Device-Authorization-Erweiterung gar nicht unterstützt.
OAuth 2.0 Device Authorization Grant
An dieser Stelle geht es jedoch erst einmal darum zu demonstrieren, wie FreeIPA Benutzer mithilfe von OAuth 2.0 gegenüber einem externen IdP authentifiziert. Das Protokoll basiert primär auf der Idee, dass ein Client bei der Anmeldung an einem Service an einen sogenannten Authorization Server weitergeleitet wird, der dann die Autorisierung des Clients vornimmt. Dieser Redirect findet zumeist über einen Browser und HTTP sowie JavaScript-Abfragen statt.
Wenn Sie jetzt die Stirn runzeln und sich fragen, wie so etwas denn in POSIX-Umgebungen ablaufen soll, wo ein Benutzer beispielsweise mittels SSH auf ein Remote-System zugreifen möchte, haben Sie nicht ganz Unrecht. Und genau hier kommt eine Erweiterung des OAuth-2.0-Protokolls ins Spiel: Sie nennt sich Device Authorization Grant und dürfe von den meisten Lesern wahrscheinlich schon einmal eingesetzt worden sein, vielleicht sogar unbewusst.
OAuth 2.0 Device Authorization Grant ist im RFC 8628 definiert und findet in solchen Umgebungen Verwendung, wo Benutzer gar keine oder nur sehr wenig Möglichkeiten haben, mit einem Browser zu agieren, um die Anmeldedaten einzugeben. Dies ist beispielsweise bei Set-Top-Boxen der Fall, die an einem Fernsehgerät angeschlossen sind. Dort ist in der Regel eine Vielzahl von Apps installiert, die eine Anmeldung mit einem Benutzerkonto erfordern.
Statt die Anmeldedaten nach der Installation einer App manuell einzugeben, wird der Nutzer stattdessen oftmals dazu aufgefordert, einen QR-Code mit dem Smartphone zu scannen und die eigentliche Anmeldung über den Account dann auf dem Handy vorzunehmen. Danach ist der User automatisch auch in der App auf der Set-Top-Box angemeldet. Dies funktioniert deshalb, weil die App auf dem Gerät zur Authentifizierung des Accounts auf die OAuth-2.0-DeviceAuthorization-Erweiterung zurückgreift.
In POSIX-Umgebungen lässt sich die OAuth 2.0 Device Authorization somit also problemlos einsetzen. Meldet sich ein Benutzer mittels SSH an einem FreeIPA-Client an, wird eine URL ausgegeben. Der User kann diese dann in einem beliebigen Browser aufrufen, um den Client gegenüber dem IdP-Authorization-Server zu autorisieren (Bild 1). Ist dieser Vorgang erfolgreich abgeschlossen, bekommt der User auf dem Clientsystem ein Kerberos-Ticket ausgestellt, um damit auf andere Ressourcen der Umgebung zugreifen zu können – solange das Ticket gültig ist. Damit dies funktioniert, wurde der SSSD ab der Version 2.7.0 um das neue Kerberos-Plug-in "idp" erweitert.
Bild 1: Der Ablauf einer OAuth 2.0 Device Authorization.
OAuth-2.0-Client registrieren
Nach so viel Theorie soll nun endlich etwas Praxis folgen. Wir gehen an dieser Stelle davon aus, dass Sie bereits ein funktionstüchtiges FreeIPA-System am Laufen haben. Sollte dies nicht der Fall sein, finden Sie unter [6] eine Anleitung zur Installation eines einzelnen FreeIPA-Servers.
Das Ziel ist es nun, Benutzerkonten bei einem externen Identity-Provider zu verwenden, um hiermit auf Ressourcen eines Clients innerhalb der FreeIPA-Umgebung zuzugreifen. Hierfür delegieren wir die Authentifizierung und Autorisierung des Clients an das IdP-System. Im Erfolgsfall liefert es einen sogenannten Access-Grant an das FreeIPA-System zurück, das dann ein Kerberos-Ticket für den Benutzer ausstellt. Hiermit ist Letzterer dann in der Lage, Ressourcen innerhalb der FreeIPA-Topologie zu nutzen – also beispielsweise mittels SSH eine Anmeldung auf einem Remote-System durchzuführen.
In diesem Beispiel kommt Keycloak als externes IdP-System zum Einsatz. Die Registrierung des FreeIPA-Servers als OAuth-2.0-Client können Sie entweder über das Webinterface oder die Keycloak-eigenen Kommandozeilentools durchführen. Dabei ist wichtig zu erwähnen, dass die komplette FreeIPA-Topologie, also sämtliche Server- und Clientsysteme, vom externen IdP als ein einzelner OAuth-2.0-Client angesehen werden.
Listing 1 zeigt ein Skript, das mithilfe des Keycloak-Tools "kcreg.sh" eine Registrierung der FreeIPA-Topologie in Keycloak vornimmt. Die redirectURI "ipa-ca.ipa.test" zeigt auf den FreeIPA-Server, unabhängig davon, welcher Hostname tatsächlich auf dem System zum Einsatz kommt. Natürlich müssen Sie den Domänennamen (ipa.test) an Ihre eigene Umgebung anpassen. Wie die Registrierung im Detail abläuft, ist abhängig vom eingesetzten Identity-Provider. Wir empfehlen, die Dokumentation des jeweiligen IdPs zu konsultieren. Hat dies soweit funktioniert, müssen Sie auf dem FreeIPA-System noch eine Referenz auf das externe IdP-System anlegen. Dies geschieht mit dem folgenden Befehl:
Wichtig dabei ist, dass Sie als "client-id" zwingend die ID verwenden, die Sie bei der Registrierung des FreeIPA-Systems angelegt haben. In diesem Beispiel ist dies also die ID "ipa_oidc_client".
Achten Sie darauf, den korrekten Provider beim Aufruf von "ipa idp-add" anzugeben. Jeder Identity-Provider verwendet andere Endpunkte für die OAuth 2.0 Device Authorization und FreeIPA nutzt insofern auch andere URI-Adressen – je nachdem, welchen Provider Sie beim Anlegen der IdP-Referenz auswählen.
Natürlich benötigen wir nun auch noch Benutzerkonten in Keycloak oder dem IdP, den Sie einsetzen. Listing 2 zeigt, wie Sie erneut auf das Kommandozeilentool kcadm zugreifen, um damit einen Benutzer innerhalb von Keycloak anzulegen. Alternativ können Sie selbstverständlich auf die Web-GUI zugreifen, um den Account zu erzeugen. In diesem Beispiel legen wir den Account "testuser1" mit einer E-Mail-Adresse als zusätzliches Attribut an. Außerdem bekommt der User ein Passwort zugewiesen.
Der soeben angelegte testuser1 ist jetzt zwar innerhalb der Keycloak-Umgebung sichtbar, nicht jedoch innerhalb von FreeIPA. Es gibt nun zwei Möglichkeiten, um den Benutzer auch dort sichtbar zu machen. Entweder greifen Sie auf das am Anfang erwähnte Open-Source-Projekt ipa-tuura zurück, um Accounts zwischen dem externen IdP und FreeIPA zu synchronisieren. Oder aber Sie erzeugen den Account einfach zusätzlich innerhalb von FreeIPA, setzen aber kein Passwort für den Account, da der Benutzer ja über den externen IdP authentifiziert und autorisiert wird.
Das Setup von ipa-tuura, das eine SCIM-Bridge zwischen dem externen IdP und FreeIPA erzeugt, würde jedoch den Rahmen dieses Artikels sprengen, weshalb wir an dieser Stelle empfehlen, den Account zu Testzwecken einfach auch auf dem Free-IPA-Server zu erzeugen.
Mit dem Befehl ipa user-add legen Sie das neue Konto an und mittels ipa user-mod bearbeiten Sie bereits vorhandene Accounts, um weitere Attribute hinzuzufügen:
Wichtig ist, dass Sie das Konto mit der zuvor angelegten IdP-Referenz verknüpfen, die E-Mail-Adresse als User-ID verwenden und als Authentifizierungsmethode IdP festlegen. Ein Passwort benötigt der Account in der FreeIPA-Umgebung wie erwähnt nicht.
Bild 2: Über den kinit-Befehl wird der Benutzer gegenüber dem externen IdP authentifiziert und bekommt schließlich ein Kerberos-Ticket ausgestellt.
Authentifizierung mit kinit und SSSD
Zu Testzwecken können Sie jetzt mittels des kinit-Befehls versuchen, eine Authentifizierung gegenüber dem externen IdP durchzuführen, um so für unseren Testbenutzer ein Ticket-Granting-Ticket (TGT) vom Kerberos-Server (KDC) ausgestellt zu bekommen. Der KDC ist Teil des FreeIPA-Frameworks und wird beim Setup automatisch mitinstalliert.
Da für den Account als Authentifizierungsmethode idp hinterlegt ist, verwendet der Client die gleichnamige Kerberos-Pre-Authentication-Methode. Ähnlich wie auch bei der Authentifizierung mittels One-Time-Password Token (OTP) ist hierfür jedoch zwischen dem FreeIPA-Client und dem Kerberos-Server zuerst ein sicherer FAST-Tunnel (Flexible Authentication Secure Tunneling) aufzubauen.
Mittels Anonymous PKINIT können Sie ein anonymes Kerberos-Ticket vom KDC abfragen, das Sie dann im nächsten Schritt zum Aufbau des FAST-Tunnels einsetzen. Hierfür rufen Sie einfach kinit -n -c auf und geben zusätzlich einen Speicherort für das anonyme Kerberos-Ticket an, etwa "/tmp/armor". Mittels kinit -t übergeben Sie das Ticket dann erneut an kinit, um damit einen sicheren Tunnel zum KDC aufzubauen (Bild 2).
Intern verwendet FreeIPA nun den ipa-otpd-Service, um über das oidc_child-Tool einen sogenannten Device Code Authorization Grant von dem konfigurierten Identity-Provider abzufragen. Teil des eben gestarteten Workflows ist ein Code, der zusammen mit einer URL vom externen IdP an den Client zurückläuft.
Die URL ist anschließend vom Benutzer in einem Browser aufzurufen, um den Benutzer zu authentifizieren und dem OAuth-Client Zugriff auf bestimmte Attribute des Benutzerprofils zu geben, das im IdP gespeichert ist. Hat dies alles geklappt, zeigt Ihnen der Aufruf von klist -c, dass der Kerberos-Server des FreeIPA-Frameworks ein Ticket-Granting-Ticket für den Benutzer ausgestellt hat.
Die klist-Option "-c" zeigt Ihnen zusätzlich zum TGT ebenfalls die eingesetzte Kerberos-Pre-Authentication-Methode an. "pa_type 152" bedeutet in diesem Fall idp.
Natürlich müssen Sie nun nicht jedes Mal kinit verwenden, um eine Authentifizierung gegenüber einem externen IdP durchzuführen. Greifen Sie mittels SSH auf ein FreeIPA-Clientsystem zu, kümmert sich der dort installierte System Security Services Daemon um die Authentifizierung und Autorisierung des Benutzers gegenüber dem externen Identity-Provider.
An dieser Stelle sei noch der Hinweis erlaubt, dass aktuell ein Login in das FreeIPA-Webinterface mit dem externen IdP-Account nicht unterstützt wird. Dies ist aber für eine der zukünftigen Versionen geplant.
Wenn dieser Artikel Lust auf mehr gemacht hat, finden Sie unter [7] weitergehende Dokumentation zu dem Thema. Einen praktischen Hands-on-Workshop gib es unter [8]. Dort erhalten Sie auch weitere Hilfestellung zum Setup des Keycloak-Identity-Providers. Der Workshop eignet sich hervorragend für erste Tests.
Fazit
Über die OAuth 2.0 Device Authorization stellt das FreeIPA Identity-ManagementFramework eine Anbindung an externe Identity-Provider bereit. Somit ist eine Authentifizierung der dort gespeicherten Accounts auch in POSIX-Umgebungen möglich und es müssen nicht zwangsläufig unterschiedliche Benutzerkonten für Webapplikationen beziehungsweise Clouddienste und POSIX-Systeme zum Einsatz kommen. Eine Synchronisation der Konten in FreeIPA mit dem externen IdP gelingt mithilfe des SCIM-Standards. Dieser wiederum wird in der hier vorgestellten Umgebung über das recht neue Projekt ipa-tuura realisiert.