Installation: Shibboleth in der Schulungs-VM

Hinweise

  1. Sie können den Shibboleth Identity Provider mit dieser Anleitung in der VM installieren. Die weiterführenden Links zur Dokumentation sind für Ihre spätere, echte Installation gedacht.
  2. Import des heruntergeladenen .ova-Images in Virtualbox: Datei -> Appliance importieren
  3. Für ein Copy & Paste ohne Gasterweiterungen können Sie diese Installationsanleitung selbstverständlich auch im Browser in der VM abrufen.
  4. Betriebssystem der Schulungs-VM: Ubuntu 18.04
  5. Alle Passwörter lauten "shibboleth".
  6. Auf der VM befindet sich ein LDAP-Browser (phpLDAPadmin), der im Web-Browser (Firefox) als Lesezeichen gesetzt ist.
  7. Zum besseren Verständnis des lokalen Setups (idp/sp1/sp2) hilft ein Blick in /etc/hosts und die Apache-Konfiguration unter /etc/apache2/sites-enabled.
  8. Für die echte spätere Installation folgen Sie bitte der Anleitung im Wiki.
  9. Die wichtigsten vorinstallierten Pakete sind Web-, Datenbank- und LDAP-Server:
  10. Los geht's! Starten Sie die virtuelle Maschine in Virtualbox und melden Sie sich mit dem Passwort "shibboleth" an.

JAVA vorbereiten

Erlangen Sie root-Rechte:
sudo su
Setzen Sie folgende Umgebungsvariablen:
# Datei: /etc/environment
JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64/"
Laden Sie diese Umgebungsvariablen neu:
exit
sudo su

Vorbereitung der MySQL-Datenbank

zur Doku: Datenbank-Konfiguration

Nun legen Sie eine Datenbank für Shibboleth an. Sie wird zwei Tabellen enthalten:
  1. "StorageRecords" für die Speicherung von User Consent und Sessions: Diese Tabelle wird bei der Installation automatisch angelegt.
  2. "shibpid" für die Speicherung von persistentIDs: Diese Tabelle legen Sie händisch an.
mysql -uroot -pshibboleth
SET NAMES 'utf8';
SET CHARACTER SET utf8;
CHARSET utf8;
CREATE DATABASE IF NOT EXISTS shibboleth CHARACTER SET=utf8;
USE shibboleth;
 
CREATE TABLE IF NOT EXISTS shibpid (
    localEntity VARCHAR(255) NOT NULL,
    peerEntity VARCHAR(255) NOT NULL,
    persistentId VARCHAR(50) NOT NULL,
    principalName VARCHAR(50) NOT NULL,
    localId VARCHAR(50) NOT NULL,
    peerProvidedId VARCHAR(50) NULL,
    creationDate TIMESTAMP NOT NULL,
    deactivationDate TIMESTAMP NULL,
    PRIMARY KEY (localEntity, peerEntity, persistentId)
);
 
CREATE USER 'shibboleth'@'localhost' IDENTIFIED BY 'shibboleth'; 
GRANT ALL PRIVILEGES ON shibboleth.* TO 'shibboleth'@'localhost';
FLUSH PRIVILEGES;
exit;

Shibboleth-Installation

zu den Shibboleth-Downloads

