Action disabled: revisions

Chiffrement total d'ubuntu 24.04 sur raspberry pi 5

date 2024/07/31 18:03
durée 1/2 journée, voire plus, dépend des données
temps de lecture  15 minutes
difficulté ★★★★★
système Raspberry PI 5
sous ubuntu 24.04 « Noble Numbat »
cible sysadmins
:!: Attention Sauvegarde complète nécessaire, tout peut être perdu !
Références https://github.com/brutus-downunder/LUKS-on-Raspberry-Pi4/blob/main/index.md
https://rr-developer.github.io/LUKS-on-Raspberry-Pi/
https://www.kali.org/docs/arm/raspberry-pi-with-luks-full-disk-encryption-2/
déverouiller un disque luks avec dropbear ssh
github : Générer une clé ssh et l'ajouter à l'agent
digital ocean: configurer une authentication par clé ssh

Le raspberry pi 5 dispose d'instructions AES dans son processeur. Cela permet une accélération des opérations de chiffrement déchiffrement telle qu'on peut envisager sereinement d'utiliser un système chiffré. La seule partie qu'on ne peut pas chiffrer est la partition d'amorçage. L'EEPROM du pi ne sachant pas déchiffrer un disque, il faut bien amorcer la pompe à un moment… Mais ce n'est pas bien grave ; au pire qqu'un sait quel noyau vous utilisez.

Cet article décrit une méthode que j'ai mise au point en m'inspirant d'autres, merci donc à vous tous qui avez tenté l'opération avant moi. Je n'aurais pas osé sans vous.

Cet article donne en une seule lecture :

  1. la méthode pour chiffrer un système
  2. la méthode pour ajouter dropbear ssh dès la phase de boot, ce qui permet de déverrouiller le système chiffré via une connexion ssh, et donc d'avoir un PI sans tête, comprendre sans clavier ni écran

Cet article se concentre sur le chiffrement de la partition racine du système. Si vous avez d'autres disques, des disques de données, ou d'autres partitions sur votre système, ceux là ne seront pas chiffrés. C'est possible de les chiffrer, ça n'est pas bien difficile, mais ce n'est pas l'objet de cet article.

évaluation de la rapidité de Luks sur Raspberry PI 5 et 4

Je profite de cet article pour indiquer les performances relatives des carte raspberry PI 5 et 4 concernant le chiffrement.

sur un raspberry pi 5

root@rpi5:~ (0) # cryptsetup benchmark
# Tests are approximate using memory only (no storage IO).
PBKDF2-sha1      1134822 iterations per second for 256-bit key
PBKDF2-sha256    2101354 iterations per second for 256-bit key
PBKDF2-sha512     936228 iterations per second for 256-bit key
PBKDF2-ripemd160  613920 iterations per second for 256-bit key
PBKDF2-whirlpool  262144 iterations per second for 256-bit key
argon2i       4 iterations, 638296 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
argon2id      4 iterations, 636820 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
#     Algorithm |       Key |      Encryption |      Decryption
        aes-cbc        128b       988.7 MiB/s      1638.5 MiB/s
    serpent-cbc        128b        70.7 MiB/s        79.9 MiB/s
    twofish-cbc        128b       115.9 MiB/s       122.3 MiB/s
        aes-cbc        256b       814.8 MiB/s      1396.8 MiB/s
    serpent-cbc        256b        72.8 MiB/s        80.0 MiB/s
    twofish-cbc        256b       120.0 MiB/s       122.2 MiB/s
        aes-xts        256b      1365.0 MiB/s      1365.2 MiB/s
    serpent-xts        256b        74.5 MiB/s        83.3 MiB/s
    twofish-xts        256b       126.4 MiB/s       129.2 MiB/s
        aes-xts        512b      1201.8 MiB/s      1203.1 MiB/s
    serpent-xts        512b        75.9 MiB/s        82.9 MiB/s
    twofish-xts        512b       128.2 MiB/s       129.2 MiB/s
root@rpi5:~ (0) # 

Sur un raspberry pi 4

