Sommaire
’Jour ’Nal,
Aujourd’hui je te présente un petit outil de mon cru, un client en ligne de commande pour le gestionnaire de mots de passe Passman.
Introduction
Passman est un gestionnaire de mots de passe qui se présente sous la forme d’une application Nextcloud. Il fournit naturellement un client web (intégré à l’interface de Nextcloud), ainsi qu’un client pour Android et une extension pour navigateurs. En revanche, il n’existe à l’heure actuelle aucun client desktop ou pour la ligne de commande.
Le projet que je présente ici vise à combler ce dernier manque — pour la ligne de commande, j’entends ; pour un client desktop graphique, il faudra attendre encore un peu longtemps.
Pebble (ou incenp-pebble pour limiter le risque de name-clash, pebble étant un nom courant) est donc un client en ligne de commande pour Passman, écrit en Python et libre (licence GPLv3). Il permet de consulter, modifier, ajouter, et supprimer des entrées (credentials) dans un coffre (vault) Passman.
Ne cherchez aucune logique derrière le nom Pebble. Je cherchais juste un nom commençant par P (pour faire un lien, aussi ténu soit-il, avec Passman et password), et il se trouve que lorsque j’ai commencé ce projet je travaillais entre autre sur un gène appelé pebble, alors voilà…
Démarrage rapide
Pebble est disponible sur PyPI et peut donc s’installer en une ligne via pip
:
$ python -m pip install incenp.pebble
Pebble se présente sous la forme d’un programme unique (pbl
) fournissant un jeu de commandes, à la manière de git
par exemple, et peut s’utiliser de deux manières :
- si une commande est précisée lors de l’invocation initiale (
pbl commande
), la commande indiquée sera exécuté, puis Pebble rendra immédiatement la main ; - si
pbl
est invoqué sans commande, un shell interactif est démarré, à partir duquel plusieurs commandes peuvent être lancées successivement sans jamais quitter le programme.
Pebble a besoin d’un fichier de configuration indiquant au minimum comment se connecter au serveur Nextcloud. Le fichier de configuration par défaut est $XDG_CONFIG_HOME/pebble/config
— un fichier différent peut être indiqué via l’option -c
. Si le fichier de configuration n’existe pas lorsque Pebble est invoqué, l’utilisatrice est invitée à fournir de quoi créer une configuration minimale:
$ pbl
Hostname: nextcloud.example.org
Username: alice
Password: cpe1704tks
Note
Il est conseillé de créer dans Nextcloud un mot de passe d’application qui sera réservé à Pebble, plutôt que d’utiliser le mot de passe principal de son compte Nextcloud.
Une fois la configuration créée, on peut alors lister les credentials dans le coffre :
pbl> list
LinuxFR.org
APRIL
FSFE
Framasoft
Puis afficher un credential donné (ou plus précisément tous les credentials correspondant au motif indiqué) :
pbl> show linux
Passphrase for vault MyVault on alice@nextcloud.example.org:
+---- LinuxFR.org (1) ----
| URL: https://linuxfr.org/
| Username: alice
| Email: alice@example.org
| Password: motdepasse123
+----
Pour modifier un credential, on utilisera logiquement la commande edit
, suivie de l’identifiant du credential (le (1) dans l’exemple ci-dessus). L’éditeur préféré de l’utilisatrice (tel qu’indiqué par la variable d’environnement EDITOR
, sinon vim
par défaut) est alors lancé pour permettre l’édition des différents champs, les modifications étant envoyées vers Passman lorsque l’éditeur est fermé. Pour ajouter un credential, même principe (avec la commande add
), sauf que les champs sont initialement vides.
Un peu de technique
Cette section présente quelques aspects du fonctionnement de Pebble. Ce n’est pas nécessaire pour utiliser Pebble, c’est juste pour comprendre un peu ce qui se passe en arrière-plan.
Pebble étant conçu dès le départ comme un client pour Passman (plutôt que comme un gestionnaire de mots de passe indépendant auquel on aurait ajouté par la suite la synchronisation avec Passman), sans surprise l’API de Passman a conditionné une bonne partie de la conception.
Le cache
Pebble obtient de Passman l’intégralité du contenu du coffre (vault) sous la forme d’un tableau JSON. Chaque entrée de ce tableau est un dictionnaire représentant un credential comme suit :
{
"credential_id": 1,
"guid": "DBA3A918-DF30-49F9-A64C-A486528EE68C",
"user_id": "alice",
"vault_id": 3,
"label": "LinuxFR",
"description": "...encrypted...",
"created": 1518182280,
"changed": 1518193542,
"tags": "...encrypted...",
"email": "...encrypted...",
"username": "...encrypted...",
"password": "encrypted...",
"url": "...encrypted...",
"icon": null,
"renew_interval": null,
"expire_time": 0,
"delete_time": 0,
"files": "...encrypted...",
"custom_fields": "...encrypted...",
"otp": "...encrypted...",
"hidden": 1,
"shared_key": null,
"compromised": null
}
Une fois téléchargé, le contenu du coffre est stocké sur disque, dans son format original (JSON donc), sous $XDG_DATA_HOME/pebble/<server>/<user>/<vault>/data
. Toutes les opérations de lecture (lister ou afficher les credentials) subséquentes utilisent ce fichier, ces opérations peuvent donc être réalisées hors ligne. Par défaut, ce cache local est rafraîchi auprès du serveur s’il date de plus de vingt-quatre heures. La durée de validité du cache peut être changée via l’option max_age
dans le fichier de configuration (par exemple, max_age: 5d
pour un cache expirant après 5 jours) ; on peut aussi forcer un rafraîchissement du cache, ou à l’inverse interdire le rafraîchissement et forcer l’utilisation d’un cache expirée, avec les options --refresh
ou --no-refresh
, respectivement.
Les opérations d’écriture (ajout, suppression ou modification d’un credential) ne passent pas du tout par le cache : elles sont directement envoyées au serveur, puis répercutées dans le cache. Ce mode de fonctionnement a été choisi par simplicité, pour éviter d’avoir à réconcilier deux versions du contenu du coffre (la version en cache et la version du serveur). L’inconvénient évident est que les opérations d’écriture imposent donc un accès au serveur, elles ne sont pas réalisables en mode hors-ligne.
Chiffrement
Comme on peut le voir dans l’exemple ci-dessus, Passman renvoit principalement des données chiffrées, à l’exception de quelques métadonnées qui sont stockées en clair (comme l’étiquette — label — associée à chaque credential, ce qui permet de chercher un credential par son étiquette sans avoir à la déchiffrer avant). Ces données sont conservées chiffrées dans le cache local de Pebble, elles ne sont déchiffrées qu’à la demande, juste avant d’afficher le contenu d’un credential (ou de l’envoyer vers l’éditeur pour modification).
Le chiffrement utilise le format proposé par la bibliothèque Javascript SJCL (Stanford Javascript Crypto Library). Brièvement, la fonction encrypt
de cette bibliothèque prend en entrée un texte clair à chiffrer et un mot de passe, et :
- dérive une clef de chiffrement symétrique à partir du mot de passe, avec l’algorithme de dérivation PBKDF2 ;
- chiffre le texte clair avec cette clef, en utilisant par défaut l’algorithme AES-256 en mode CCM ;
- encapsule le texte chiffré résultant ainsi que les paramètres requis pour le déchiffrement dans une structure JSON comme ci-dessous :
{
"v": 1,
"salt": "0KezOqk2Yks=",
"iter": 1000,
"ks": 256,
"ts": 64,
"iv": "OuObJWWL074phMiuy+YUcw==",
"cipher": "aes",
"mode": "ccm",
"ct": "w9Q6G5gGk0UJ6UJ2/Q=="
}
- encode la structure JSON produite en base64 et renvoie le résultat.
Pour déchiffrer les valeurs contenues dans les credentials renvoyés par Passman, il faut donc procéder en sens inverse :
- décoder la chaîne en base64 pour retrouver une structure JSON comme ci-dessus ;
- extraire de cette structure les paramètres de déchiffrement ;
- dériver la clef de chiffrement à partir des paramètres
salt
(sel),iter
(nombre d’itérations de PBKDF2),ks
(taille de la clef à dériver), et du mot de passe fourni par l’utilisatrice ; - déchiffrer le texte chiffré à partir des paramètres
cipher
(algo de chiffrement),mode
(mode d’opération),iv
(vecteur d’initialisation),ts
(taille du tag pour l’algorithme d’authentification), et de la clef dérivée.
Pebble confie tout ceci au module python sjcl.
Pour en savoir plus
Le code est hébergé sur ma forge personnelle. Une copie de la documentation est disponible en ligne. La documentation est aussi fournie avec les sources, et peut être trouvée dans le dossier build/sphinx/html
après avoir été construite avec python setup.py build_sphinx
.
# bon cheval ?
Posté par Mimoza . Évalué à 5.
Super boulot !
Mais je me demande si tu as misé sur le bon cheval.
Passman n'est pas l'appli officiel de Nextcloud pour les mot de passe et son développement est chaotique depuis quelques années.
Je l'ai utilisé pendant 2/3 ans et il remplissait très bien son rôle tout en ayant un niveau de sécurité assez élevé, surtout son plugin pour navigateur. Malheureusement a chaque monté de version stable de Nextcloud il n'y avait pas de version disponible, voir même trainait des pieds plusieurs semaines/mois. Et les message sur les forum n'était pas engageant quand a sa pérénité.
Donc une possible évolution de ton client, serait de le rendre compatible avec l'application officier «Password».
[^] # Re: bon cheval ?
Posté par gouttegd . Évalué à 4. Dernière modification le 27 août 2021 à 12:41.
Je sais, j’ai moi-même mes inquiétudes quant à l’avenir de Passman… Mais les choses semblent aller un peu mieux de ce côté depuis les derniers mois.
À moins que Passman ne soit définitivement abandonné sans espoir de reprise et que je me retrouve donc forcé de passer à Password, il y a franchement peu de chances que ça arrive, pour au moins deux raisons :
Pendant longtemps Password n’offrait que du chiffrement côté serveur (i.e., le serveur voit les mots de passe en clair), et même si ça a changé récemment j’ai du mal avec l’idée du chiffrement côté client (aka end to end même si à mon avis le terme est impropre ici) ajouté après coup comme un truc optionnel, là où Passman fait ça depuis le début. D’ailleurs aujourd’hui encore le chiffrement côté client est toujours optionnel et non-activé par défaut avec Password.
L’API de Password est soit très complexe, soit sous-documentée, voire les deux. Le fait est qu’en consultant la « documentation » je n’ai pas la moindre idée de comment je peux utiliser l’API pour faire quelque chose de similaire à ce que je fais avec Passman. Non, chers développeurs de services web, une liste de endpoints ne constitue pas une documentation !
[^] # Re: bon cheval ?
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 0.
Si, si, une liste de endpoints constitue une doc …pour des gens qui ne sont pas dans ton cas (comme expliqué dans le journal, tu as besoin de faire du offline et de mettre en cache un vault complet à moindre coût.)
Merci, c'est vendredi.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
# Merci
Posté par E3Ms6vyX . Évalué à 4.
Sans surprise, un journal de qualité, comme d'habitude. Merci.
# Lien incorrect
Posté par pamputt . Évalué à 4.
Les liens vers le client pour Android et l'extension pour navigateurs renvoient tous les deux vers https://github.com/nextcloud/passman-android. J'imagine que c'est incorrect pour le deuxième.
[^] # Re: Lien incorrect
Posté par Benoît Sibaud (site web personnel) . Évalué à 4.
Corrigé, merci.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.