Dans le monde du logiciel propriétaire, la compatibilité est un sujet relativement simple à appréhender. Si vous voulez que le logiciel Nénufar fonctionne avec le logiciel Prophète, alors il y a deux possibilités. Soit Nénufar intègre un connecteur Prophète (ou l’inverse, ou les deux…), soit les deux logiciels ne sont pas prévus pour fonctionner ensemble, et dans ce cas-là, en général, votre intégration ne réussit pas. Et quand bien même elle réussirait, la configuration résultante ne serait pas officiellement supportée, ce qui bien souvent est rédhibitoire à la mise en production. Cette logique est reposante pour le DSI. Au moment d’acheter un nouveau logiciel, il suffit de regarder les logos sur la plaquette, si les logos correspondent aux logiciels déjà utilisés, c’est gagné, sinon, c’est fichu, à moins bien sûr d’avoir les poches suffisamment bien remplies pour que l’éditeur du logiciel accepte de faire évoluer son produit pour rajouter l’intégration manquante. En fin de compte, le monde propriétaire fonctionne avec une logique de produit, un concept issu du marketing, ce qui est bien normal quand on se rappelle que le but premier des éditeurs de logiciels propriétaire est de vendre quelque chose.

Logiciel et produit

C’est au contact du logiciel libre que l’on découvre véritablement la nuance qui existe entre la notion de logiciel et la notion de produit. Un logiciel, c’est un morceau de code, qui s’exécute quelque part, et qui a un certain comportement, sans se préoccuper du logo qu’il porte, de l’état des licences, du nombre de CPU disponibles, ou de l’année en cours. Un logiciel se fiche complètement d’être en train de parler avec un serveur web Apache, avec un serveur web propriétaire, ou avec un humain qui irait toucher les bons connecteurs de sa prise réseau au bon moment. Wordpress est prévu pour fonctionner avec une base MySQL, mais remplacez la par Percona Server ou MariaDB, et il fonctionnera tout aussi bien. Parce que Percona Server, bien que légèrement différent de MySQL, se comporte presque exactement de la même façon, et que WordPress, logiciel libre, n’implémente pas d’anti-fonctionnalité limitant artificiellement sa compatibilité. Un produit, en revanche, est une vue de l’esprit. C’est un logiciel auquel on a donné un nom, un numéro de version, un logo, et bien souvent un ensemble de propriétés plus ou moins avérées (améliorer votre sécurité, accélérer votre site web, appâter vos futurs clients, etc.). D’ailleurs, l’informaticien avisé se retiendra bien de croire en les promesses de la plaquette du produit avant d’avoir vu tourner le logiciel qui le constitue. Mais le produit est une abstraction bien pratique pour se construire une image mentale de ce qui se passe. On abstrait ainsi le logiciel, ses dépendances, ses composants, son architecture parfois complexe, en un terme plaisant à l’oreille. On peut regretter que beaucoup d’utilisateurs et de développeurs de logiciels libres se reposent un peu trop sur l’approche produit, reproduisant quelques vieux et mauvais réflexes du monde propriétaire. Et on voit beaucoup d’ingénieurs se laisser piéger par cette approche, jusqu’à en oublier les bases du logiciel libre. Un bug apparaît lorsque l’on fait communiquer deux logiciels? On dira alors “ça n’est pas compatible” en haussant les épaules, alors que le correctif serait peut être trivial si on prenait la peine de soumettre le problème aux développeurs. Et que dire des nombreux logiciels libres qui ne fonctionnent désormais plus que sur une seule (ou deux) distributions Linux, ou ceux avec des bibliothèques systèmes patchées. Ces logiciels se sont laissés complètement dépasser par l’approche produit, et l’univers entier est prié de bien vouloir gentiment graviter autour d’eux.

Qu’est ce que ça veut dire, être compatible?

Logo LemonLDAP::NG