root@rpi4:~ (0) # cryptsetup benchmark
# Tests approximatifs en utilisant uniquement la mémoire (pas de stockage E/S).
PBKDF2-sha1       248713 itérations par seconde pour une clé de 256 bits
PBKDF2-sha256     416763 itérations par seconde pour une clé de 256 bits
PBKDF2-sha512     224823 itérations par seconde pour une clé de 256 bits
PBKDF2-ripemd160  201030 itérations par seconde pour une clé de 256 bits
PBKDF2-whirlpool   57487 itérations par seconde pour une clé de 256 bits
argon2i       4 itérations, 289342 mémoire, 4 threads parallèles (CPUs) pour une clé de 256 bits (temps de 2000 ms demandé)
argon2id      4 itérations, 293225 mémoire, 4 threads parallèles (CPUs) pour une clé de 256 bits (temps de 2000 ms demandé)
#    Algorithme |       Clé |     Chiffrement |    Déchiffrement
        aes-cbc        128b        65,9 MiB/s        86,5 MiB/s
    serpent-cbc        128b               N/D               N/D
    twofish-cbc        128b        24,0 MiB/s        61,5 MiB/s
        aes-cbc        256b        64,8 MiB/s        67,1 MiB/s
    serpent-cbc        256b               N/D               N/D
    twofish-cbc        256b        61,3 MiB/s        61,6 MiB/s
        aes-xts        256b        85,1 MiB/s        90,0 MiB/s
    serpent-xts        256b               N/D               N/D
    twofish-xts        256b        60,7 MiB/s        62,9 MiB/s
        aes-xts        512b        68,6 MiB/s        69,6 MiB/s
    serpent-xts        512b               N/D               N/D
    twofish-xts        512b        62,6 MiB/s        62,6 MiB/s
root@rpi4:~ (0) # 

1365.0 Mo/s soit plus de 1,3650 Go/s pour la PI 5 contre 85,1 Mo/s pour la PI 4. Cela est du aux instructions AES présentes dans le CPU de la PI 5 et absente de la PI 4.

root@rpi5:~ (0) # grep -e ^Feature -e ^BogoMIPS -e ^Model /proc/cpuinfo | tail -3
BogoMIPS	: 108.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
Model		: Raspberry Pi 5 Model B Rev 1.0
root@rpi5:~ (0) # 
root@rpi4:~ (0) # grep -e ^Feature -e ^BogoMIPS -e ^Model /proc/cpuinfo | tail -3
BogoMIPS	: 108.00
Features	: fp asimd evtstrm crc32 cpuid
Model		: Raspberry Pi 4 Model B Rev 1.1
root@rpi4:~ (0) # 

Aperçu de la méthode

Le chiffrement sous Ubuntu/GNU Linux se fait via Luks. Il n'existe pas de méthode pour “activer” le chiffrement sur un système existant, contrairement à ce qui se fait sous macos avec Filevault ou Windows avec bitlocker, La méthode est grossière et consiste à :

  1. copier la partition racine qq part en lieu sûr
  2. chiffrer et formater la partition racine
  3. modifier l'initrd
  4. restaurer la partition racine à partir de la copie.

