Comment créer un service provider SAMLV2 avec SimpleSAMLPHP

Dans cet article vous allez voir comment installer un Service Provider avec la solution SimpleSAMLphp, et l'ajout d'un Identity Provider à notre Service Provider. La configuration est réalisée sur un serveur debian avec PHP et nginx comme serveur web. Vous pouvez aussi réaliser l'installation avec un serveur apache2 sans le moindre souci. Nous allons pousser la configuration jusqu'à ajouter un Identity Provider pour réaliser la mise en place d'un SSO via la technologie SAML2.

SAML2 : kesako ?

SAML pour : Security assertion markup language, tous les détails sur https://fr.wikipedia.org/wiki/Security_assertion_markup_language, c'est d'ailleurs cette technologie qui est couramment utilisé pour la mise en place de SSO sur des applications métiers.

SimpleSAMLphp

Cette solution que vous trouverez ici : https://simplesamlphp.org, est une implémentation de SAML2 en PHP. Vous allez voir que la mise en place via cette solution d'un Service Provider et même d'un Identity Provider sont très simples et c'est d'ailleurs ce qui m'a orienté vers cette solution. L'avantage notamment de SimpleSAMLphp c'est que sur l'Identity Provider, vous aurez la possibilité de configurer plusieurs types de fournisseur d'identité, fichier en clair (oui c'est moche, mais pour les tests ça fera largement le boulot), Yubikey, SQL, LDAP et bien d'autres !

Service Provider

On en parle depuis le début mais qu'est ce que c'est ? Le Service Provider c'est en fait un service qui est rattaché à l'application finale. C'est dans le Service Provider que l'on définit qui est notre/nos fournisseurs d'identités. En effet vous pouvez très bien sur une application autoriser l'IdP (identity Provider) de l'entreprise TeraCorp et aussi celui de GigaCorp, c'est juste que lorsque la personne devra s'authentifier elle devra faire un choix pour dire je suis de tel ou tel organisation.

C'est donc un service qui est installé et géré par l'éditeur de l'application finale.

Installation de SimpleSAMLphp

On va devoir en premier lieu télécharger SimpleSAMLphp, pour se faire le plus simple est de passer directement par le github du projet via les release : https://github.com/simplesamlphp/simplesamlphp/releases

Lors de la rédaction du tuto c'est la version 1.18.8 pour la télécharger directement sur le serveur :

wget https://github.com/simplesamlphp/simplesamlphp/releases/download/v1.18.8/simplesamlphp-1.18.8.tar.gz

Avant de commencer l'installation il est aussi important de jeter un oeil au prérequis de SimpleSAMLphp : https://simplesamlphp.org/docs/stable/simplesamlphp-install#section_1 surtout que certaine librairie PHP sont indispensables et pas toujours installé comme libxml par exemple.

On va créer un répertoire dans notre dossier /var/www pour accueillir notre Service Provider (SP) pour cela très original on va le mettre ici :

mkdir /var/www/service_provider

Dans l'idée vous devrez dans le temps pouvoir mettre à jour votre Service Provider, avec les nouvelles versions de SimpleSAMLphp, pour vous faciliter la tâche je vous propose l'astuce suivante, copié votre SimpleSamlPHP avec son numéro de version dans votre répertoire /var/www/service_provider comme ceci, puis vous le décompressez :

cp simplesamlphp-1.18.8.tar.gz /var/www/service_provider/ && cd /var/www/service_provider/ &&  tar xzf simplesamlphp-1.18.8.tar.gz

Au lieu de renommer votre dossier simplesamlphp-1.18.8 en simplesamlphp, comme le fait la documentation officielle, je vous propose de créer un lien symbolique vers le dossier versionné :

cd /var/www/service_provider/ && ln -s simplesamlphp-1.18.8 simplesamlphp

Pourquoi faire ça ? Comme ça le jour ou une nouvelle version arrive vous pourrez l'installer à côté sans souci et la tester sans perturber les applications qui utilisent la version 1.18.8 et pour ensuite passer toutes vos anciennes applications vers la nouvelle version, il suffira de modifier le lien symbolique. Cela pourra aussi être utile en cas de monter majeure de version qui ne serait pas compatible avec l'existant de vos applications.

Reste à supprimer l'archive qui ne nous est plus utile :