Zu Übungszwecken installieren Sie in diesem Tutorial zunächst die veraltete Version 3.4.5. Später führen Sie ein Update auf die aktuellste Version durch. Das zip-Archiv für den Shibboleth-IdP 3.4.5 liegt bereits im Verzeichnis /opt/install.
unzip /opt/install/shibboleth-identity-provider-3.4.5.zip -d /opt/install
Führen Sie das interaktive Installerskript aus:
/opt/install/shibboleth-identity-provider-3.4.5/bin/install.sh
Achtung, an zwei Stellen müssen Sie die Standardvorgabe ändern: bei "Hostname" und "Attribute Scope"
Source (Distribution) Directory: [/opt/install/shibboleth-identity-provider-3.4.5]
Installation Directory: [/opt/shibboleth-idp]
Hostname: [localhost.localdomain]
idp.local
SAML EntityID: [https://idp.local/idp/shibboleth]
Attribute Scope: [localdomain]
local
### im Folgenden setzen Sie Passwörter, zu Testzwecken z.B. "shibboleth"
Backchannel PKCS12 Password: 
Re-enter password: 
Cookie Encryption Key Password: 
Re-enter password:

Shibboleth-Konfiguration

Metadaten

zur Doku: Metadaten-Konfiguration

In diesem Schritt machen Sie dem neuen IdP die beiden Service Provider bekannt, die in der VM bereits konfiguriert sind. Laden Sie die lokalen Metadaten der beiden SPs in das entsprechende Verzeichnis des IdP. In einer Produktivinstanz würden Sie hier die Föderationsmetadaten einbinden. (Den Zertifikatscheck überspringen Sie hier nur wegen der lokalen Domain bzw. des selbst signierten Zertifikates in der VM.)
wget --no-check-certificate https://sp1.local/Shibboleth.sso/Metadata -O /opt/shibboleth-idp/metadata/sp1-metadata.xml
wget --no-check-certificate https://sp2.local/Shibboleth.sso/Metadata -O /opt/shibboleth-idp/metadata/sp2-metadata.xml
Konfigurieren Sie diese Metadatensätze im IdP: Die beiden Zeilen gehören vor das schließende MetadataProvider-Tag in der Datei. Ja: Die Datei hat dann ein MetadataProvider-Tag im MetadataProvider-Tag.
<!-- Datei: /opt/shibboleth-idp/conf/metadata-providers.xml -->
<MetadataProvider id="Shibboleth Metadata" xsi:type="ChainingMetadataProvider" [...] />
    <MetadataProvider id="SP1-Metadata"  xsi:type="FilesystemMetadataProvider" metadataFile="/opt/shibboleth-idp/metadata/sp1-metadata.xml"/>
    <MetadataProvider id="SP2-Metadata"  xsi:type="FilesystemMetadataProvider" metadataFile="/opt/shibboleth-idp/metadata/sp2-metadata.xml"/>
</MetadataProvider>
In neueren Shibboleth-Versionen haben die automatisch erstellten IdP-Metadaten ein Ablaufdatum. Das wollen wir in der Schulungs-VM nicht haben. Stellen Sie sicher, dass diese Zeile kein Ablaufdatum enthält, z.B. validUntil="2019-05-14T12:58:20.203Z".
<!-- Datei: /opt/shibboleth-idp/metadata/idp-metadata.xml -->
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 
    xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui"
    xmlns:req-attr="urn:oasis:names:tc:SAML:protocol:ext:req-attr" entityID="https://idp.local/idp/shibboleth">

LDAP-Anbindung

zur Doku: LDAP-/AD-Anbindung

Konfigurieren Sie die Anbindung an den vorinstallierten LDAP-Server in der Schulungs-VM. Tipp für die Praxis: Der bind-Account sollte nur Leserechte haben! Folgende Zeilen müssen editiert werden, der Rest bleibt so, wie er ist:
# Datei: /opt/shibboleth-idp/conf/ldap.properties
idp.authn.LDAP.authenticator                   = bindSearchAuthenticator
idp.authn.LDAP.ldapURL                         = ldap://idp.local:389
idp.authn.LDAP.useStartTLS                     = false
idp.authn.LDAP.baseDN                          = dc=users,dc=nodomain
idp.authn.LDAP.userFilter                      = (uid={user})
idp.authn.LDAP.bindDN                          = cn=admin,dc=nodomain
idp.authn.LDAP.bindDNCredential                = shibboleth

Zugriff auf das Webinterface regeln

Erlauben Sie den Zugriff auf das Webinterface vom kompletten /8er-Netz auf localhost: Tragen Sie statt der allowedRange 127.0.0.1/32 die 127.0.0.0/8 ein.
<!-- Datei: /opt/shibboleth-idp/conf/access-control.xml -->
  <util:map id="shibboleth.AccessControlPolicies">
    <entry key="AccessByIPAddress">
      <bean parent="shibboleth.IPRangeAccessControl" p:allowedRanges="#{ {'127.0.0.0/8', '::1/128'} }" />
    </entry>
  </util:map>

Cookie-Einstellungen

Hier stellen Sie ein, dass die Cookies, die der IdP setzt, nur über TLS-gesicherte Verbindungen gesendet werden.
# Datei: /opt/shibboleth-idp/conf/idp.properties
idp.cookie.secure = true

Konfiguration des Attribute Resolver

zur Doku: Attribut-Konfiguration

Die zentrale Konfigurationsdatei für die Generierung von Attributen ist /opt/shibboleth-idp/conf/attribute-resolver.xml.

Hier definieren Sie, welche Attribute Ihre Shibboleth-Instanz kennt, aus welchen Quellen dafür Attribute geholt werden und ggf. wie sie im IdP umgebaut werden sollen. Shibboleth liefert eine umfangreiche Beispieldatei conf/attribute-resolver-full.xml mit. Um es für die Schulung etwas übersichtlicher zu halten, laden Sie sich bitte zunächst unsere Beispieldatei ins conf-Verzeichnis herunter:
wget https://download.aai.dfn.de/ws/2019/attribute-resolver.xml -O /opt/shibboleth-idp/conf/attribute-resolver.xml

Attribut-Definitionen

Schauen Sie sich die Datei an: Im oberen Teil der conf/attribute-resolver.xml finden Sie die Attribute, die der IdP kennt. In diesem Beispiel-Abschnitt steht sinngemäß: "Ich definiere für den IdP ein Attribut uid. Um es zu befüllen, hole ich aus dem Data Connector LDAP das dortige Attribut uid. Service Provider sollen es als urn:oid:0.9.2342.19200300.100.1.1 lesen."

<!-- Datei: /opt/shibboleth-idp/conf/attribute-resolver.xml -->
    <AttributeDefinition xsi:type="Simple" id="uid">
        <InputDataConnector ref="myLDAP" attributeNames="uid"/>
        <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.1" friendlyName="uid" encodeType="false" />
    </AttributeDefinition>

Data Connectors

Im unteren Teil der conf/attribute-resolver.xml finden Sie den Abschnitt "DataConnectors", also die Datenquellen für die zu generierenden Attribute. Sie sehen dort drei Connectors:
  1. einen LDAP-Connector,
  2. einen Connector mit statisch definierten Attributen, hier z.B. mit dem hartkodierten Namen der Hochschule
  3. einen Connector für die MySQL-Datenbank (mit den persistentIDs)
Beispielauszug:

<!-- Datei: /opt/shibboleth-idp/conf/attribute-resolver.xml -->
    <DataConnector id="staticAttributes" xsi:type="Static">
        <Attribute id="o">
            <Value>Beispiel-Hochschule</Value>
        </Attribute>
    </DataConnector>

Konfiguration von SAML2-NameIDs

Der IdP soll zwei Arten von SAML2-NameIDs unterstützen:
  1. persistentIDs werden pro Useraccount und pro SP nach einem konfigurierbaren Schema generiert. SPs können anhand dieser Pseudonyme Accounts personalisieren, ohne Realnamen zu kennen. (Es sei denn, die werden auch übermittelt.)
  2. transientIDs werden bei jedem Login neu generiert.
Um die SAML Name IDs verwenden zu können, sind etliche Konfigurationsschritte an verschiedenen Stellen nötig. (Die jeweilige Datei ist den Schnipseln als Kommentar beigefügt.) Der MySQL-Datenbank-Connector ist - wie eben gesehen - bereits in unserer Beispielkonfiguration in conf/attribute-resolver.xml enthalten.

Ihre persistentIDs sollen aus dem Quellattribut uid mit einem Hash erstellt und in der MySQL-DB gespeichert bzw. aus ihr geholt werden. Dies stellen Sie in /opt/shibboleth-idp/conf/saml-nameid.properties ein. Behalten Sie die bereits bestehende Konfiguration und ändern bzw. aktivieren Sie nur folgende Zeilen:
# Datei: /opt/shibboleth-idp/conf/saml-nameid.properties
idp.persistentId.sourceAttribute = uid
idp.persistentId.salt = my-very-very-long-hash
idp.persistentId.generator = shibboleth.StoredPersistentIdGenerator
idp.persistentId.dataSource = shibboleth.MySQLDataSource
So schalten Sie die Generierung der persistentIDs an:
<!-- Datei /opt/shibboleth-idp/conf/saml-nameid.xml -->
        <!-- Uncommenting this bean requires configuration in saml-nameid.properties. -->
        <ref bean="shibboleth.SAML2PersistentGenerator" />
Gleich geschafft. Jetzt aktivieren Sie noch die Verarbeitung der persistentIDs, indem Sie folgende Zeile aktivieren:
<!-- Datei: /opt/shibboleth-idp/conf/c14n/subject-c14n.xml -->
<ref bean="c14n/SAML2Persistent" />

Konfiguration der MySQL-Anbindung

Fügen Sie für die MySQL-Konfiguration folgende Zeilen zur Datei conf/global.xml hinzu:
<!-- Datei /opt/shibboleth-idp/conf/global.xml -->
        <bean id="shibboleth.MySQLDataSource"
              class="%{mysql.class}"
              p:driverClassName="com.mysql.jdbc.Driver"
              p:url="%{mysql.url}"
              p:username="%{mysql.username}"
              p:password="%{mysql.password}"
              p:maxWait="15000"
              p:testOnBorrow="true"
              p:maxActive="100"
              p:maxIdle="100"
              p:validationQuery="select 1"
              p:validationQueryTimeout="5" />

        <bean id="shibboleth.JPAStorageService"
              class="org.opensaml.storage.impl.JPAStorageService"
              p:cleanupInterval="%{idp.storage.cleanupInterval:PT10M}"
              c:factory-ref="shibboleth.JPAStorageService.EntityManagerFactory" />

        <bean id="shibboleth.JPAStorageService.EntityManagerFactory"
              class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
            <property name="packagesToScan" value="org.opensaml.storage.impl"/>
            <property name="dataSource" ref="shibboleth.MySQLDataSource"/>
            <property name="jpaVendorAdapter" ref="shibboleth.JPAStorageService.JPAVendorAdapter"/>
            <property name="jpaDialect">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
            </property>
        </bean>

        <bean id="shibboleth.JPAStorageService.JPAVendorAdapter"
              class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
              p:generateDdl="true"
              p:database="MYSQL"
              p:databasePlatform="org.hibernate.dialect.MySQL5Dialect" />
Speichern Sie die Datenbank-Credentials, indem Sie diese Zeilen an die conf/idp.properties anhängen:
# Datei /opt/shibboleth-idp/conf/idp.properties
mysql.class    = org.apache.tomcat.jdbc.pool.DataSource
mysql.url      = jdbc:mysql://localhost:3306/shibboleth
mysql.username = shibboleth
mysql.password = shibboleth
An derselben Stelle geben Sie an, dass Sie die Sessions und den User Consent in der MySQL-Datenbank speichern möchten:
# Datei /opt/shibboleth-idp/conf/idp.properties
idp.session.StorageService = shibboleth.JPAStorageService
idp.consent.StorageService = shibboleth.JPAStorageService
idp.consent.attribute-release.userStorageKey = shibboleth.consent.PrincipalConsentStorageKey
idp.consent.attribute-release.userStorageKeyAttribute = %{idp.persistentId.sourceAttribute}
idp.consent.terms-of-use.userStorageKey = shibboleth.consent.PrincipalConsentStorageKey
idp.consent.terms-of-use.userStorageKeyAttribute = %{idp.persistentId.sourceAttribute}
idp.consent.allowGlobal = false
idp.consent.compareValues = true

Konfiguration von Attributfreigaben für die beiden lokalen Service Provider

Die zentrale Konfigurationsdatei für Attributfreigaben: /opt/shibboleth-idp/conf/attribute-filter.xml

Geben Sie einige Attribute für die beiden lokalen Service Provider in der Schulungs-VM frei. Dazu fügen Sie einen der folgenden Schnipsel zwischen die AttributeFilterPolicyGroup-Tags ein. Beide Schreibweisen sind möglich.
<!-- Datei: /opt/shibboleth-idp/conf/attribute-filter.xml -->
    <!-- Release to local SPs (1. Schreibweise) -->
    <AttributeFilterPolicy id="SPs_locals">
        <PolicyRequirementRule xsi:type="OR">
            <Rule xsi:type="Requester" value="https://sp1.local/shibboleth" />
            <Rule xsi:type="Requester" value="https://sp2.local/shibboleth" />
        </PolicyRequirementRule>
        <AttributeRule attributeID="eduPersonScopedAffiliation" permitAny="true" />
        <AttributeRule attributeID="surname"                    permitAny="true" />
        <AttributeRule attributeID="givenName"                  permitAny="true" />
        <AttributeRule attributeID="mail"                       permitAny="true" />
        <AttributeRule attributeID="uid"                        permitAny="true" />
    </AttributeFilterPolicy>
<!-- Datei: /opt/shibboleth-idp/conf/attribute-filter.xml -->
    <!-- Release to local SPs (2. Schreibweise) -->
    <AttributeFilterPolicy id="SPs_locals">
        <PolicyRequirementRule xsi:type="OR">
            <Rule xsi:type="Requester" value="https://sp1.local/shibboleth" />
            <Rule xsi:type="Requester" value="https://sp2.local/shibboleth" />
        </PolicyRequirementRule>
        <AttributeRule attributeID="eduPersonScopedAffiliation">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
        <AttributeRule attributeID="surname">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
        <AttributeRule attributeID="givenName">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
        <AttributeRule attributeID="mail">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
        <AttributeRule attributeID="uid">
            <PermitValueRule xsi:type="ANY" />
        </AttributeRule>
    </AttributeFilterPolicy>

Anpassung von Dateirechten

Stellen Sie sicher, dass der Tomcat-Benutzeraccount das Installationsverzeichnis besitzt. Bei Tomcat 9 heißt der Tomcat-User nur noch tomcat (ohne Versionsnummer).
# chown -R tomcat:tomcat /opt/shibboleth-idp/

Neustart von Diensten

Beobachten Sie in einem Terminal das Tomcat-Log und starten Sie in einem anderen den Tomcat neu:
# tail -f /var/log/tomcat9/catalina.DATUM.log
# systemctl restart tomcat9
In der Schulungs-VM müssen Sie auch den lokalen Shibboleth-SP hier einmal neustarten, weil er die neu hinzugekommenen IdP-Metadaten einlesen muss. shibd ist der SP, nicht der IdP!
# systemctl restart shibd

Prüfung des IdP-Status

Versuchen Sie, die Statusseite des IdP zu erreichen: https://idp.local/idp/status.

Wenn Ihr IdP noch nicht erreichbar ist, können Sie entweder bis zur Schulung warten oder sich schon selbst auf die Fehlersuche begeben. Die relevanten Logdateien: In unserem Wiki finden Sie außerdem ein paar Hinweise zum Troubleshooting.

Erstes Login für SP1

Wenn Sie eine funktionierende Statusseite haben, können Sie versuchen, sich am SP1 anzumelden. Besuchen Sie im Firefox in der VM den SP1 (Lesezeichenliste). Sie sollten zum IdP umgeleitet werden. Dort melden Sie sich mit folgenden Zugangsdaten an:
username: pingel
password: shibboleth
"SP2" ist noch geschützt und kann so nicht benutzt werden.

Shibboleth-Update

zur Doku: Shibboleth 3.x-Updates

Prüfen Sie zunächst, welche Version installiert ist:
$ curl -s -k https://idp.local/idp/status | grep ^idp_version
Führen Sie anhand der Dokumentation ein Update auf die aktuellste Shibboleth-Version durch. Wichtig ist, dass Sie auf die Wiederanpassung der Rechte für den Tomcat-User achten. Prüfen Sie erneut Ihre Installation:
$ curl -s -k https://idp.local/idp/status | grep ^idp_version

Untersuchung der Installation

Metadatensätze:
ls /opt/shibboleth-idp/metadata/
less /opt/shibboleth-idp/metadata/idp-metadata.xml
less /opt/shibboleth-idp/metadata/sp1-metadata.xml
Datenbank:
mysql
use shibboleth;
select * from StorageRecords\G;
select * from shibpid\G;
exit;

Tipps und Tricks

Neuladen des Shibboleth-IdP

Es gibt verschiedene Wege, eine Shibboleth-Instanz neu zu laden: Sie können Tomcat das Servlet neu laden lassen. Für Tests ist das ein guter, schneller Weg, im Produktivbetrieb würde es jedoch eine kurze Störung des Betriebs geben.
# touch /opt/shibboleth-idp/war/idp.war
Sie können das war file neu bauen lassen. Dies ist nötig, wenn Sie im Ordner edit-webapp Veränderungen vorgenommen haben.
# /opt/shibboleth-idp/bin/build.sh
Sie können die Intervalle für automatisches Neuladen der Konfiguration anpassen. Standardwerte:
# Datei: /opt/shibboleth-idp/conf/services.properties
# Beispiele:
idp.service.logging.checkInterval = PT5M
idp.service.relyingparty.checkInterval = PT15M
idp.service.attribute.resolver.checkInterval = PT15M
idp.service.attribute.filter.checkInterval = PT15M
Sie können so genannte Reload-URLs benutzen, um das Neuladen der Konfiguration über eine URL zu triggern.

zur Doku: WebInterfaces

# Beispiele:
# attribute-resolver.xml neu laden
curl https://idp.local/idp/profile/admin/reload-service?id=shibboleth.AttributeResolverService
# attribute-filter.xml neu laden
curl https://idp.local/idp/profile/admin/reload-service?id=shibboleth.AttributeFilterService

Logging

zur Doku: Logging

Logfiles, Loglevel uvm. konfigurieren Sie in der Datei /opt/shibboleth-idp/conf/logback.xml. Die wichtigsten Logdateien im Überblick:

Handler

Sie können über URLs bestimmte Handler direkt aufrufen. Hier ein paar Beispiele:

Identity Provider

Logout (Session am IdP beenden): https://idp.local/idp/profile/Logout

Service Provider

Übungen

Etherpad für die Lösungen

Übung 1

Setzen Sie das Loglevel für IdP, Messages und Encryption auf DEBUG hoch. Beobachten Sie das Log während der folgenden Übungen.

Übung 2

Ändern Sie die Resolver-Konfiguration für das Attribut eduPersonScopedAffiliation: Es soll nicht aus dem LDAP geholt werden, sondern aus eduPersonAffiliation und dem Scope zusammengesetzt werden.

Übung 3

Differenzieren Sie die Attributfreigaben für SP1 und SP2 aus: In der Ausgangskonfiguration erhalten beide Service Provider dieselben Attribute. SP1 soll jetzt keine personenbezogenen Attribute übermittelt bekommen, sondern nur eduPersonScopedAffiliation.

Übung 4

Schreiben Sie ein ScriptedAttribute für das Attribut eduPersonEntitlement: Nutzen Sie die eduPersonAffiliation, um für allen Accounts, die "member" sind, das Entitlement "urn:mace:dir:entitlement:common-lib-terms" zu übertragen.

Übung 5

Am SP2 darf sich nur eine bestimmte Gruppe aus dem LDAP anmelden, nämlich die ou "politologie". Für diese befugte Gruppe erwartet der SP ein bestimmtes Entitlement vom IdP. Wandeln Sie das Skript für eduPersonEntitlement so ab, dass nur an den SP2 zusätzlich zu common-lib-terms noch das Entitlement "https://sp2.local/entitlement/politologie" übertragen wird. Der SP1 soll es nicht bekommen. Melden Sie sich mit einem befugten Account am SP2 an.

Lösungen zeigen