Journal Comment j'ai (presque) réussi à ne plus retenir de mot de passe pour mes services en ligne

28
21
jan.
2021

Sommaire

Après quelques années d'errance, je suis presque au bout de mon périple pour atteindre l'objectif suivant:

  • Ne plus avoir de mot de passe à retenir
  • Avoir un niveau de sécurité supérieur ou égal au niveau de sécurité offert par un mot de passe seul
  • Avoir un spectre de moyens d'authentification suffisamment large pour pouvoir me connecter depuis différents supports.

Ce journal est donc un retour d'expérience et une description des différents outils que j'ai mis en place pour atteindre l'objectif sus-cité.

Les services que j'héberge et pour lesquels j'ai ca en place sont:

  • Gestionnaire de mots de passe en ligne
  • Nextcloud pour le carnet d'adresse/agenda, le partage de fichiers
  • Divers sites web pseudo statiques mais qu'il ne faut pas rendre publics pour autant
  • Courriel: Rainloop webmail et Postfix/Dovecot

La base: un serveur d'authentification SSO OAuth2/OpenID Connect (OIDC)

C'est le socle de base et celui sur lequel beaucoup de ces services reposent pour authentifier l'utilisateur, et c'est la première étape de l'authentification sans mot de passe.

Les méthodes d'authentification que j'ai mises à ma disposition sont:

  • Le mot de passe à l'ancienne de mon serveur OpenLDAP
  • Le mot de passe à usage unique, les fameux codes à six chiffres HOTP ou TOTP qui changent tout le temps
  • Webauthn avec les yubikeys ou assimilé ou encore la reconnaissance de mon empreinte digital sur mon téléphone android
  • Le certificat TLS
  • Le mot de passe à usage unique, mais envoyé par courriel

Le serveur SSO OAuth2/OpenID Connect que j'utilise (et que je développe mais c'est pas le sujet) se base sur les scope pour définir les niveaux d'autorisation: un utilisateur doit avoir le droit d'utiliser les scopes disponibles pour les services. J'ai donc défini les scopes dont j'ai besoin: mail, cloud, admin, fichiers, etc. Et j'ai assigné les scopes aux utilisateurs selon leurs besoins.

Chaque scope est configuré pour être autorisé si l'utilisateur prouve son identité via un ou deux (voire plus) facteurs d'authentification. Typiquement les scopes non critiques sont accessibles avec une simple session non expirée sur le SSO (niveau 0), les scopes un peu sérieux comme Nextcloud, Icinga ou des rapports générés par des scripts maison nécessitent un facteur d'authentification, enfin les scopes critiques comme le courriel ou le gestionnaire de mot de passe demandent deux facteurs d'authentification.

Gestionnaire de mot de passe en ligne

C'est le seul pour lequel un mot de passe est encore requis pour accéder aux données, car elles sont chiffrées avec le mot de passe maître. Mais l'accès au services est protégé par le SSO OIDC en amont. En ce qui me concerne j'utilise mon gestionnaire maison qui a son plugin OAuth2 mais de ce que j'ai pu voir, keepass et bitwarden par exemple ont des connecteurs OIDC.

Nextcloud

Pour lui il y a au moins un plugin OIDC qui fonctionne bien avec ma config. Puis pour brancher mon agenda et carnet d'adresse ou l'accès webdav, j'utilise des mots de passe d'application qui sont des mots de passe permettant de se connecter aux services nextcloud. Ces mots de passe sont générés automatiquement et doivent être sécurisés quelque part (comme dans le gestionnaire de mot de passe par exemple…)

Apache2 mod_auth_openidc pour tous les services web statiques ou pseudo statiques

Lui c'est le module apache qui sécurise tout sous-domaine ou site que je veux avec OIDC. La configuration est suffisamment fine pour permettre de configurer différemment les sites à sécuriser selon le niveau de sécurité adapté. Ainsi mes flux video de caméra, mes applis toutes pourries que j'ai faites en PHP il y a des années et que j'ai pas envie de changer parce que ca marche, tous les trucs comme ca sont planqués derrière mod_auth_openidc.
Je crois savoir que des plugins similaires existent pour NGINX mais je ne connais pas ce serveur HTTP, si quelqu'un a un retour d'expérience je suis preneur.

Webmail sous Rainloop

Alors lui ca m'a pris un peu plus de temps et d'énergie. À l'origine, je n'avais besoin que de mon mot de passe LDAP pour me connecter au webmail. J'ai regardé si des connecteurs OIDC existent pour Rainloop mais apparemment pas. Ne voulant pas en développer un moi-même, j'ai fini par trouver une solution de contournement à base de mod_auth_openidc décrit plus haut, de master password dans dovecot, et d'un plugin rainloop maison, développé à l'arrache en une journée, et qui combine les deux. Concrètement mod_auth_openidc s'occupe d'authentifier l'utilisateur, une fois que c'est fait il donne au plugin rainloop le login de l'utilisateur connecté via un header HTTP dédié, puis le plugin rainloop connecte rainloop au serveur IMAP avec la combinaison login utilisateur@master/master password.