cd /var/www/service_provider/ && rm simplesamlphp-1.18.8.tar.gz

Configuration de nginx

On va maintenant ajouter un alias dans notre configuration nginx pour que notre Service Provider puisse être utilisé par l'application. Par exemple ici si jamais je souhaitais l'ajouter au blog devops.kanjian.fr :

cd /etc/nginx/sites-available && nano devops.kanjian.fr

Dans notre fichier "devops.kanjian.fr" on va venir ajouter un block location dans le block server qui est configuré pour le SSL

server {
listen 443 ssl;
server_name devops.kanjian.fr;

# ici pour réduire la taille de l'exemple les [...] sont à retirer bien entendu
[...]

location ^~ /simplesaml {
alias /var/www/service_provider/simplesamlphp/www;

location ~ ^(?<prefix>/simplesaml)(?<phpfile>.+?\.php)(?<pathinfo>/.*)?$ {
include fastcgi_params;

# Vous pouvez aussi directement donner une valeur à fastcgi_pass notamment si celle-ci est déjà définit ailleurs exemple : fastcgi_pass unix:/run/php/php7.4-fpm.sock;
fastcgi_pass $fastcgi_pass;
fastcgi_param SCRIPT_FILENAME $document_root$phpfile;

# Must be prepended with the baseurlpath
fastcgi_param SCRIPT_NAME /simplesaml$phpfile;

fastcgi_param PATH_INFO $pathinfo if_not_empty;
}
}

# ici pour réduire la taille de l'exemple les [...] sont à retirer bien entendu
[...]
}

La seule modification par rapport à la documentation officielle c'est le changement du chemin de l'alias.

Modification du fichier de configuration

Avant de tester que notre installation fonctionne, il va nous falloir en premier lieu modifier un peu la configuration de SimpleSAMLphp. Pour cela il faut éditer le fichier config.php qui se trouve dans /var/www/service_provider/simplesamlphp/config/config.php.

nano /var/www/service_provider/simplesamlphp/config/config.php

Vous pouvez si vous le souhaitez modifier le paramètre par défaut de baseurlpath pour donner le domaine complet avec l'alias vers simplesaml, mais si vous avez suivi la configuration ci-dessus de nginx vous n'aurez pas besoin de le modifier, il doit donc ressembler à ceci :

'baseurlpath' => 'simplesaml/',

Ne pas oublier le / à la fin, vous pouvez en profiter pour modifier les informations : technicalcontact_*, si vous avez un SMTP, c'est tout de même plus efficace si vous en avez un n'hésitez pas, pour envoyer vos e-mails je vous invite à le configurer aussi dans ce fichier.

Renforcer la sécurité

Dans la suite du fichier vous allez voir un paragraphe bien nommé : SECURITY CONFIGURATION OPTIONS. Dans cette section on a avoir pas mal de chose qu'il faut impérativement modifier pour ne pas avoir de souci par la suite.

La première chose c'est le "secretsalt", et les gars de chez SimpleSAMLphp sont sympas ils vous proposent une commande pour réaliser un salt random :

LC_CTYPE=C tr -c -d '0123456789abcdefghijklmnopqrstuvwxyz' </dev/urandom | dd bs=32 count=1 2>/dev/null;echo

Vous pouvez aussi utiliser openssl pour réaliser votre chaine aléatoire (openssl à tout de même la réputation de faire cela plus "random") :

openssl rand -hex 16

Une fois votre chaine aléatoire obtenue il faut remplacer la valeur de "secretsalt" par cette dernière par exemple :

'secretsalt' => 'ytrbn3ci4vr8t6gtgoszmm9ywc4isrf0',

Ensuite il vous faudra modifier le password par défaut de l’utilisateur admin en modifiant la ligne :

'auth.adminpassword' => '123',

Par exemple vous pouvez utiliser mon générateur de mot de passe : https://www.kanjian.fr/downloads/password-generator.

Je vous recommande aussi de modifier à true les deux paramètres suivant :

'admin.protectindexpage' => true,
'admin.protectmetadata' => true,

Il sera important par la suite de modifier la valeur de trusted.url.domains, qui permettra de restreindre les domaines ayant le droit d'utiliser votre Service Provider, en effet il ne faudrait pas qu'une autre application du serveur s'amuse à pouvoir utiliser votre Service Provider, dans le cas d'hébergement mutualisé cela pourrait conduire à du vol d'informations.

