Réinstallation de mon environnement de gestion des mot de passe sous MacOS

Dans le cadre de mon emploi, j’ai pu procéder il y a peu à un changement de mon ordinateur car je commençais à m’approcher dangereusement des limites de ses capacités de stockage. Malgré une analyse du contenu du disque dur, difficile de trouver plus d’une dizaine de giga octets de place, même après nettoyage des anciennes versions de certains logiciels (versions restées sur le disque après mise à jour). Bref, qui dit changement de matériel, dit réinstallation de mon environnement de développement et une occasion à ne pas manquer de prendre quelques notes sur les étapes et autres réglages incontournables à ne pas oublier.

Avant de préciser les étapes d’installation, quelques mots sur la manière dont je gère mes mots de passe s’imposent.

Pour la gestion des mots de passe, j’utilise pass, petit utilitaire en ligne de commande, où chaque mot de passe est stocké dans un fichier chiffré sur le disque. La sauvegarde et la synchronisation sont réalisées via git. La clé de chiffrement est stockée sur un support externe de type « smartcard », une YubiKey en l’occurrence. Après maintenant trois ans d’utilisation quotidienne, le système a fait ses preuves et me convient parfaitement. Pour plus de détails, se référer aux articles de la catégorie Crypto autour de novembre 2018.

Récapitulons les éléments nécessaires:

  • gnupg.
  • pinentry, pour la saisie du mot passe protégeant la YubiKey.
  • Une YubiKey.
  • pass.
  • browserpass, pour une utilisation simplifiée dans les navigateurs.

Première étape, installer les briques logicielles nécessaires en utilisant brew.

brew install libyubikey yubikey-personalization gnupg pinentry pinentry-mac gpg-suite-pinentry pass amar1729/formulae/browserpass

Je n’entre pas dans les détails de l’étape intermédiaire de déploiement des fichiers de configuration de mon dossier dotfiles, celle-ci étant décrite dans l’article dotfiles, git et rcm. Mentionnons tout de même que ce déploiement permet la configuration de gnupg et de la variable d’environnement indiquant le chemin vers le dossier des mots de passe à pass.

Passons à la suite.

Lors de mes premiers tests, j’obtiens l’erreur suivante: gpg: WARNING: unsafe permissions on homedir '/home/path/to/user/.gnupg'. Problème de permission sur le répertoire de configuration .gnupg, une petite recherche concernant l’erreur, me donne un gist qui propose la solution ci-dessous:

chown -R $(whoami) ~/.gnupg/
# Set 600 for files
find ~/.gnupg -type f -exec chmod 600 {} \;
# Set 700 for directories
find ~/.gnupg -type d -exec chmod 700 {} \;

Je passe ensuite à la configuration de l’extension de navigateur browserpass sur les deux navigateurs que j’utilise: Firefox et Brave. Une fois l’extension installée, il faut déployer ce qui permettra à celle-ci de dialoguer en local avec pass. Ces commandes, ou une version similaire, sont rappelées à l’installation de browserpass via brew et je conseille vivement de faire une petite relecture du Readme du projet sur GitHub pour se remettre en tête la procédure.

PREFIX='/usr/local/opt/browserpass' make hosts-firefox-user -f '/usr/local/opt/browserpass/lib/browserpass/Makefile'

PREFIX='/usr/local/opt/browserpass' make hosts-brave-user -f '/usr/local/opt/browserpass/lib/browserpass/Makefile'

Je termine en configurant au niveau de l’extension le chemin vers le répertoire des mots de passe, celle-ci n’arrivant pas à utiliser la variable d’environnement dédiée et pourtant parfaitement accessible dans un terminal.

Je pensais en avoir terminé après cette opération, mais mes mots de passe restaient inaccessibles car pass ne parvenait pas à trouver les informations de clef nécessaires au déchiffrement. Après un test rapide de l’état de ma YubiKey avec gpg --card-status pour m’assurer que celle-ci était bien lisible et reconnue, je me suis souvenue qu’il me fallait importer ma clef publique. Après transfert du fichier la contenant, j’ai donc procédé à l’import via gpg --import < publickey.txt.

Nouveau test. C’est mieux, mais le programme se plaint de ne pas savoir quelle confiance accorder à la clef que je viens d’importer et limite donc grandement son utilisation. Pas de problème ! Accordons à cette clef une confiance absolue.

gpg --edit-keys <email_address> 
gpg> trust
Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y
gpg> quit