On peut indéniablement considérer LemonLDAP::NG comme un produit. Il a un logo, une image de marque, une philosophie de développement. Le logiciel qui le constitue implémente un certain nombre de protocoles standards : SAML2.0, OpenID Connect, CAS, ainsi que son propre protocole, qui n’a pas de nom. Que veut-on dire quand on se demande si une application est compatible avec LemonLDAP::NG? Un logiciel qui annonce une compatibilité avec le standard SAML2.0 a toutes les chances d’être compatible. Sans pour annoncer explicitement la compatibilité avec LemonLDAP::NG. C’est tout l’intérêt des standards, d’autant plus lorsqu’ils sont ouverts. Mais un logiciel qui n’annonce qu’une compatibilité avec Keycloak a de grande chances d’être également compatible avec LemonLDAP::NG car les deux produits implémentent la même norme : OpenID Connect. Pas la peine donc de vouloir absolument trouver le logo de LemonLDAP::NG sur les plaquettes produit des logiciels qu’il va protéger, ou de se référer uniquement à la liste des applications déjà testées. Pour illustrer cela, voyons deux exemples d’intégration qui, semblent inenvisageables au premier abord, mais qui, avec un peu de travail d’intégration, sont finalement tout à fait possibles.

Mattermost Team Edition

Logo Mattermost

Mattermost est un logiciel de messagerie instantanée basée sur les technologies web, une catégorie de logiciel ayant le vent en poupe dans les projets agiles, et qui se pose en concurrent direct d’un fameux logiciel propriétaire au nom quelque peu mal ajusté. Mattermost fait partie d’une catégorie de logiciel libre bien particulière : l’Open Core. Le principe est de proposer à la communauté une version libre mais limitée fonctionnellement, tout en réservant aux clients payants, en général des entreprises, les fonctionnalités d’intégration les plus intéressantes. La moralité de cette démarche ne faisant pas l’objet de ce billet, on se concentrera donc sur les possibilités d’intégration de la version communautaire. Voyons la plaquette du “produit” Mattermost. Dans sa version communautaire, les possibilités d’authentification sont les suivantes :

  • Comptes internes
  • Gitlab

Alors que la version entreprise propose, en supplément :

  • OAuth2.0
  • Google
  • Office 365
  • AD/LDAP
  • SAML

On note ici la confusion qui règne entre support d’un protocole générique susceptible d’être implémenté par plusieurs produits (SAML, OAuth2.0) et des solutions logicielles purement propriétaires, quand ce n’est pas simplement le nom de leur éditeur. À première vue donc, le seul terrain d’entente entre LemonLDAP::NG et Mattermost semble être le protocole SAML, et nécessite donc la version entreprise. Ce raisonnement, simpliste, est le seul possible dans un monde propriétaire fait de protocoles secrets et légalement restreints. Naturellement, le logiciel libre nous offre de nouvelles armes. On dit souvent que dans l’informatique, tout est possible, il suffit de le développer. Et bien parfois, il n’y a même pas besoin ! Voyons donc comment interconnecter LemonLDAP::NG et la version communautaire de Mattermost, sans rajouter la moindre ligne de code à aucun de ces deux produits.

Stratégie

Il n’est bien sur pas question de récupérer de quelque manière que ce soit la moindre ligne du code propriétaire qui permet l’intégration de SAML dans Mattermost. Nous allons donc devoir utiliser exclusivement les fonctionnalités de la version communautaire. Rappelons-nous des paragraphes précédents. Un logiciel se fiche complètement d’à qui il est en train de parler. La version communautaire de Mattermost est un logiciel libre, et respecte donc, entre autres, les libertés fondamentales suivantes :

  • Utiliser le code de la manière que l’on veut, y compris légèrement détournée
  • Étudier le fonctionnement du code

C’est ces libertés que nous allons mettre à contribution pour réaliser cette intégration, là où le contrat de licence d’un logiciel propriétaire nous interdirait sans doute de ne serait-ce que tenter cette intégration. Notre stratégie sera donc de configurer LemonLDAP::NG pour se faire passer pour un serveur Gitlab. LemonLDAP::NG étant lui-même un logiciel libre, cette approche, qui relève de la noble culture du hacking, est non seulement permise, mais encouragée !

Étude du fonctionnement de Mattermost

Pour commencer, il nous faut étudier la manière dont Mattermost et Gitlab interagissent dans une configuration standard. On installe donc la version communautaire de Mattermost, et de Gitlab :

Screenshot mattermost GitLab

Et on configure l’interconnexion entre les deux :

Screenshot mattermost GitLab2

Reste à tester une connexion, en capturant toutes les requêtes HTTP reçues par le serveur Gitlab, puisque c’est elles qu’il nous faudra imiter.