Postfix/Dovecot

C'est le dernier service pour lequel je me suis débarrassé du mot de passe. Il est branché sur mon annuaire LDAP pour la connexion.
J'ai découvert récemment qu'on peut avoir plusieurs mots de passe pour un même compte LDAP. C'est utilisé notamment par certains systèmes pour stocker les anciens mots de passes et s'assurer que lorsqu'on demande à un utilisateur de changer son mot de passe, il n'utilise pas un ancien déjà utilisé. Mais OpenLDAP ne fait pas de manières si tu as plusieurs mots de passe pour ton compte et que tu utilises l'un ou l'autre pour t'authentifier. Et donc rien ne t'empêche d'avoir ton mot de passe "primaire" qui est un mot de passe que tu peux retenir, puis d'en avoir d'autres qui sont des mots de passes aléatoires et stockés dans ton gestionnaire de mot de passe.

Ainsi mes logiciels de courriel comme thunderbird ou K9-mail utilisent un mot de passe secondaire que je ne connais pas. Autre avantage, si je perds mon téléphone ou ma tablette, je n'ai qu'à changer ou supprimer le mot de passe secondaire compromis.

Il ne doit en rester qu'un

Le mot de passe qu'il reste encore est celui du gestionnaire de mot de passe.
Mais l'avenir est plein de promesses parce qu'il existe plein de solutions comme GnuPG ou une extension appelée hmac secret définie par la FIDO alliance et implémentée par les yubikeys et d'autres. En gros cette extension permet de générer un mot secret et de manière déterministe avec la yubikey en lui donnant un seed. La clé retourne alors un mot de 32 ou 64 octets, impossible à deviner. Ce mot peut alors servir comme mot de passe maître pour son gestionnaire de mot de passe. Cette extension n'est pas encore supportée par les navigateurs mais j'ai l'espoir que ca arrive un jour!

Conclusion