A l’issue de ces étapes de configuration, une nouvelle tentative d’accès à l’un de mes mots de passe se traduit cette fois par une récupération réussie de celui-ci.

La réinstallation de mon environnement de gestion des mots de passe sous MacOS s’est donc déroulée sans trop de difficulté et dans un temps plus que raisonnable, puisque je ne crois pas y avoir passé plus d’une heure. Il est toujours agréable de ne pas rencontrer trop d’erreurs en réinstallant et en reconfigurant ses outils, et de retrouver son environnement habituel propre, neuf et fonctionnel en peu de temps et sans avoir à y laisser un ou deux points de santé mentale.

Et un jour, qui sait, je disposerais peut-être enfin d’un script de réinstallation pour simplifier encore le processus.

Chiffrement d’une image de container

Le chiffrement d’une image de container est un sujet assez peu évoqué. Les premiers exemples de solution n’apparaissant qu’à partir de 2019, poussé en particulier par Brandon Lum de IBM.

Le support de ces fonctionnalités ne semble pas très répandu pour le moment. Du côté des outils, nous parlons actuellement de buildah et skopeo pour la partie build, containerd et cri-o pour la partie runtime et enfin docker distribution pour la partie registry. A priori, il ne semble pas y avoir de support dans docker, mais un commentaire au détour d’une issue GitHub semblait indiquer que cela sera le cas. Je n’ai en revanche pas trouvé d’annonce ou de roadmap permettant de valider ce point.

Voici à la suite, les étapes de mon test de chiffrement d’une image de container, réalisé sous ArchLinux, mais transposable à toute autre distribution, à condition de pouvoir installer l’ensemble de dépendances.

Préparation

Installation des Dépendances

Sous ArchLinux, installation des composants:

$ sudo pacman -S docker containerd buildah podman minikube kubectl helm
$ sudo systemctl start docker
$ sudo gpasswd -a <user> docker

Registre d’image local

Démarrage d’un registre local pour y stocker l’image chiffrée que je vais générer.

$ sudo systemctl start docker  
$ sudo systemctl status docker
$ sudo docker run -d -p 5000:5000 --restart=always --name registry registry:2

Génération du couple de clé

$ openssl genrsa -out testKey.pem 2048
$ openssl rsa -in testKey.pem -pubout -out testKey.pub.pem

Création d’une image basique

$ mkdir app
$ cd app
$ nano Dockerfile
$ nano secret-file

Avec pour contenu du Dockerfile:

FROM nginx:latest
COPY secret-file /secret-file

Et secret-file, un simple fichier texte contenant une chaîne de caractère aléatoire.

Construction de l’image

$ sudo buildah bud -t encrypted-test .

Export

Export vers le registre local.

$ sudo buildah push --tls-verify=false --encryption-key jwe:../testKey.pub.pem encrypted-test localhost:5000/vvision/encrypted-test:latest

Export local dans une archive oci.

$ sudo buildah push --encryption-key jwe:../testKey.pub.pem  encrypted-test oci-archive:encrypted-test:latest

Nettoyage

Suppression de toutes les images locales.

$ sudo buildah rmi --all

Récupération de l’image

$ sudo buildah pull --tls-verify=false --decryption-key keys/testKey.pem localhost:5000/vvision/encrypted-test

Exécution

$ sudo podman run -it localhost:5000/vvision/encrypted-test
/bin/bashroot@3e3c1ccde93c:/# cat secret-file
ASecretSecret

Vérification

Tentative de récupération de l’image depuis le registre local, sans préciser la clef.

$ sudo buildah pull --tls-verify=false localhost:5000/vvision/encrypted-test Getting image source signatures
Copying blob 302a0c0a162e [--------------------------------------] 0.0b / 914.0b
Copying blob 83e2b8dcdf4b [--------------------------------------] 0.0b / 27.1MiB
Copying blob 9b30e9f5d77e [--------------------------------------] 0.0b / 617.0b
Copying blob 0f9f80250abf [--------------------------------------] 0.0b / 134.0b
Copying blob 7c9b3bc4d85b [--------------------------------------] 0.0b / 26.3MiB
Copying blob 538a8875d492 [--------------------------------------] 0.0b / 678.0b
Error decrypting layer sha256:83e2b8dcdf4bfca8bea0b23e771e84074e4cc308bf892bd6d63b3a0c9dab0564: missing private key needed for decryption

Utilisation dans Kubernetes