On sait que Gitlab utilise la norme OAuth2.0 pour fournir des informations d’identité à des applications tierces, mais OAuth2.0 n’est pas un protocole précisément détaillé, et de nombreux choix sont laissés à chaque implémentation. La capture des échanges nous permet d’y voir beaucoup plus clair. Lors du clic sur le bouton de connexion via Gitlab, Mattermost redirige le navigateur vers le serveur Gitlab, qui fait alors la requête suivante (mise en forme) :

GET /oauth/authorize?
response_type=code&
client_id=70c981f8106376837bb4e7446525718033068768943e2cfba0f2298af73bfeb1&
redirect_uri=http%3A%2F%2Fmattermost.lxd%3A8065%2Fsignup%2Fgitlab%2Fcomplete&
state=eyJhY3Rpb24iOiJsb[...]
HTTP/1.1
HTTP/1.1 302 Found
Location: http://mattermost.lxd:8065/signup/gitlab/complete?
code=f5363d478962e7a2cb7b77cd1b4b8b66ab5f0cbe932b1820087efa0dace25897&
state=eyJhY3Rpb24iOiJsb[...]

Le serveur Gitlab redirige vers Mattermost avec un code, ce qui correspond au flux OAuth2.0 “Authorization Code Grant”. Mattermost va ensuite échanger ce code contre un Access Token, toujours auprès du serveur Gitlab :

POST /oauth/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
client_id=70c981f8106376837bb4e7446525718033068768943e2cfba0f2298af73bfeb1&
client_secret=6224955736a6c5f8f2bd822c707c5cb1fd9c89a51dd5d23c6c4e752849109d25&
code=f5363d478962e7a2cb7b77cd1b4b8b66ab5f0cbe932b1820087efa0dace25897&
grant_type=authorization_code&
redirect_uri=http%3A%2F%2Fmattermost.lxd%3A8065%2Fsignup%2Fgitlab%2Fcomplete
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"refresh_token" : "1b62c38c57d0d588d8c6fbcb6c99762be6447144a726dc3b503ec68c6a81a31a",
"created_at" : 1559664283,
"token_type" : "bearer",
"scope" : "api",
"access_token" : "97523e103aed6460ed78719c01dc4f7f9cba5ccb81d07d541328ca31b6eeef75"
}

Enfin, Mattermost utilise cet Access Token pour récupérer l’identité de l’utilisateur :

GET /api/v4/user HTTP/1.1
Authorization: Bearer 97523e103aed6460ed78719c01dc4f7f9cba5ccb81d07d541328ca31b6eeef75
HTTP/1.1 200 OK
Content-Type: application/json
{
"two_factor_enabled" : false,
"private_profile" : null,
"external" : false,
"public_email" : "",
"identities" : [],
"last_activity_on" : "2019-06-04",
"username" : "test",
"confirmed_at" : "2019-06-04T15:11:14.451Z",
"can_create_group" : true,
"website_url" : "",
"name" : "Mr. Test User",
"state" : "active",
"linkedin" : "",
"email" : "test@example.com",
"theme_id" : 1,
"organization" : null,
"location" : null,
"avatar_url" : "https://www.gravatar.com/avatar/55502f40dc8b7c769880b10874abc9d0?s=80&d=identicon",
"can_create_project" : true,
"color_scheme_id" : 1,
"last_sign_in_at" : "2019-06-04T15:56:27.547Z",
"web_url" : "http://gitlab.lxd/test",
"bio" : null,
"created_at" : "2019-06-04T15:11:14.687Z",
"id" : 2,
"current_sign_in_at" : "2019-06-04T15:56:38.977Z",
"twitter" : "",
"projects_limit" : 100000,
"skype" : ""
}

En bref, tout ceci constitue un flux OAuth2.0 relativement standard, et assez proche du flux OpenID Connect “authorization code” qui est une spécialisation de OAuth2.0 implémenté par LemonLDAP::NG. Pour assurer l’interconnexion, il suffit donc que LemonLDAP::NG réponde de la même manière que Gitlab à trois requêtes HTTP. La configuration pour y parvenir a été documentée par nos soins dans le wiki de LemonLDAP::NG. Deux difficultés seulement ont été rencontrées.

