~~NOTOC~~
====== User Deprovisionierung via Attribute Query ======
Dieser Artikel ist ein Community-Beitrag für Shibboleth IdP 3.x. Es ist unklar, ob er für Shibboleth IdP 4.x so noch gilt.
{{INLINETOC 2}}
===== Einleitung =====
Dies ist **keine fertige Schritt für Schritt Anleitung** wie man eine "shibbolisierte" Webanwendung (die Daten über Ihre Nutzer speichert) bereinigt, sondern die aufgeführten Punkte sollen lediglich als Gedankenanstoß dienen, wie man das Thema User Deprovisionierung angehen kann und was es zu beachten gilt.
Problem hierbei ist meist, dass man bei dem Shibboleth-Verfahren nur dann Informationen über einen Nutzer erhält, wenn sich dieser aktiv einloggt.\\
(Ausgenommen: man hat seinen Dienst noch über eine weitere Schnittstelle an das eigene IDM-System angeschlossen)
===== Lösungsansätze =====
Also wie entscheiden wir nun ob ein Nutzer noch existiert oder nicht?\\
Überlegen wir mal kurz welche Möglichkeiten sich uns bieten, wenn wir nun "alte" Nutzer identifizieren und sperren oder gar entfernen wollen:
- Sperren und Löschen des Nutzers nach definierter Inaktivität dessen
* Positiv: einfachster Weg ohne die Notwendigkeit irgendwelcher Erweiterungen
* Negativ: Nutzererfahren unschön wenn man nach längerer Inaktivität einen leeren Account vorfindet und/oder sich in regelmäßigen Abständen einloggen muss (eventuell vorher per Mail benachrichtigen?!)
- Abfrage des IDM-Systems (regelmäßig oder nach definierter Inaktivität des Nutzers) über eine weitere Schnittstelle
* Positiv: Nutzer wird nur gesperrt/gelöscht wenn er wirklich nicht mehr existiert
* Negativ: größerer Aufwand durch Anbindung über weitere Schnittstelle (eventuell Probleme mit Datenschutz und anderen technischen Vorkehrungen etc.)
- Abfrage des Shibboleth-IdP (regelmäßig oder nach definierter Inaktivität des Nutzers) via Attribute-Query
* Positiv: Nutzung einer bereits existierenden Schnittstelle
* Negativ: es sind teilweise Anpassungen am SP und/oder IDM nötig
===== Anforderungen an Queries =====
Betrachten wir die letzte Variante mit Hilfe des Shibboleth-eigenen Board-Mittels namens **Attribute-Query**. \\ Ursprünglich diente dieses Verfahren bei SAML1 der direkten Übertragung der Nutzer-Attribute zwischen IdP und SP über einen Backchannel. \\ Hier stellte der SP nach erfolgreichem Login des Nutzers eine separate Anfrage an den IdP, welcher alle Attribute über den Nutzer auslieferte, die beim heutigen SAML2 über den Frontchannel übergeben werden.
Um solch einen Query stellen zu können muss der SP im Besitz eines gültigen **NameIdentifiers** sein, damit der IdP diesen Auflösen kann. \\ Wenn ein SP einen Query zu einem beliebigen Zeitpunkt stellen möchte (unabhängig vom Vorhandensein eines Login-Contextes des Nutzers), so kommt hier lediglich die **"persistentId"** in Frage. \\ Diese muss auf Seiten des IdP dazu in einer Datenbank gespeichert werden, damit Sie rückwärts aufgelöst werden kann.
**Anforderungen**
* persistentId wird an SP ausgeliefert
* persistentId wird auf IdP Seite gespeichert
* persistentId wird auf SP Seite gespeichert
* Nutzer hat sich wenigstens einmal am SP angemeldet
* Attribute Query Profil ist für RelyingParty freigeschalten
* SP hat Möglichkeit einen Query zu stellen
* SP erreicht den IdP direkt (ohne Umweg/Redirect über den Browser des Client)
* es wird wenigstens ein Attribut an den SP ausgeliefert
===== Queries stellen =====
Wie stellt man einen Query?
Standardmäßig bringt der SP ein **"resolvertest"**-Skript mit. \\ Dieses empfiehlt sich jedoch nur für einen initialen Test, um zu Prüfen ob alle Einstellungen passen und der Attribute Query korrekt beantwortet wird. \\ Für den Produktiv-Einsatz arbeitet es **zu langsam**!
# ./resolvertest -n +9Blu1I8v96axDXHj01Gmpg36fM= -i https://your-idp.de/idp/shibboleth -saml2 -f urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
# persistentId: https://your-idp.de/idp/shibboleth!https://your-sp.de/shibboleth!+9Blu1I8v96axDXHj01Gmpg36fM=
# givenName: Max
# surname: Mustermann
# ...
Hat man Zugriff zum IdP kann man die übermittelten Attribute auf Vollständigkeit überprüfen, indem man am IdP nochmal den resolvertest ausführt.
# wget https://your-idp.de/idp/profile/admin/resolvertest?requester=https://your-sp.de&principal=uid-des-test-nutzers
# persistentId: https://your-idp.de/idp/shibboleth!https://your-sp.de/shibboleth!+9Blu1I8v96axDXHj01Gmpg36fM=
# givenName: Max
# surname: Mustermann
# ...
Will man nun vom SP aus automatisiert Anfragen stellen, so existiert hierfür der. sog. Attribute Resolver Handler:
**Vergessen Sie nicht, sowohl den ''shibd'' als auch den Webserver/httpd neu zu starten**! \\ Danach kann ein Aufruf z.B. direkt mit CURL durchgeführt werden.
!!!ACHTUNG!!!
persistentId muss codiert übergeben werden, da Sonderzeichen zu Misserfolg führen!!!
Stichwort URLencoding!!!
# curl -k "https://your-sp.de/Shibboleth.sso/AttributeResolver?entityID=https://your-idp.de/idp/shibboleth" --data-urlencode "nameId=+9Blu1I8v96axDXHj01Gmpg36fM="
===== Verlässlichkeit von Queries =====
Hat man seine ersten Queries erfolgreich gestellt, kommen schnell die Fragen auf: \\ **Wann kann ich den Nutzer löschen?** und **Wie verlässlich ist die Rückgabe?**
Wenn wir davon ausgehen dass ein Query das gleiche Attribut-Set zurück liefert, wie ein normaler Login-Vorgang, dann sollte man den Nutzer löschen können, sobald eine **leere Menge** zurück kommt. \\ Aber **ACHTUNG**!!! \\ Folgende Fallstricke können fälschlicherweise zu einem **leeren** oder auch **nicht leeren** Ergebnis führen:
**leeres Ergebnis**
* persistentId wurde falsch übergeben
* persistentId existiert nicht auf Seiten des IdP
* persistentId wird auf Seiten des IdP generell nicht gespeichert
* Nutzer ist nicht mehr im LDAP (obwohl noch im IDM vorhanden - Synchronisierungsproblem)
* LDAP-Verbindung ist abgebrochen
* Query ist fehlgeschlagen
**nicht leeres Ergebnis**
* Statische Attribute im Resolver liefern immer Werte (obwohl Nutzer nicht mehr existent)
* Nutzer wurde im LDAP nicht entfernt (obwohl im IDM nicht mehr vorhanden - Synchronisierungsproblem)
* Nutzer Passwort wurde im LDAP geändert (damit er sich nicht mehr einloggen kann)
Prinzipiell kann behauptet werden, dass man einem **leeren** Ergebnis **nicht** trauen kann und sollte! \\ Doch wie schließt man nun die diversen Fehler bei der Übertragung aus?
Sicherer wäre es, wenn man ein Attribut bekommt, was den Nutzerstatus abbildet und an Hand dessen man entscheidet, ob der Nutzer deaktiviert bzw. gelöscht werden kann. \\ Denn wenn man einen definierten Wert für das Attribut erhält kann man zumindest davon ausgehen, dass alles funktioniert hat und der Query erfolgreich abgearbeitet wurde und auch sonst keine Probleme bei der Abfrage des LDAP oder sonst irgendwo bei der Kommunikation zwischen den Systemen aufgetreten sind!
Um den LDAP nicht mit **Karteileichen** vollzumüllen, so wäre eine Möglichkeit die Struktur des LDAP wie folgt zu erweitern:
* ''ou=users,dc=einrichtung,dc=de'' (nur aktive Nutzer)
* ''ou=archive,dc=einrichtung,dc=de''
* ''ou=users,ou=archive,dc=einrichtung,dc=de'' (nur archivierte Nutzer)
* ''ou=disabled,ou=users,ou=archive,dc=einrichtung,dc=de'' (gelöscht, kann aber wieder angelegt werden)
* ''ou=locked,ou=users,ou=archive,dc=einrichtung,dc=de'' (vorübergehend gesperrt, z.B. aus Sicherheitsgründen)
* ''ou=deleted,ou=users,ou=archive,dc=einrichtung,dc=de'' (endgültig gelöschte Nutzer, falls die Identitäten noch gespeichert werden sollen, z.B. um sicherzustellen, dass User IDs nicht neu vergeben werden)
Scheidet ein Nutzer aus oder muss z.B. auf Grund eines Sicherheitsvorfalls kurzfristig deaktiviert werden, so verschiebt man diesen zunächst nach ''locked''. \\ Damit kann er sich schon mal nicht mehr am IdP authentifizieren (da nicht mehr im baseDN). \\ Kommt der Nutzer nach x-Tagen nicht mehr zurück an die Einrichtung so kann man ihn dauerhaft löschen und nach ''disabled'' oder ''deleted'' verschieben.
Generell reicht es zu, wenn die Einträge unter ''ou=archive'' nur noch die "uid" beinhalten und nicht mehr alle Attribute. (das spart Speicher ^^)
Jetzt definieren wir im IdP das Attribut [[:de:common_attributes#a15|schacUserStatus]], das den Status der betreffenden Identität abbilden soll:
Vokabular für schacUserStatus:
* Attributwert: ''urn:schac:userStatus:de:aai.dfn.de:idmStatus:STATUS'', wobei ''STATUS'' die folgenden Werte annehmen kann:
* ''active'' (optional) (entspricht AD 'enabled')
* ''locked'' (optional) (vorübergehend gesperrt, z.B. aus Sicherheitsgründen)
* ''disabled'' (optional) (gelöscht, kann aber wieder angelegt werden)
* ''deleted'' (mandatory) (Account und Benutzerinformationen gelöscht)
Benutzerstatus
Userstatus
Status eines Benutzers für einen Dienst
set of status of a person as user of services
idp.attribute.resolver.LDAP.archiveDN = ou=archive,dc=einrichtung,dc=de
idp.attribute.resolver.LDAP.archiveSearchFilter = (uid=$requestContext.principalName)
Bekommt ein SP nun bei einem Query einen der folgenden Werte, so kann er den Nutzer verlässlich sperren oder gar löschen.
* ''urn:schac:userStatus:de:aai.dfn.de:idmStatus:disabled''
* ''urn:schac:userStatus:de:aai.dfn.de:idmStatus:locked''
* ''urn:schac:userStatus:de:aai.dfn.de:idmStatus:deleted''
*
Als Beispiel für eine Attribute Query, die ein solches Attribut liefert, kann dieser URL genutzt werden: \\ [[https://testsp3.aai.dfn.de/Shibboleth.sso/AttributeResolver?entityID=https://testidp.aai.dfn.de/idp/shibboleth&nameId=MCE6NXEQ3FC3PUKY4M75EYCOWN4TGKBH&format=urn:oasis:names:tc:SAML:2.0:nameid-format:persistent|https://testsp3.aai.dfn.de/Shibboleth.sso/AttributeResolver?entityID=https://testidp.aai.dfn.de/idp/shibboleth&nameId=MCE6NXEQ3FC3PUKY4M75EYCOWN4TGKBH&format=urn:oasis:names:tc:SAML:2.0:nameid-format:persistent]] \\
(zuvor bitte bei der DFN-AAI Hotline Bescheid sagen, damit die ACL für diesen Handler entsprechend erweitert wird)
===== Queries einschränken =====
Wer darf eigentlich Queries stellen?
Da Queries nur mit Angabe der persistentId funktionieren, so kann man per RelyingParty-Config einschränken, dass nur die SPs Queries stellen dürfen für die auch die persistentId freigegeben ist. \\ Dies lässt sich unter Angabe einer [[de:shibidp:config-activation-condition|Activation Condition]] simpel lösen. \\
**ACHTUNG:** Diese Einstellung ist nur zu empfehlen, wenn **keine SAML1 SPs** mehr bedient werden!!!
https://your-sp.de/shibboleth
https://another-sp.de/shibboleth
Wozu sollen Queries genutzt werden?
* User-Synchronisierung
* User-Deprovisionierung
Möchte man via Query Nutzerdaten synchron halten, so sind keine weiteren Einstellungen nötig. \\ Es empfiehlt sich jedoch im Vorfeld mit dem **Datenschutzbeauftragten** dieses Vorgehen im Vorfeld zu besprechen, da hier **ohne Einverständnis** des Nutzers **personenbezogene Daten** ausgetauscht werden!!!
Will man Queries ausschließlich zur User-Deprovisionierung benutzen, so kann man sämtliche anderen Attribute für diesen Kanal deaktivieren. \\ Dies bringt unter anderem folgende Vorteile mit sich:
* Minimierung der openLDAP Abfragen
* Lastminimierung bei scripted Attributes
* Datenschutz + Datensparsamkeit!
Dazu laden wir uns zunächst folgende JAR-File [[https://box.fu-berlin.de/s/tMzEosxrAxp8wtC|idp-predicate-impl-1.0.0.jar]] herunter und legen diese unter "./edit-webapp/WEB-INF/lib/" ab. \\
Anschließend den IdP neubauen.
# ./bin/build.sh
Danke noch mal an Steffen Hofmann (FU Berlin), der diese Datei zur Verfügung gestellt hat. \\ Mit Hilfe des **enthaltenen Predicate** ist es uns möglich eine Entscheidung zu treffen ob es sich um einen **Attribute-Query** handelt oder nicht. \\ Darauf aufbauend erstellen wir uns beliebige **Activation Conditions**, um die Erstellung und **Freigabe von Attributen** zu steuern.
https://your-sp.de/shibboleth
https://another-sp.de/shibboleth
Nun aktivieren wir die Bedingungen im Resolver. \\ Dadurch werden **normale** Attribute **nur** noch bei einem normalen Login gebaut und an den Filter weitergereicht werden, aber nicht bei einem Query. \\ Das **schacUserStatus** Attribut hingegen wird **nur** noch bei einem Query erstellt.
# Die Angabe einer ActivationCondition an einem normalen Attribut führt leider nicht dazu, dass die LDAP-Abfragen nur gestellt werden, wenn der SP in der Condition steht
# Sondern:
# - Das Attribut wird erst gebaut (LDAP-Abfrage) und erst danach greift die Condition und regelt die Weitergabe des Attributs an die Filter
# - Die Condition MUSS daher an die LDAP-Dependency (DataConnector) gehangen werden, um die Ausführung der LDAP-Anfragen zu steuern/vermeiden
# Bei Attributen, die zusätzliche Conditions erhalten sollen können diese direkt am Attribut referenziert werden
Zusammenfassend hat man somit folgende Punkte realisiert:
* attributeQueries sind nur für SPs freigeschalten, die laut relying-party.xml auch die persistentID beziehen (ohne diese ist eine Abfrage erst gar nicht möglich)
* wer diese bezieht ist in der activation-conditions.xml unter der bean "SP-consumes-persistentId" definiert
* bei einem query soll lediglich das attribut "schacUserStatus" (und eventuell die "affiliation"?) ausgelesen und ausgeliefert werden
* alle anderen attribute "nur" bei normalen logins (datenschutz und entlastung des ldap)
* die einschränkungen müssen in der attribute-resolver.xml mittels activationConditionRef realisiert werden
* jeder dataConnector erhält eine condition, somit werden z.B. unnötige LDAP-Abfragen vorweg vermieden
* jedes attribut welches nur von einem dataconnector abhängt braucht nicht unbedingt eine weitere condition
* lediglich attribute die von keinem connector oder von anderen attributen (ohne connector) abhängen benötigen eine zusätzliche condition
* teils verknüpfung mehrerer bedingungen bei den conditions da einige attribute z.B. nur an bestimmte SPs ausgeliefert werden
===== Wann / Wie oft Queries stellen =====
Wann und wie oft sollten Queries gestellt werden?
Zunächst vorweg: \\ **Der SP ist verantwortlich** dafür Sorge zu tragen, dass der IdP während seines Betriebes **nicht gestört** wird, in dem er z.B. mit zu vielen Anfragen "bombardiert" wird!!!
Daher empfiehlt es sich Nutzer nur dann zu prüfen wenn folgende Bedingungen erfüllt sind:
* letzter login> x Tage
* letzte Prüfung> x Tage
Desweiteren sollte man **kleine Pausen zwischen einzelnen Queries** lassen, damit man als SP nicht Gefahr läuft z.B. durch Mechanismen wie Fail2Ban ausgesperrt zu werden. \\ Als Betreiber eines IdP ist es durchaus denkbar sich ähnlich wie im Thema [[:de:shibidp:fail2ban|Abwehr Brute Force]] Gedanken zu machen um sich gegen zu viele Query-Anfragen zu schützen.
Am Besten man nutzt also eine Cachefile, in der man hinterlegt, wann der Nutzer das letzte Mal erfolgreich abgefragt wurde. \\ Es ist also **nicht notwendig** und **nicht emfehlenswert** jeden Nutzer jeden Tag zu prüfen!
Vorteile:
* man minimiert die Anzahl an Anfragen (und damit die Last auf z.B. den IdP und den LDAP)
* man vermeidet DOS-Attacken und eventuelle Sperrung via Fail2Ban
* man gewährleistet, dass sich weiterhin Nutzer einloggen können
Alternativ besteht vielleicht die Möglichkeit einen **separaten IdP** aufzusetzen, der **ausschließlich** für die Abarbeitung von **Queries** zuständig ist. \\ Dabei sollte sichergestellt sein, dass alle IdP-Server via **Datenbank-Replikation** den gleichen Datenbestand aufweisen!!!
Denkbare Schwellwerte für Fail2Ban wären: maximal 50 Queries innerhalb von 10 Sekunden (nicht getestet und abhängig von der Menge der zu prüfenden Nutzer)
===== Links / Dokumentation =====
* [[de:shibidp:config-storage|Server-side Storage und persistent Id]]
* [[https://www.switch.ch/aai/support/presentations/techupdate-2014/04_Account_Checking.pdf|https://www.switch.ch/aai/support/presentations/techupdate-2014/04_Account_Checking.pdf]]
* [[https://shibboleth.atlassian.net/wiki/spaces/SP3/pages/2065334903/Attribute+Resolver+Handler|Shibboleth Wiki: Attribute Resolver Handler]]
* [[https://shibboleth.atlassian.net/wiki/spaces/SP3/pages/2065334902/resolvertest|Shibboleth Wiki: resolvertest]]
* [[https://shibboleth.atlassian.net/wiki/spaces/IDP4/pages/1265631506/SecurityAndNetworking#Back-Channel-Support|Shibboleth Wiki: IdP 4 Backchannel Support]]
* [[https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPAccountChecking|https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPAccountChecking]]
{{tag>archiv fixme}}