Démarrage d’un cluster Kubernetes avec minikube.

$ minikube start --network-plugin=cni --enable-default-cni --container-runtime=cri-o --bootstrapper=kubeadm --insecure-registry="<local_ip_addr>:5000"
$ minikube dashboard

Mécanisme de synchronisation des clefs

Installation du composant responsable déploiement de la clef de déchiffrement.

$ git clone https://github.com/IBM/k8s-enc-image-operator.git
$ cd k8s-enc-image-operator
$ kubectl create namespace enc-key-sync
$ helm install --namespace=enc-key-sync k8s-enc-image-operator ./helm-operator/helm-charts/enckeysync/

Ajout de la clé dans Kubernetes Secret

$ kubectl create -n enc-key-sync secret generic --type=key --from-file=testKey.pem test-decryption-key

Déploiement du container

$ kubectl run test-enc --image=localhost:5000/vvision/encrypted-test

Conclusion

Au moment de mes premiers tests, je ne crois pas avoir rencontré de difficulté à récupérer l’image dans le registre local. Néanmoins, lors de tests plus récents, je n’ai pas réussi à reproduire ce fonctionnement sur d’autres plateformes. En revanche, une récupération depuis le registre docker, ou celui proposé dans la Google Cloud Plateform fonctionne.

Il me reste maintenant à reproduire ce mécanisme sur un cas réel complet, c’est-à-dire, de l’intégration au processus et aux outils de CI/CD, à la configuration du cluster Kubernetes cible, avec pour objectif un déploiement de la solution en production.

Sources

Nouveau disque chiffré dédié à la sauvegarde

Ayant fait l’acquisition début d’année, d’un nouveau disque vierge d’une capacité de stockage de 1To, je me suis enfin occupé de sa configuration, afin de pouvoir y déposer les données numériques dont je souhaite absolument éviter la perte.

Mon choix s’est porté sur un disque de marque Seagate au format 2.5″, et j’ai choisi un boîtier « Mobility Disk S8 » de la marque Advance, pour protéger le disque et simplifier sa connexion comme disque externe. Esthétiquement parlant, le boîtier est très bien, sobre, en aluminium et simple dans l’installation du disque. Ce sera désormais mon choix par défaut pour les prochains disques externes dont je pourrais avoir besoin. En cas de problème sur le disque ou l’interface de connexion, il est en effet bien plus simple de changer l’un ou l’autre. Chose qui est impossible sur les disques externes tout assemblés comme ceux de Western Digital, où toute l’électronique est solidaire du disque.

Mon disque externe le plus récent de marque Western Digital. Le premier de mes disques à faire défaut.

Place ensuite à l’étape d’initialisation du disque chiffré et direction mon article « Chiffrer un support » de 2018, pour suivre la procédure et vérifier que celle-ci est toujours valable; ce qui est le cas.

Au passage, après avoir généré aléatoirement et stocké le mot de passe de la partition chiffrée dans pass, sous une arborescence spécifique crypt/usb/ et sauvegarder dans une entrée nommée suivant l’UUID du disque, j’en profite pour mettre à jour ma configuration de udiskie. udiskie, programme qui s’occupe de la gestion des médias amovibles sur mon système.

Je modifie donc le fichier .config/udiskie/config.yml pour y ajouter le contenu suivant:

program_options:
  tray: auto
  automount: false
  notify: true
  password_prompt: ["pass", "crypt/usb/{id_uuid}"]

Avec ces quelques lignes, j’indique au programme d’exécuter la commande pass pour accèder au mot de passe stocké dans crypt/usb et identifié par son UUID, d’où le choix du nommage au paragraphe précédent. UUID du disque qu’il est possible de connaître de plusieurs manières (décrites dans le wiki ArchLinux, partie Persistent block device naming – by-uuid), dont celle-ci:

$ ls -l /dev/disk/by-uuid/

On effectue ensuite un petit test pour vérifier le bon fonctionnement de la configuration via la commande:

$ udiskie-mount -r /dev/sdX

Cela fonctionne correctement. Après un redémarrage, je constate néanmoins que c’est la fenêtre de demande de mot de passe par défaut qui s’affiche lorsque j’utilise PCmanFM comme explorateur de fichier. Il me semble avoir résolu le problème en désactivant le montage automatique configuré dans PCmanFM. C’est alors bien udiskie qui prend la main.