L’URL vers laquelle Mattermost redirige l’utilisateur ne contient pas le paramètre scope=, nécéssaire dans le protocole OpenID Connect. Pour résoudre ce problème, on peut faire appel à un peu d’inventivité, et à une règle de réécriture que tout bon serveur web permet de réaliser facilement. On configure donc Mattermost pour rediriger l’utilisateur non pas vers l’URL */authorize de LemonLDAP::NG, mais vers une URL /gitlab_authorize qui déclenche elle même une redirection vers /authorize en ajoutant le paramètre scope= La deuxième requête fonctionne à l’identique avec Gitlab et LemonLDAP::NG. La troisième requête également, à condition de s’assurer de la présence des bons attributs avec le bon nom dans le document JSON décrivant l’utilisateur. Finalement, avec un regard attentif, un peu d’astuce pour rajouter le paramètre scope=, et quelques tests pour trouver quels attributs sont nécessaires au bon fonctionnement de Mattermost, on arrive au résultat suivant :

Mattermost, le logiciel libre, n’est donc pas compatible avec Gitlab, mais plutôt avec “le jargon OAuth2.0 utilisé par Gitlab”, et quiconque sait parler ce jargon, peut donc s’intégrer avec Mattermost.

Jitsi Meet

Screenshot mattermost GitLab

Jitsi Meet est un logiciel de conférence web utilisant la technologie WebRTC. Logiciel libre, il est disponible à l’installation, mais ne propose pas beaucoup de possibilités d’authentification différentes. Un seul est en réalité proposé, il s’agit de Shibboleth. Shibboleth est un système complexe à mettre en place, basé sur SAML. On pourrait tout à fait s’en donner la peine, et configurer une fédération d’identité SAML entre Shibboleth et LemonLDAP. Mais ce serait se donner bien de la peine. Lisons plutôt, en détail, ce que nous dit la documentation.

In order to authenticate the user is redirected to special ‘login location’ which is protected by Shibboleth. It means that valid Shibboleth session is required in order to visit it. So whenever user tries to visit ‘login location’ and there is no valid Shibboleth session it will be redirected to Shibboleth login page for authentication. After that the user is taken back to Jicofo our ‘login location’ and is allowed to access it this time. Under ‘login location’ there is special authentication servlet which runs inside of the Jicofo. Because the location provides Shibboleth session, server will inject into the request additional headers or attributes(depending on deployment type). This attributes will tell Jicofo which user is logged-in(if any). Jicofo will generate session-id bound to that user and return in to the user in HTTP response. This session-id is considered secret and known only to the client and Jicofo. It is used to authorize all future requests.

Récapitulons : tout ce dont Jifoco (le composant serveur de Jitsi Meet) a besoin, c’est que son url /login soit protégée par le serveur web via un système d’authentification, et que le serveur web transmettre l’identité de l’utilisateur via des en-têtes HTTP. C’est tout simplement le fonctionnement historique de la plupart des systèmes de SSO basés sur le serveur web, et notamment celui du composant Handler de LemonLDAP::NG. On ne sait pas trop ce qui a pu passer par la tête des développeurs de Jitsi Meet pour proposer directement un exemple de configuration basé sur Shibboleth, alors que n’importe quel module système de SSO est compatible avec ce mode de fonctionnement. Ni une ni deux, on réalise la configuration permettant de protéger Jitsi Meet avec LemonLDAP::NG comme s’il s’agissait de n’importe quel autre système SSO. À nouveau, cette documentation a été contribuée par Worteks dans le cadre de l’étude à l’origine de cet article. Le résultat :

Jitsi Meet est ainsi connecté avec LemonLDAP::NG, sans avoir touché à la moindre ligne de code de Shibboleth. Là encore, il a fallu faire l’effort de ne pas s’arrêter aux apparences. La documentation annonce qu’il faut Shibboleth? Méfiance, ce n’est pas vraiment Shibboleth dont on a besoin, mais d’une toute petite de ses fonctionnalités, disponible dans tous les autres SSO.

Conclusion

Ces deux exemples nous montrent qu’avec la souplesse des logiciels libres il est possible d’intégrer des solutions qui n’avaient jamais prévu de fonctionner ensemble, et aucun contrat de licence ne saurait nous en empêcher. Ces exemples nous enjoignent également à un certain optimisme. Deux logiciels n’annoncent pas qu’ils peuvent fonctionner ensemble ? Pas d’inquiétude, c’est peut être possible malgré tout, et peut-être même de manière très simple malgré de trompeuses apparences.