Avec un peu de détails, ça donne ceci :

  • Avec n'importe quelle machine, préparer un support alternatif, une clé/disque USB, amorçable contenant un système Linux de votre choix
  • éteindre le PI
  • retirer le support d'amorçage habituel, par exemple la carte micro SD
  • amorcer la carte Raspberry PI 5 sur le support alternatif nouvellement créé,
  • insérer le support d'amorçage habituel, par exemple la carte micro SD
  • Recopier toute la partition racine du système habituel sur un autre support (la clé sur laquelle vous avez amorcé si assez grosse, un disque réseau, un autre disque USB (le PI a 4 ports, on a largement de quoi faire) etc..,
  • initialiser le système de chiffrement sur le périphérique habituel, et créer un volume chiffré, ce qui détruit TOUTES les données instantanément
  • formater le volume chiffré dans le système de fichiers Linux de votre choix (ext4, xfs…)
  • Recopier dans le sens inverse toutes les données de la partition racine du système
  • Adapter un ou deux fichiers de config
  • déballer l'initramfs de la partition d'amorçage
  • le modifier un tantinet pour qu'il comprenne que le disque est chiffré
  • reconstruire l'initramfs, le mettre en place
  • redémarrer sur le système chiffré.
  • déverrouiller le disque
  • reconstruire l'initramfs de manière automatique pour avoir la certitude que les mises à jour de noyau se passeront bien
  • rebooter
  • C'est validé !
  • dernière étape, dropbear ssh, Les difficultés sont derrière vous à ce moment.

NB
Si vous voulez faire ça sur un PI neuf, commencez par installer ubuntu 24.04 (ou ultérieur) sur la carte micro SD, avec le logiciel Raspberry PI Imager. bootez une fois ou deux sur ce système, histoire de terminer rapidement sa configuration. N'en faites pas trop et n'ajoutez pas de paquets, ça ne ferait que rallonger cette procédure inutilement.

Matériel nécessaire

Il va falloir disposer du matériel suivant :

  • La carte raspberry pi 5
  • Le support du système ubuntu Gnu/Linux 24.04 (au minimum) installé. La carte micro SD par exemple, ou autre chose
  • Une clé USB prête qu'on va formater avec un système Gnu/Linux temporaire
  • Éventuellement un support supplémentaire capable d'accueillir une copie de la partition racine de votre système si ça ne peut pas tenir sur la clé USB temporaire.
  • Un écran et un clavier à brancher au PI, Au minimum un clavier. À un moment en effet, on devra déverrouiller le disque avec le clavier. quand tout sera fini on pourra déverrouiller le disque par le réseau, mais initialement, on aura besoin du clavier.

Logiciels utilisés

Côté logiciel, il faut :

  • Le Raspberry PI Imager pour préparer la clé usb temporaire. On peut utiliser n'importe quel ordinateur pour réaliser cette opération.

Mise en pratique

J'ai réalisé ces opérations sur une carte Raspberry PI 5 avec

  • une carte micro SD de 64 G à peine remplie car ne contenant qu'un ubuntu 24.04 serveur tout juste installé
  • une clé USB de 32 Go, largement suffisante pour un os temporaire et la copie d'ubuntu serveur.

Dans toute la suite de cet article, /dev/mmcblk0 désigne la carte micro SD avec le système ubuntu GNU/Linux qu'on veut chiffrer ; et /dev/sda désigne la clé USB avec le système temporaire. Adaptez les commandes à votre cas.

C'est parti !

Commande sous le compte root

Pour économiser mes petits doigts, et ne pas saisir sudo toutes les deux lignes, toutes les commandes shell sont passées sous le compte root. donc, devenez root, immédiatement et complètement. par exemple avec

sudo -i

La commande id doit vous répondre que vous êtes root

root@leia:~# id
uid=0(root) gid=0(root) groups=0(root)

installation d'un OS temporaire sur une clé USB

À l'aide du Raspberry PI Imager, j'installe le système officiel «Raspberry PI OS (64-bit)» sur une clé USB. Pourquoi un système différent ? Pour reconnaître facilement le système sur lequel je me trouve, et ne pas, par étourderie commettre de boulettes irréparables. Aussi, parce que c'est cool, qu'on est par défaut connecté avec une interface graphique légère et efficace, et que c'est le premier sur la liste.

Je retire la carte micro SD, je boote une ou deux fois sur la clé USB. Un coup de raspi-config pour activer ssh, ça ne mange pas de pain et ça peut servir.

Comme je compte utiliser XFS et non pas ext4 sur le système chiffré, j'ajoute le paquet xfsprogs. Question de goûts personnels.

sudo apt-get install xfsprogs

montage de la carte sd, copie de la partition racine, de l'initrd

  • Booter la carte PI 5 sur le système alternatif, et s'y connecter
  • insérer la carte micro SD
  • copier la carte micro SD sur un dossier. La copie se fait par rsync avec les options -a -H -x –numeric-ids et des options de visualisation du progrès. Les options aHx et numeric-ids doivent être utilsées, pour le reste faites comme bon vous semble. Je copie tout dans /root/system.org, car j'ai la place à cet endroit. Ça peut durer plus ou moins longtemps…
    install -d -m 0 /root/{system.org,initramfsunpacked.org} /mnt/{0..9}
    umount /dev/mmcblk0p1 2>/dev/null
    umount /dev/mmcblk0p2 2>/dev/null
    mount /dev/mmcblk0p2 /mnt/2
    rsync -aHx --numeric-ids --info=progress2 /mnt/2/. /root/system.org/.
  • Déballer l'initrd de la carte micro SD, on s'en servira plus tard
    ummkinitramfs /mnt/1/initrd.img /root/initramfsunpacked.org

Créer et formater le volume chiffré

Pour l'instant, on va choisir une phrase de passe stupidement courte. Et également utiliser une lettre qui a le même emplacement quel que soit le clavier chargé (QWERTZ, QWERTY, AZERTY etc…). On pourra facilement changer la phrase de passe à la fin de cette procédure.

Je propose d'utiliser t.

umount /dev/mmcblk0p2
cryptsetup luksFormat /dev/mmcblk0p2

Puis on ouvre et formate le volume chiffré. J'utilise le nom crypted-root, mais ce nom étant libre, utilisez ce que bon vous semble.

cryptsetup luksOpen /dev/mmcblk0p2 crypted-root

Le volume chiffré est maintenant ouvert, et disponible pour la suite des opérations. Il est temps d'installer un système de fichier sur ce volume. Chacun fera comme il veut, j'aime bien XFS, mais utilisez ext4 si c'est votre système préféré.

mkfs.xfs -f /dev/mapper/crypted-root -L "racine pi 5"

prompt et commandes

Recopier le système

On peut maintenant recopier le système d'origine sur la partition chiffrée

  1. Monter le nouveau FS
    mount /dev/mapper/crypted-root /mnt/2
  2. recopier le système
    rsync -aHx --numeric-ids --info=progress2 /root/system.org/. /mnt/2/.

Ajuster les fichiers de configuration

On ajuste alors sur la carte micro sd les fichiers de configuration suivants

  1. /mnt/2/etc/crypttab
    L'option tries=0 offre un nombre infini d'essais d'ouverture du volume chiffré, ce qui nous rendra service quand on utilsera ssh pour ouvrir le volume. Pas question de se trouver bloqué au bout de trois tentatives infructueuses, avec un système distant qu'on ne peut même pas redémarrer.
    Quant au contenu, c'est au choix, directement le nom de partition ou la partition référencée par son UUID. N'utiliser que l'une des deux options
    1. crypted-root /dev/mmcblk0p2 none luks,tries=0
    2. crypted-root PARTUUID=0529037a-02 none luks,tries=0

      NB
      On détermine facilement l'uuid de partition correspondant à /dev/mmcblk0p2, par exemple avec cette commande

      ls -l /dev/disk/by-partuuid/ | awk '/mmcblk0p2/ { print $ (NF - 2) }'
  2. /mnt/2/etc/fstab
    /dev/mapper/crypted-root	/	xfs	defaults	0	1
  3. /mnt/1/cmdline.txt
    On change le nom du périphérique racine root=… et éventuellement, le système de fichiers rootfstype=…
    console=serial0,115200 multipath=off dwc_otg.lpm_enable=0 console=tty1 root=/dev/mapper/crypted-root rootfstype=xfs rootwait fixrtc cfg80211.ieee80211_regdom=FR

Ajuster le fichier /cryptroot/crypttab de l'initrd

  1. fusionner les composants de l'initrd et insérer le fichier /cryptroot/crypttab dans l'arborescence
    install -d -m 0 /tmp/remkinitrd
    rsync -aHx /root/initramfsunpacked.org/early/. /tmp/remkinitrd/.
    rsync -aHx /root/initramfsunpacked.org/main/. /tmp/remkinitrd/.
    cp /mnt/2/etc/crypttab /tmp/remkinitrd/cryptroot/crypttab
  2. recréer un fichier initrd.img et le mettre en place
    cd /tmp/remkinitrd &&
    find .r* * | cpio -o -H newc | gzip -9 >/tmp/new-initrd.img
    test -r /mnt/1/initrd.img.org || mv /mnt/1/initrd.img /mnt/1/initrd.img.org
    mv /tmp/new-initrd.img /mnt/1/initrd.img

On peut maintenant redémarrer. Le système va demander la phrase de passe et on va répondre t.

reboot

déverouillage à distance avec dropbear ssh

  • Commençons par installer le logiciel
    apt-get install dropbear-initramfs
  • On supprime les clés d'hôtes suspicieuses NIST/NSA. Le script postinst de dropbear-initramfs ne crée des clés que si aucune clé d'hôte n'existe. Donc, pour les mises à jour, notre réglage tiendra.
    rm /etc/dropbear/initramfs/dropbear_ecdsa_host_key
  • Pour joindre le raspberry pi par ssh, il lui faut une adresse IP. En l'absence de configuration, les scripts d'initramfs tenteront d'en obtenir une par DHCP. Si on veut une adresse IP fixe, ce qui est une bonne idée, il suffit de créer un fichier dans /etc/initramfs-tools/conf.d indiquant l'IP, la passerelle, le masque de sous réseau et le nom d'hôte. Comme cette configuration réseau va persister, il faut également ajouter les serveurs DNS. Tout cela comme une déclaration de variable. Utilisez les valeurs qui conviennent à votre cas. On pourrait créer un fichier static-wired-ip.conf contenant ceci :
    /etc/initramfs-tools/conf.d/static-wired-ip.conf
    # Voir https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt
    IP=172.131.1.57::172.131.1.1:255.255.255.0:leia:::172.131.1.4:1.1.1.1:
    • IP 172.131.1.57,
    • passerelle 172.131.1.1,
    • le masque 255.255.255.0
    • nom d'hôte de la carte pi : leia,
    • DNS1 172.131.1.4 (dns de mon réseau)
    • DNS2 1.1.1.1 (cloudflare)
  • Pour autoriser l'accès, on ajoute un fichier de clés autorisées authorized_keys. Le format est le même que celui d'openssh, une clé par ligne. Voilà un exemple réel avec l'une de mes clés
    ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH7tTbt9XLZ0bOi4+dNFIFUv8cuM7/TphfnLfjuR58/q ma clé

    Si vous n'y connaissez rien en clé ssh, je vous conseille d'utiliser des clés ed25519 et d'utiliser un agent. Deux liens sont dans les références de ce billet.

  • Il ne reste qu'à reconstruire l'initrd pour y incorporer tout ça et redémarrer pour vérifier.
    update-initramfs -c k $(uname -r)
    reboot

    Tout doit toujours fonctionner.

Effacement de la configuration réseau de boot

Lorsque dropbear a terminé sa config, par défaut, il déconfigure l'interface réseau. Le module networking du boot va le réactiver de toute façons. C'est donc une bonne chose.

Mais, l'un des composants d'initramfs se dit « Tiens ! j'ai une configuration réseau, je vais donc générer un fichier /run/netplan/xxxx.yaml pour que cette configuration soit reprise dans les phases suivantes. » FBI. Fausse Bonne Idée. Pour plusieurs raisons,

  1. La configuration générée est assez moche et provoque des warnings, mais c'est pas si grave,
  2. La configuration réseau habituelle dans /etc/netplan sera appliquée dans les étapes suivantes de boot. Et cette première configuration générée vient se mélanger et finalement prends le pas sur la configuration habituelle.

    C'est très gênant car on ne peut plus modifier la configuration réseau depuis /etc/netplan :-(.

    Voir les messages d'erreurs provoqués par la présence de cette configuration quand on tente d'appliquer notre configuration

  3. On n'a pas précisé de domaine de recherche par défaut, car on ne peut pas. Et si on voulait le faire dans /etc/netplan, et bien, on ne pourrait pas.

Pour ces raisons, je propose qu'on se débarrasse de la configuration générée. Il faut, à la fin de l'exécution des scripts d'initramfs, supprimer les fichiers de /run/netplan.

La solution consiste à créer un fichier /etc/initramfs-tools/scripts/init-bottom/deconfnet avec ce contenu :

/etc/initramfs-tools/scripts/init-bottom/deconfnet
#!/bin/sh
 
PREREQ="dropbear"
 
prereqs() {
  echo "$PREREQ"
}
 
case "$1" in
  (prereqs)
    prereqs
    exit 0
  ;;
esac
 
rm -f /run/netplan/* >/dev/null || :

Considérations variées

On se retrouve maintenant avec des clés ssh pour la phase de boot qui seront différentes des clés ssh d'openssh une fois la machine bootée. Pour éviter les problèmes de «Offending key…», on a deux solutions.

  1. Créer une configuration ssh particulière sur le PC qu'on utilise habituellement pour se connecter en ssh au raspberry pi. C'est très simple, on crée ou modifie le fichier ~/.ssh/config sur son pc perso de connexion à la carte raspberry pi et on y ajoute une section concernant la carte raspberry pi en cours de boot. Mon pi s'appelle leia, j'utilise le nom leiaboot pour cette phase de boot.
    ~/.ssh/config.extrait
    Host leiaboot
      user root
      Hostname 172.131.1.57
  2. Utiliser les mêmes clés. On convertit les clés d'openssh en clé dropbear.
    rm /etc/dropbear/initramfs/dropbear_*key
    dropbearconvert openssh dropbear \
      /etc/ssh/ssh_host_ed25519_key \
      /etc/dropbear/initramfs/dropbear_ed25519_host_key
    dropbearconvert openssh dropbear \
      /etc/ssh/ssh_host_rsa_key \
      /etc/dropbear/initramfs/dropbear_rsa_host_key

La deuxième chose à réaliser est que l'utilisation de ssh n'est pas la panacée. Si un ou une attaquante dispose d'un accès physique à la carte raspberry, elle peut tout à fait vous berner très facilement. Il suffit d'éteindre le pi et de déballer l'initrd. Ce dernier se trouve en effet sur une partition FAT non chiffrée. L'attaquante ou l'attaquant n'a plus qu'à placer ces clés sur une carte micro SD préparée à l'avance. Lorsque vous vous connecterez en ssh pour déverrouiller le disque, vous serez en fait connecté sur le système piégé. Il sera simplement impossible de s'en rendre compte, car ce système disposant alors de vos clés ssh répondra comme vous vous y attendez. Sauf qu'au lieu de déverrouiller votre disque chiffré, vous donnerez la clé de déchiffrement à l'attaquante, qui pourra alors inspecter votre disque sans problème.

Changement de la phrase de passe

On a mis t comme phrase de passe, c'est pas terrible. Il est temps de la changer. En manque d'inspiration ? Allez donc voir xkcd 936.

:!: il faut utiliser printf %s, non pas echo, sinon la phrase de passe contiendra un retour chariot qu'on ne peut pas saisir au clavier, puisqu'il valide la saisie.


Changement de la phrase de passe. Dans l'expemple, la nouvelle phrase de passe sera exact cheval pile agrafe :

printf %s "exact cheval pile agrafe" >fichier-de-passe
cryptsetup luksChangeKey /dev/mmcblk0p2 fichier-de-passe

Et encore un reboot. Ça passe ou ça casse. Si ça casse, tout est perdu, et il faut recommencer.

Sauvegarde des en-têtes luks

S'il advient malheur aux secteurs de l'en-tête luks de la partition chiffrée, alors cette partition est verrouillée à tout jamais, et devient aussi utile qu'une brique (ou un timbre poste, attendu qu'on parle ici d'une carte µSD).

On peut sauvegarder ces en-têtes. Il suffit d'en faire un vidage et de les mettre en sécurité qq part. Le fichier à une taille d'exactement 16 MiB.

Si un attaquant trouve le fichier contant le vidage d'en-tête, il va pouvoir commencer une attaque par force brute, comme s'il avait volé le disque. Comme le chiffrement est censé résister au moins plusieurs années à ce genre d'attaque, c'est pas si grave, mais pas super non plus. Ici s'arrêtent mes compétences concernant la résistance du chiffrement. Si ma copie de sauvegarde était divulguée, je rechiffrerais immédiatement le disque.

cryptsetup luksHeaderBackup /dev/mmcblk0p2 --header-backup-file luks-root-header

Et voilà, le fichier luks-root-header contient une copie des entêtes. Ne reste plus qu'à le mettre en sécurité.