Il n'est pas recommandé, ni souhaitable, de nos jours d'avoir encore des applications sans SSL donc, il n'est pas recommandé d'autoriser le paramètre suivant enable.http_post, mais si jamais vous êtes sur une installation sans SSL pensez à le mettre à true sinon vous aurez des soucis.

Dans le cadre d'un Service Provider qui serait en production, il est recommandé de désactiver les options de debug, et donc de modifier ainsi les lignes 'debug' de SimpleSAMLphp :

'debug' => [
'saml' => false,
'backtraces' => false,
'validatexml' => false,
],

Ainsi que de ne pas afficher les erreurs éventuelles pour cela il faut modifier 'showerrors' ainsi :

'showerrors' => false,

Laissez errorreporting à true, en effet il est toujours utile de garder les messages d'erreurs dans les logs même en production au cas où un incident surviendrait.

Dans le cadre d'une utilisation en production du Service Provider il est important aussi d'ajuster le logging des évènements / erreurs etc. pour ce faire il faut modifier logging.level, je vous recommande le niveau WARNING, cela vous permettra d'avoir des choses dans les logs qui ne sont pas actuellement bloquants, mais qui pourrait le devenir.

'logging.level' => SimpleSAML\Logger::WARNING,

Vous pouvez maintenant vous rendre à l'URL de votre Service Provider (généralement en ajoutant simplesaml à votre domaine dans mon exemple : https://devops.kanjian.fr/simplesaml/) et vous connecter en tant qu’administrateur, puis vous rendre dans l'onglet "Configuration"  :

Vérifiez bien que tout ce qui est obligatoire est bien installé sinon il faudra y remédier. Attention SimpleSAMLphp est installé sur une machine de test avec un PHP ancien, préféré la version 7.4 voir carrément la version 8.

Configuration des sources d'authentifications

Pour que votre Service Provider fonctionne, il faut définir comment il va pouvoir demander aux utilisateurs de s'authentifier. L'idée ici est d'utiliser le protocole SAML2, notamment pour faire du SSO. Tous les détails de tous les paramètres de configuration pour le SAML2 se trouvent ici : https://simplesamlphp.org/docs/stable/saml:sp. On va donc modifier le fichier authsources.php de SimpleSAMLphp et plus particulièrement le code suivant :

// An authentication source which can authenticate against both SAML 2.0
// and Shibboleth 1.3 IdPs.
'default-sp' => [
'saml:SP',

// The entity ID of this SP.
// Can be NULL/unset, in which case an entity ID is generated based on the metadata URL.
'entityID' => null,

// The entity ID of the IdP this SP should contact.
// Can be NULL/unset, in which case the user will be shown a list of available IdPs.
'idp' => null,

// The URL to the discovery service.
// Can be NULL/unset, in which case a builtin discovery service will be used.
'discoURL' => null,

/*
* The attributes parameter must contain an array of desired attributes by the SP.
* The attributes can be expressed as an array of names or as an associative array
* in the form of 'friendlyName' => 'name'. This feature requires 'name' to be set.
* The metadata will then be created as follows:
* <md:RequestedAttribute FriendlyName="friendlyName" Name="name" />
*/
/*
'name' => [
'en' => 'A service',
'no' => 'En tjeneste',
],

'attributes' => [
'attrname' => 'urn:oid:x.x.x.x',
],
'attributes.required' => [
'urn:oid:x.x.x.x',
],
*/
],

Pour modifier le fichier :

cd /var/www/service_provider/simplesamlphp/config && nano authsources.php

On va d'abord modifier la valeur 'default-sp' par ce que l'on souhaite par exemple :

