SAML Authentifikation mit Apache und mod_auth_mellon
Eigentlich ist HTTP Basic-Auth ja eine durchaus feine Sache. Schnell eingerichtet und für die meisten Zwecke ausreichend sicher. Aber es gibt auch Nachteile, so können viele (alle?) Browsererweiterungen von gängigen Kennwortsafes diese nicht automatisch ausfüllen. Schlimmer noch, in vielen Browsern verhindert der Anmeldedialog, dass ich überhaupt an meinen Kennwortsafe herankomme (zumindest wenn diese als Browsererweiterung implementiert ist), weil das Fenster den Fokus hat und auch nicht abgeben mag. Also Anmeldevorgang abbrechen, Zugangsdaten raussuchen, neu versuchen. Nervt. Bliebe noch ein Benutzername und Kennwort die man sich merken kann, auch nicht schön.
Schön wäre SSO (und nicht nur, weil es insbesondere komfortabler ist, als das oben geschilderte Szenario). Viele Dienste für Kennwortsafes bieten SAML-IdP (Identity Provider) von Haus aus gleich mit an und wenn man sowas nicht hat, ein IdP ist z.B. mit simplesamlphp
auch recht schnell selbst aufgesetzt. Aber wie bringt man das jetzt Apache bei?
Hier hilft mod_auth_mellon
(https://github.com/latchset/mod_auth_mellon).
Bei Debian ist mod_auth_mellon
bereits als Paket enthalten, die Installation ist also denkbar einfach:
apt install mod_auth_mellon
a2enmod mellon
systemctl restart apache2
Aber ein wenig Konfiguration ist natürlich auch noch nötig. In diesem Beispiel nehmen wir an, dass ein einzelnes Verzeichnis (/restricted
) eines Servers (https://test.21x9.org
) geschützt werden soll. Hierzu führen wir zunächst das Script /usr/sbin/mellon_create_metadata
aus. Es benötigt zwei Parameter ENTITY_ID
und ENDPOINT-URL
. Das Script erzeugt drei Dateien, die wir in unsere Apache-Konfiguration benötigen. Also erzeugen wir einen entsprechenden Unterordner in /etc/apache2/
und rufen das Script dort auf:
mkdir -p /etc/apache2/mellon
cd /etc/apache2/mellon
/usr/sbin/mellon_create_metadata https://test.21x9.org/restricted/saml/metadata https://test.21x9.org/restricted/saml
Der Output sollte wie folgt aussehen:
Output files:
Private key: https_test.21x9.org_restricted_saml_metadata.key
Certificate: https_test.21x9.org_restricted_saml_metadata.cert
Metadata: https_test.21x9.org_restricted_saml_metadata.xml
Host: test.21x9.org
Endpoints:
SingleLogoutService: https://test.21x9.org/restricted/saml/logout
AssertionConsumerService: https://test.21x9.org/restricted/saml/postResponse
Nun können wir uns an die eigentliche Apache-Konfiguration machen. Wir editieren hierzu unsere bestehende VirtualHost-Konfiguration und erweitern diese um die folgenden Einträge:
<Location /restricted>
MellonEnable "auth"
MellonSPPrivateKeyFile /etc/apache2/mellon/https_test.21x9.org_restricted_saml_metadata.key
MellonSPCertFile /etc/apache2/mellon/https_test.21x9.org_restricted_saml_metadata.cert
MellonSPMetadataFile /etc/apache2/mellon/https_test.21x9.org_restricted_saml_metadata.xml
MellonIdPMetadataFile /etc/apache2/mellon/idp-metadata.xml
MellonIdPPublicKeyFile /etc/apache2/mellon/idp-pubkey.pem
MellonEndpointPath "/restricted/saml"
MellonIdP "https://identity.idp.example.com"
Require valid-user
</Location>
Die Dateien /etc/apache2/mellon/idp-metadata.xml
und /etc/apache2/mellon/idp-pubkey.pem
erhalten wir von unserem SAML-IdP. Diesen müssen wir außerdem mit unseren ServiceProvider (SP) Metadaten bekannt machen. Bei den meisten IdPs reicht es aus hierzu die Datei /etc/apache2/mellon/https_test.21x9.org_restricted_saml_metadata.xml
hochzuladen. Sollte der IdP nicht die Möglichkeit bieten, die SP-Metadaten als XML-Datei hochzuladen, hier ein paar oft verwendete Begriffe für die nötigen Angaben:
- Assertion Customer Service (ACS), Post-Back-URL, Reply-URL, SingleSignOn-URL, Dienstanbieter-URL, etc.:
https://test.21x9.org/restricted/saml/postResponse
- Instanz-ID, Metadaten-URL, Aussteller-ID, App-ID, etc.:
https://test.21x9.org/restricted/saml/metadata
- Partnerzertifikat, SP-Zertfikat, etc.: der Inhalt der Datei
/etc/apache2/mellon/https_test.21x9.org_restricted_saml_metadata.cert
bzw. der identische Abschnitt<ds:X509Certificate>
aus/etc/apache2/mellon/https_test.21x9.org_restricted_saml_metadata.xml
Sofern möglich sollte der IdP sha-256
statt sha-1
für die Signaturen seiner Antworten nutzen.
Werden mehrere IdPs genutzt, sollte man für jeden IdP natürlich separate XML/PEM-Dateien nutzen und diese bestenfalls weniger generisch benennen, als in diesem Beispiel. Oder sie gleich in eigenen Unterordnern von /etc/apache2/mellon/
ablegen. ;)
Den korrekten Parameter für MellonIdP
erhalten wir aus der Datei /etc/apache2/mellon/idp-metadata.xml
. Dort gibt es (meist in der 2. Zeile der Datei) folgenden Eintrag:
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://identity.idp.example.com">
Der Wert MellonIdP
muss der entityID
aus der Metadatendatei des IdP entsprechen. Nach einem Neustart von Apache sollte nun alles passen. Rufen wir nun unsere URL https://test.21x9.org/restricted
auf - ohne bereits an unserem IdP angemeldet zu sein - werden wir zur Anmeldemaske unseres IdP weitergeleitet. Geben wir korrekte Anmeldedaten ein, werden wir zurück zu https://test.21x9.org/restricted
geleitet und können auf unsere dort hinterlegten Inhalte zugreifen. Sind wir bereits am IdP angemeldet, entfällt die Anmeldemaske und wir können direkt zugreifen (es sollte nur eine kurze Umleitung (zum IdP und zurück) im Browser zu sehen sein und diese auch nur beim ersten Aufruf).
Wer mehrere Verzeichnisse mit immer demselben IdP schützen möchte, kann die Apache-Konfiguration auch wie folgt gestalten:
<Location />
MellonEnable info
MellonSPPrivateKeyFile /etc/apache2/mellon/https_test.21x9.org_restricted_saml_metadata.key
MellonSPCertFile /etc/apache2/mellon/https_test.21x9.org_restricted_saml_metadata.cert
MellonSPMetadataFile /etc/apache2/mellon/https_test.21x9.org_restricted_saml_metadata.xml
MellonIdPMetadataFile /etc/apache2/mellon/idp-metadata.xml
MellonIdPPublicKeyFile /etc/apache2/mellon/idp-pubkey.pem
MellonEndpointPath "/restricted/saml"
MellonIdP "https://identity.idp.example.com"
</Location>
<Location /private>
AuthType Mellon
MellonEnable auth
Require valid-user
</Location>
<Location /restricted>
AuthType Mellon
MellonEnable auth
Require valid-user
</Location>