Enfin, dernière étape incontournable pour conclure, la sauvegarde des entêtes du disque chiffré, afin de pouvoir restaurer un accès aux données chiffrées en cas de corruption des entêtes présentes sur le support. Une corruption des entêtes d’un disque chiffré entraîne en effet une impossibilité totale d’accéder à l’ensemble du contenu, et cela, même si cette partie du disque est parfaitement saine. C’est donc une opération à faire absolument, lorsqu’on décide de chiffrer un disque et on s’évitera ainsi des sueurs froides en cas de problème sur les entêtes.

Sauvegarde des entêtes LUKS

Petit pense-bête pour noter ici la procédure de sauvegarde des entêtes d’une partition chiffrée luks pour un disque nommé carbone, reconnu sur /dev/sde et stocker les informations dans une entrée de mon gestionnaire de mot de passe pass. Le tout, de la façon la plus sécurisée possible, avec l’utilisation d’un dossier monté en ram via ramfs, afin que l’information ne soit jamais écrite en clair sur le disque.

$ sudo mkdir /mnt/tmp
$ sudo mount ramfs /mnt/tmp -t ramfs
$ sudo cryptsetup luksHeaderBackup /dev/sde --header-backup-file /mnt/tmp/dump
$ sudo chown vvision:vvision /mnt/tmp/dump
$ pass insert -m crypt/luksheader/carbone < /mnt/tmp/dump
$ sudo umount /mnt/tmp
$ sudo rmdir /mnt/tmp

Procédure trouvée sur l’excellent blog de P. Hogg. Article LUKS Header Backup.

Réinitialisation du jeton cryptographique Gnuk

Après avoir réalisé quelques tests du jeton cryptographique fraîchement créé à partir d’un ST-Linkv2 et de Gnuk, j’ai cherché à nettoyer les clefs stockées sur ce jeton de test. Mes recherches m’ont guidé vers le dossier tool du dépôt gnuk, qui contient plusieurs scripts permettant de réaliser divers opérations sur le jeton. L’instruction factory-reset ne fonctionnant pas sur les jetons Gnuk, je me suis tourné vers le script gnuk_remove_keys_libusb.py. Script qui nécessite d’arrêter gpg-connect-agent avant d’être exécuté. Je n’ai au final pas pu valider son fonctionnement, pour la raison que vous découvrirez au paragraphe suivant.

gpg-connect-agent "SCD KILLSCD" "SCD BYE" /bye
./tool/gnuk_remove_keys_libusb.py -p

Lors de mes premières tentatives, je n’avais pas utilisé le paramètre p de la commande et celle-ci utilisait donc le PIN par défaut à savoir 12345678 pour le PIN admin. Ayant changé ce dernier, j’ai très vite atteint la limite d’essai autorisé et ai rendu mon jeton inopérant. Il a donc fallu s’intéresser à la façon de réinstaller Gnuk sur un jeton bloqué.

Pour effectuer la reprogrammation, il faut connecter le jeton à notre ST-Link comme dans l’article précédent. Afin de déverrouiller le jeton, il est également nécessaire de connecter les pins 7 (NRST) et 8 (VSSA) du micro-contrôleur STM8F103. Une fois les deux pins connectés, avec la pointe de mesure d’un multimètre par exemple, il faut lancer openocd comme précédemment. Une fois le programme en cours de fonctionnement, sans erreur ni tentative permanente de reconnexion, on peut arrêter de maintenir la connexion entre les deux pins.

Pour repérer les deux pins concernés, voir le schéma ci-dessous.

Pins d’un STM32F103C8T6

L’opération n’étant pas des plus évidentes, j’ai dû effectuer plusieurs tentatives avant de pouvoir reprendre la main pour reprogrammer le micro-contrôleur. Par ailleurs, impossible de me reconnecter correctement aux pins du ST-Link, j’ai donc été obligé de sortir le fer à souder, d’autant plus que le STM32 est positionné de l’autre côté de la carte et donc impossible d’y accéder pour connecter les deux pins en utilisant le précédent système de connexion.

Le montage après reprogrammation et juste avant de retirer les fils soudés.

Pas de changement dans les instructions de programmation à part l’ajout du mass_erase pour supprimer tout le contenu de la mémoire.

halt
stm32f1x unlock 0
reset halt
stm32f1x mass_erase 0
flash write_bank 0 ./src/build/gnuk-vidpid.bin 0
stm32f1x lock 0
reset halt

Après reprogrammation, le jeton est à nouveau vierge de toutes informations et prêt à recevoir de nouvelles clés!