OAuth2 et OpenID connect, c'est cool!

  • # Mot de passe applicatifs NC

    Posté par  (site web personnel) . Évalué à 5.

    Tu écris qu'il faut sécuriser quelque part les mdp applicatifs générés par NC. Je croyais qu'il étaient à usage unique!, comme les masques chirurgicaux. Je les générais, hop dans l'appli en question, puis poubelle! et si j'ai besoin d'une autre appli je génère un autre. Bon bah j'avais du mal comprendre!, en fait on pourrait s'en servir plusieurs fois, comme les… masques chirurgicaux?

    • [^] # Re: Mot de passe applicatifs NC

      Posté par  (site web personnel) . Évalué à 3.

      En effet, il faut comprendre comme je sécurise mes mots de passe d'application quelque part pour les réutiliser ailleurs, mais rien n'empêche de l'utiliser qu'une fois et d'en générer à chaque fois que tu en as à nouveau besoin.

  • # Scope

    Posté par  . Évalué à 3.

    Le serveur SSO OAuth2/OpenID Connect que j'utilise (et que je développe mais c'est pas le sujet) se base sur les scope pour définir les niveaux d'autorisation: un utilisateur doit avoir le droit d'utiliser les scopes disponibles pour les services. J'ai donc défini les scopes dont j'ai besoin: mail, cloud, admin, fichiers, etc

    C’est bizarre de faire ça, c’est pas vraiment ce pour quoi les scopes existent. Ils sont supposés représenter des groupes d’informations sur l’utilisateur (les claims) que l’application a le droit de connaître.

    Par exemple, tu as des claims comme given_name, family_name, nickname que tu vas envoyer si l’application demande le scope profile (et que l’utilisateur l’accorde).

    Voir les spec d’OIDC.

    • [^] # Re: Scope

      Posté par  (site web personnel) . Évalué à 4.

      C'est un choix d'implémentation.

      La question qui doit être répondue est la suivante: Le programme X veut accéder aux ressources Y de l'utilisateur Z. Mais il se peut que Z n'ait pas le droit d'accéder à Y tout court, genre l'utilisateur lambda n'a pas le droit d'accéder à la console d'administration, seulement à ses mails et nextcloud. Donc pour permettre ces niveaux d'accès, j'utilise les scopes.

      La norme OAuth2 est assez souple sur la nature scope et à quoi il sert. Elle spécifie qu'un ou plusieurs scopes peuvent être associés à un access token mais ne rend pas les scopes obligatoires.

      Le client inclus dans sa requête d'authentification la liste des scopes qu'il souhaite avoir, mais peut recevoir une sous-liste partielle des scopes demandés.

      Les scopes utilisés pour accéder à des claims est une fonctionnalité propre à OIDC qui permet à un utilisateur d'autoriser ou non le client d'accéder à des données personnelles de l'utilisateur.

      • [^] # Re: Scope

        Posté par  (site web personnel) . Évalué à 2.

        Heu moi je trouve très bizarre d'utiliser OIDC pour donner l'autorisation à un user d'accéder à une ressource. OIDC c'est autoriser une appliquication à accéder à des informations de l'utilisateur et pas l'inverse.

        Pour l'inverse il faut ajouter :

        • soit une gestion de groupe côté app
        • soit gérer des groupes dans l'ACL (bien plus commun)

        OIDC doit prendre les users dans un idp genre LDAP. Et c'est dans LDAP que tu dis que l'utilisateur "A" est du groupe "B".

        Ton app reçoit les groupes auxquels l'utilisateur appartient via le scope "groups" et permet l'utilisation de telle ou telle ressource si il est dans le bon groupe.

        En gros, il te faut un scope "groups"… Ou alors ton explication est pas claire 🙂

        • [^] # Re: Scope

          Posté par  (site web personnel) . Évalué à 3.

          Si je comprends ta méthode, l'appartenance au groupe est connue dans le claim.
          Pour moi il peut y avoir un souci de confidentialité, parce que si je comprends bien, le client connaîtra tous les groupes auquel l'utilisateur appartient, pas uniquement ceux dont il a besoin.

          Dans mon cas, les groupes de l'ACL correspondent aux scopes OAuth2 via un mappage dans l'AS qui connait les groupes auxquels l'utilisateur a accès. Les scopes autorisés se retrouvent ainsi dans l'access token, et le RS qui consomme le token pour accéder aux ressources de l'utilisateur saura quels niveaux d'accès sont autorisés pour ce token, donc pour cet utilisateur.

          En plus, les scopes autorisés dans le token ne sont pas nécessairement tous les scopes demandés par le client parce qu'il doit passer par plusieurs filtres avant d'atterrir dans le token:

          • scopes demandés par le client
          • scopes autorisés pour le client par l'AS
          • scopes autorisés pour l'utilisateur par l'AS
          • scopes autorisés par l'utilisateur pour ce client

          À la fin, si le client n'a pas accès aux scopes dont il a besoin pour faire son boulot, c'est à lui de gérer l'exception.

          Cela dit, OIDC n'est pas nécessaire parce que l'id_token ne sert pas dans ce cas, OAuth2 tout seul fait l'affaire, mais comme OIDC est une surcouche à OAuth2, les deux fonctionnent.

      • [^] # Re: Scope

        Posté par  . Évalué à 3. Dernière modification le 21 janvier 2021 à 18:35.

        La question qui doit être répondue est la suivante: Le programme X veut accéder aux ressources Y de l'utilisateur Z.

        Ça c’est ce que resolvent OAuth2 et OIDC.

        Mais de ce que je comprends de leur philosophie, c’est qu’il ne gère pas les droits d’accès. Ça c’est délégué à l’application.

        Mais il se peut que Z n'ait pas le droit d'accéder à Y tout court, genre l'utilisateur lambda n'a pas le droit d'accéder à la console d'administration, seulement à ses mails et nextcloud. Donc pour permettre ces niveaux d'accès, j'utilise les scopes.

        Ça c’est encore autre chose, et c’est souvent géré en envoyant une claim roles avec la liste des droits que l’utilisateur a dans l’application.


        Je dis pas que j’ai raison et je pense qu’OIDC est trop peu standardisé pour qu’on tombe d’accord, mais personnellement je fais comme suit et je pense que c’est plus proche de la philosophie de base :

        • Des scopes et claims classiques pour transmettre les information de l’utilisateur (avec son consentement, comme c’est prévu dans OIDC).
        • Une claim groupes pour limiter quel utilisateur a le droit d’accéder ou non à l’application (par exemple, avec --allowed-groups=monitoring dans oauth2-proxy, j’autorise seulement les membres du groupe monitoring à accéder à l’interface de Prometheus).
        • Pour les applications qui le gère, une claim roles pour donner des droits particuliers à un utilisateur dans l’application (le lien entre les roles OIDC et les droits dans l’application est géré dans la dite application).
  • # Merci et précision sur OpenLDAP

    Posté par  . Évalué à 3.

    Merci, c'est un bel article qui donne du courage pour se lancer dans le SSO :).

    Juste une note:

    on peut avoir plusieurs mots de passe pour un même compte LDAP. C'est utilisé notamment par certains systèmes pour stocker les anciens mots de passes et s'assurer que lorsqu'on demande à un utilisateur de changer son mot de passe, il n'utilise pas un ancien déjà utilisé

    En fait, c'est un attribut différent (pwdHistory, configuré dans ppolicy) qui stocke les anciens mots de passe. L'attribut habituel (ou les attributs lorsqu'on a plusieurs mots de passe comme tu l'explique parfaitement) userPassword ne sert pas d'historique.

  • # Outil de gestion OpenLDAP

    Posté par  (site web personnel) . Évalué à 0.

    Quel outil tu utilises pour gérer ton OpenLDAP ?

  • # Félicitations !

    Posté par  . Évalué à 3.

    Et avec tout ça, tu as eu besoin de te souvenir de ton mot de passe DLFP. Chapeau bas.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.