// An authentication source which can authenticate against both SAML 2.0
// and Shibboleth 1.3 IdPs.
'sp-kanjian' => [

Idem pour la valeur entityID que l'on va modifier on peut réutiliser la valeur précédente :

// The entity ID of this SP.
// Can be NULL/unset, in which case an entity ID is generated based on the metadata URL.
'entityID' => 'sp-kanjian',

Si vous avez déjà l'URL de l'IDP (Identity Provider) à contacter vous pouvez la renseigner directement dans 'idp', si vous laissez à null alors l'utilisateur se verra proposer quel IDP utiliser pour se connecter. On va aussi modifier le nom de notre service (pensez à décommenter les lignes) :

'name' => [
'en' => 'Service Provider for Kanjian',
'fr' => 'Service Provider pour Kanjian,
],

Et aussi lister les attributs que l'on souhaite recevoir de la part de notre Identity Provider, et notamment ceux qui nous seront obligatoire (attributes.required) :

'attributes' => [
'uid',
'email',
],
'attributes.required' => [
'uid',
'email',
],

Dans l'exemple ci-dessus on demande l'id de l'utilisateur et son e-mail, en fonction des IDP vous pourrez avoir accès à d'autres valeurs tel que les rôles, les noms prénoms etc.

Ajout de certificat pour le Service Provider, il est courant que les IDP requière l'utilisation de certificat pour les différents Service Provider, cela permet notamment d'avoir des échanges chiffrés entre les deux éléments.

Pour se faire on va créer un certificat auto-signé dans le répertoire /var/www/service_provider/simplesamlphp/cert via la commande suivante :

cd /var/www/service_provider/simplesamlphp/cert && openssl req -newkey rsa:4096 -new -x509 -days 3652 -nodes -out sp-kanjian.crt -keyout sp-kanjian.pem

Pensez à bien renommer les crt et pem avec quelque chose de cohérent pour votre installation, RSA en 4096 car c'est la recommandation en vigueur lors de la rédaction de cet article (attention la documentation officielle est sur du 3072) et le nombre de jours est placé à 3652, grosso modo 10 ans.

Il va vous poser tout un tas de question auxquels vous n'êtes pas obligé de répondre. Attention à ce que ces fichiers soient accessibles à l'utilisateur de nginx (www-data) par défaut.

Ensuite on va retourner modifier notre fichier authsources.php pour ajouter à notre tableau 'sp-kanjian' les noms de nos certificats. Pour rappel par défaut dans la configuration (config.php) de SimpleSAMLphp les certificats sont stockés dans son répertoire cert/ donc nul besoin d'en donner le chemin complet. Sauf si bien sur, si vous les avez stockés ailleurs sur le disque.

Donc la modification dans notre exemple donne ceci :

'attributes.required' => [
'uid',
'email',
],
'privatekey' => 'sp-kanjian.pem',
'certificate' => 'sp-kanjian.crt',

Récupération des metadata

Pour récupérer vos métadonnées et pouvoir les donner à un Identity Provider vous allez devoir retourner sur l'interface d'adminisration par exemple : https://devops.kanjian.fr/simplesaml/

Une fois connecté rendez-vous dans l'onglet "Fédération" puis cliquez sur le lien "Afficher les métadonnées" cf la capture d'écran ci-dessous :

Une fois que vous aurez cliqué sur le lien vous arrivez à un écran similaire à celui-ci :

SimpleSAMLPHP vous donne deux choses, la première une version XML au standard SAML 2.0 que vous pourrez échanger avec n'importe laquelle des solutions du marché qui supporte SAML 2.0. Et si vous savez que l'Identity Provider qui doit accepter votre Service Provider est aussi un SimpleSAML PHP vous n'aurez plus qu'à lui communiquer la version PHP et non pas la version XML.

Alors si vous gérer à la fois l'Identity Provider et le Service Provider, comme c'est le cas de temps en temps pour moi, la version PHP simplifie la vie est évite des manipulations. Mais honnêtement il est préférable de vous transmettre uniquement des données XML. Dans le chapitre d'après nous allons voir comment justement via des métadonnées transmises en XML les convertir pour SimpleSAMLPHP et les ajouter à notre Service Provider.

Ajouter un Identity Provider

Pour ajouter un Identity Provider vous devez donc vous procurer ces métadonnées au format XML par exemple. Une fois le fichier reçu par les personnes qui gère l'Identity Provider vous allez devoir les ajouter à votre Service Provider. Avec SimpleSAMLPHP pour pouvoir les utiliser vous allez devoir les convertir en PHP.

Voici un exemple de métadonnées XML :

Une fois que vous avez le fichier rendez-vous dans l'interface de votre SimpleSAMLPHP ensuite dans l'onglet "Fédération" puis cliquez sur "Convertisseur de métadonnées XML vers SimpleSAMLphp". Une fois que vous aurez cliquez sur le lien vous arrivez à l'écran suivant :

Une fois sur cet écran deux solutions, soit vous uploadez votre fichier XML ou réalisé un copier/coller du contenu du fichier. Cliquez ensuite sur "Analyser" et vous aurez l'écran suivant avec la conversion effectuée. Dans la première partie de l'écran vous avez le fichier XML source et en dessous la version convertie pour être ajouter en PHP au Service Provider géré par SimpleSAMLPHP.

Exemple de conversion :

Maintenant vous devez copier le code qui a été produit par SimpleSAMLPHP, et on va ensuite l'ajouter aux Identity Provider accepté par votre Service Provider.

Pour se faire vous devez aller éditer le fichier "saml20-idp-remote.php" qui se trouve dans "/var/www/service_provider/simplesamlphp/metadata", par défaut le fichier ne contient rien ou presque :

Vous allez donc pouvoir coller le code généré par le convertisseur à la fin du fichier en question ce qui devrait vous donner quelque chose du genre :

Et voilà votre Identity Provider est bien configuré et utilisable avec votre Service Provider. Enfin, dans la théorie, car pour être vraiement opérationnel, il faut que le gestionnaire de l'Identity Provider ait aussi ajouté les métadonnées de votre Service Provider; tant qu'il ne l'aura pas fait vous ne pourrez pas l'utiliser pour vous connecter.

Utiliser votre Service Provider dans une application PHP

Maintenant pour provoquer une connexion via le Service Provider dans une application PHP vous pouvez utiliser le code suivant :

//-- on va envoyer pour une connexion sur notre SP
require_once('/var/www/service_provider/simplesamlphp/lib/_autoload.php');

$session = SimpleSAML_Session::getSessionFromRequest();
$session->cleanup();

$as = new SimpleSAML_Auth_Simple('sp-kanjian');
$as->requireAuth();

$attributes = $as->getAttributes();

$idp = $as->getAuthData('saml:sp:IdP');
$logout_url = $as->getLogoutUrl();

Le code va signaler qu'une authentification est requise pour accéder à cette page via le Service Provider "sp-kanjian". Une fois qu'un utilisateur est connecté il revient sur cette même page dans laquelle vous pouvez via la présence de valeur dans "$attributes" savoir si la personne est bien connecté et récupérer par exemple son uid et son adresse e-mail.

Lors de la mise en place de ce genre de service si vous ne gérez pas l'Identity Provider je vous recommande de tracer le contenu des attributs pour être sûr d'avoir la bonne syntaxe dans les clés. Car par expérience on a de tout des mots standard comme "email" aux choses plus farfellu comme des URL complètes.

Configurer un Identiy Provider par défaut

Pour terminer et vous permettre d'aller un peu plus loin, vous pouvez si vous le souhaitez configurer pour votre Service Provider un Identity Provider par défaut (au final il en devient presque unique). Pour se faire vous devez modifier le fichier authsources.php qui se trouve dans : "/var/www/service_provider/simplesamlphp/config" et modifier la valeur de la clé 'idp' du tableau qui définit votre Service Provider. Attention ici il faut donner la clé qui se trouve dans le tableau "$metadata" de l'Identiy Provider que vous avez configuré préalablement.

Exemple de mon Identity Provider de test pour Kanjian :

Donc la valeur à  prendre est celle de "$metadata['idp-kanjian.fr']" donc "idp-kanjian.fr" ce qui donne dans mon fichier authsources.php le résultat suivant :

Voilà vous avez donc configuré par défaut votre Identity Provider pour ce Service Provider. Bien sur si vous voulez autoriser plusieurs Identity Provider vous pouvez, il vous suffit de ne pas faire cette dernière étape. Vos utilisateurs aurons alors une interface pour chosir l'Identity Provider qu'ils souhaitent utiliser pour se connecter à votre application.

Conclusion

Pour conclure vous avez du vous en rendre compte au final la mise en place d'un Service Provider est très simple. Les premières installations vont demanderons une petite heure de travail et ensuite on les réalisé généralement en 15/20 minutes. Enfin sauf si bien sur on reçoit des métadonnées XML de l'Identity Provider dans un format pas très standard ce qui arrive encore de temps à autre.

Si vous avez des commentaires / remarques n'hésitez surtout pas !

Leave a Reply

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *