Debian bookworm entièrement chiffrée avec LUKS2

De La Mouche VII
Aller à la navigationAller à la recherche


Le but ici est d’installer une Debian "bookworm/sid" (été 2022) sur un disque entièrement chiffré, y compris /boot. Cela était possible depuis quelques années, mais la partition contenant le noyau devait obligatoirement être chiffrée avec LUKS1 (voir [1]). GRUB 2.06 a introduit le support initial de LUKS2, nous pouvons donc à présent profiter des améliorations de LUKS2 y compris pour /boot.

Je suppose ici un démarrage EFI, tout en essayant de documenter les paramètres nécessaires à un démarrage traditionnel (BIOS) — sans les avoir testés.

Les étapes en bref

  1. Installation (non décrite ici) de Debian “bookworm” (“testing”) avec partitionnement assisté chiffré.
  2. Chiffrement de la partition /boot selon le même principe que dans [1], mais en LUKS2 avec PBKDF2 (puisque GRUB 2.06 ne supporte pas Argon).
  3. Création d’une image personnalisée de GRUB comme décrit dans [2], car GRUB 2.06 sait déchiffrer un volume LUKS2, mais n’embarque pas ce code dans le binaire EFI généré par grub-install. On va en profiter pour embarquer une carte de clavier personnalisée (bépo dans mon cas), comme décrit dans [1].

N.B. : toutes les commandes décrites ci-dessous sont à exécuter en root.

Chiffrement de /boot

Sauvegarde du contenu de /boot :

mount -oremount,ro /boot
install -m0600 /dev/null /boot.tar
tar -C /boot --acls --xattrs --one-file-system -cf /boot.tar .

Démontage de la partition :

umount /boot/efi
umount /boot

Chiffrement de la partition (N.B. : dans mon cas, /boot est sur /dev/nvme0n1p2, remplacez par le bon périphérique chez vous) :

cryptsetup luksFormat --type luks2 --pbkdf pbkdf2 /dev/nvme0n1p2
uuid="$(blkid -o value -s UUID /dev/nvme0n1p2)"
echo boot_crypt UUID=$uuid none luks,discard >>/etc/crypttab
# Ici j'en profite pour renommer le volume principal en "root_crypt" dans
# /etc/crypttab, mais ce n'est pas nécessaire.
cryptdisks_start boot_crypt

Création d’un système de fichiers sur le volume chiffré (en réutilisant l’UUID du volume initial pour éviter d’avoir à modifier /etc/fstab) :

eval $(grep 'UUID=.*/boot ' /etc/fstab | cut -d' ' -f1)
mkfs.ext2 -m0 -U $UUID /dev/mapper/boot_crypt
mount -v /boot
tar -C /boot --acls --xattrs -xf boot.tar

N.B. : contrairement à ce qui est décrit dans [1], il est inutile dans notre cas d’activer GRUB_ENABLE_CRYPTODISK dans /etc/defaults/grub car nous allons créer notre propre image de GRUB.

Création de l’image GRUB

Créer le fichier de pré-configuration de GRUB (voir ci-dessous pour les explications et les éventuelles modifications à apporter dans votre environnement) :

uuid="$(blkid -o value -s UUID /dev/nvme0n1p2 | tr -d -)" # remplacez nvme0n1p2

cat >/boot/grub/grub-pre.cfg <<EOF
terminal_input at_keyboard
keymap (memdisk)/keymap.gkb
set uuid=$uuid
cryptomount -u \$uuid
set root=crypto0
set prefix=(\$root)/grub
insmod normal
normal
EOF

Script de génération de l’image GRUB, à enregistrer par exemple dans /boot/grub/grub-install.sh :

#!/bin/sh

# Carte de clavier à utiliser (mettre par exemple "fr oss" pour le français
# AZERTY) :
KEYMAP="fr bepo"

# Paramètres pour démarrage EFI :
IMAGE_FORMAT=x86_64-efi
OUTFILE=/boot/efi/EFI/debian/grubx64.efi

# Paramètres pour démarrage BIOS traditionnel :
#IMAGE_FORMAT="i386-pc"
#OUTFILE=/boot/grub/i386-pc/core.img

GRUB_PATH=/boot/grub
GRUB_PRECONF="$GRUB_PATH"/grub-pre.cfg
MEMDISK="$GRUB_PATH"/memdisk.tar 

MODULES="part_gpt cryptodisk luks2 pbkdf2 gcry_rijndael gcry_sha256 ext2 memdisk tar terminal keylayouts at_keyboard usb_keyboard"
# Notes :
# - Ajouter (ou remplacer "part_gpt" par) part_msdos pour gérer les tables de
#   partitions DOS.
# - Remplacer ext2 par le module correspondant au système de fichiers de /boot.
# - Ajouter "lvm" si /boot est sur un volume LVM. Ajuster également le chemin
#   de grub dans grub-pre.cfg (`($root)/boot/grub` au lieu de `($root)/grub`).
# - Il est possible que "uhci" et "ehci" soient nécessaires pour un clavier
#   USB, mais quand ils sont présents le disque n'est plus détecté (même avec
#   le module ahci), donc à tester.

set -x

# Création du memdisk contenant la carte de clavier personnalisée :
memdisk_tmp="$(mktemp --tmpdir --directory)"
grub-kbdcomp -o "$memdisk_tmp"/keymap.gkb $KEYMAP
tar -C "$memdisk_tmp" -cf "$MEMDISK" .
rm -fr "$memdisk_tmp"

# Génération de l'image GRUB :
grub-mkimage -p "$GRUB_PATH" -O "$IMAGE_FORMAT" -c "$GRUB_PRECONF" -m "$MEMDISK" -o "$OUTFILE" $MODULES

Donner les droits d’exécution au script, l’exécuter, redémarrer et croiser les doigts :

chmod +x /boot/grub/grub-install.sh
/boot/grub/grub-install.sh
shutdown -r now

Explications sur grub-pre.cfg

Définition du type de clavier (at_keyboard fonctionne pour le clavier de mon ordinateur portable ; mettre usb_keyboard pour un clavier USB) :

terminal_input at_keyboard

Chargement de la carte clavier personnalisée à partir du “memdisk” créé par le script ci-dessus :

keymap (memdisk)/keymap.gkb

N.B. : les deux lignes précédentes peuvent être supprimées si l’on ne souhaite pas utiliser de carte de clavier personnalisée.

L’UUID utilisé ici est l’UUID, sans tirets, du volume chiffré contenant /boot (donc de /dev/nvme0n1p2 chez moi, pas du système de fichiers boot_crypt lui-même).

set uuid=XXXXXXXXXXXXXXXXX

Déchiffrement du volume :

cryptomount -u $uuid

Définition du volume de démarrage (celui qui contient /boot) :

set root=crypto0

Sur du LVM, on pointerait plutôt sur le LV contenant /boot :

set root=lvm/monvg-bootlv

Définition du chemin vers les fichiers de GRUB :

set prefix=($root)/grub

Penser à ajuster le chemin, par exemple si /boot est sur / :

set prefix=($root)/boot/grub

Les instructions suivantes appellent le mode “normal” (par opposition au mode “rescue”) et chargent grub.cfg :

insmod normal
normal

Trucs et astuces

Éviter de taper deux fois le mot de passe

[1] propose une méthode pour éviter de taper deux fois le mot de passe au démarrage de la machine (chapitre 4, Avoiding the extra password prompt). Il s'agit d'inclure un fichier de clef (keyfile) dans l'image initrd. J'utilisais cette méthode lorsque mon /boot était chiffré avec LUKS1, car comme expliqué dans l'article, LUKS1 ne protège pas la clef d'un volume déverrouillé (elle est accessible en espace utilisateur via la table du device mapper).

En revanche, comme LUKS2 n'a pas ce problème, avoir un fichier contenant la clef de déchiffrement, lisible depuis l'espace utilisateur une fois la machine démarrée (soit depuis /etc/keys/, soit dans l'image initrd) devient un compromis inacceptable. Je me contente donc de taper deux fois le mot de passe (et deux fois seulement, car la phrase de passe est mise en cache et est automatiquement utilisée pour déchiffrer tous les volumes ayant une phrase de passe identique).

Erreur de mot de passe GRUB

Si l'on se trompe de mot de passe à l'invite de commande de GRUB, plutôt que de redémarrer la machine, on peut simplement taper :

cryptomount $uuid
insmod normal
normal

À noter qu'il faut retenir ces commandes par cœur car grub-rescue ne propose pas de complétion.

Réinstallation inopinée de GRUB : avec un système de secours

Il arrivera fatalement à un moment où un autre que les paquets de GRUB soient mis à jour et que GRUB soit automatiquement réinstallé. Si vous ne le remarquez pas, vous aurez une petite surprise au prochain redémarrage de votre machine : une invite de commandes GRUB sur fond noir. Comme expliqué en introduction, l'image par défaut de GRUB n'embarque pas le module luks2, il est donc impossible de se dépanner en tapant des commandes, même en étant un gourou du shell GRUB.

Vous devrez démarrer la machine sur un système de secours (par exemple SystemRescue, anciennement System Rescue CD), déchiffrer vos volumes et les monter, puis exécuter notre script d'installation de GRUB depuis un chroot. Quelque chose comme ceci :

cryptsetup open /dev/nvme0n1p2 boot_crypt
cryptsetup open /dev/nvme0n1p3 root_crypt
mount /dev/mapper/rootvg-rootlv /mnt
for i in dev proc sys ; do mount -o bind /$i /mnt/$i ; done
chroot /mnt
mount -a
/boot/grub/grub-install.sh
umount -a
exit
umount /mnt/*
umount /mnt
shutdown -r now

Réinstallation inopinée de GRUB : en autonomie !

Une alternative au système de secours, c'est d'avoir une petite partition chiffrée avec LUKS1, qui contient de quoi vous dépanner.

Préparation

Si vous n'avez pas prévu le coup, vous pourrez peut-être redimensionner votre dernière partition pour vous faire de la place à la fin du disque (attention à faire les choses dans le bon ordre s'il s'agit de votre partition LVM chiffrée comme chez moi, ce n'est pas parce que je ne prends pas la peine de vous expliquer que je pourrai être tenu responsable de la perte de vos données !) ; quelques mégaoctets suffiront (/boot/grub pèse 12 Mo chez moi).

Dans l'espace libéré, créez une partition chiffrée formatée en ext2 (inspirez-vous du chapitre #Chiffrement de /boot). Vous pouvez utiliser la phrase de passe de votre choix, elle peut être différente de celle de vos volumes principaux. Montez le système de fichier et copiez-y /boot/grub.

Dans le feu de l'action

Voilà, vous avez encore passé une mise à jour sans remarquer que les paquets grub* en faisaient parti, et vous vous retrouvez devant l'invite GRUB, froide, austère, et en QWERTY. Remarquez, pas si austère que cela : ici on est dans un vrai GRUB (et pas dans un grub-rescue comme quand on se trompe de mot de passe), on a donc accès à la complétion via la touche tabulation, très pratique pour compléter les commandes, et surtout les noms de périphériques et de fichiers.

Déchiffrez d'abord votre partition de secours ; chez moi, elle est sur le disque secondaire SATA, détecté par GRUB en tant que hd0, sur la première partition, et le disque est partitionné avec GPT (pro tip : tabulez après avoir tapé « cryptomount (hd0, » pour voir la liste des partitions, cela peut aider à s'y retrouver). Cela donne donc :

cryptomount (hd0,gpt1)

Une fois la partition déchiffrée (si vous avez passé l'épreuve de taper votre mot de passe en QWERTY), elle est accessible via le périphérique crypto0, et l'on peut aller chercher le graal, c'est-à-dire le module luks2 :

set prefix=(crypto0)/grub
insmod luks2

On peut à présent déchiffrer notre vrai volume de démarrage en LUKS2 ; chez moi, il est sur le disque principal NVME, détecté par GRUB en tant que hd1, sur la deuxième partition :

cryptomount (hd1,gpt2)

Ce deuxième volume (dé)chiffré sera accessible via le périphérique crypto1 ; il suffit à présent de charger le fichier de configuration pour se retrouver dans le menu GRUB habituel :

configfile (crypto1)/grub/grub.cfg

On peut maintenant démarrer le système normalement. La bonne nouvelle c'est que pour taper le « deuxième » mot de passe, vous aurez votre carte de clavier habituelle, puisque vous serez au niveau de l'initrd, donc déjà un peu chez vous.

Éviter les réinstallation inopinées de GRUB

Pour éviter le problème entièrement, on peut tout simplement bloquer les mises à jour des paquets GRUB :

aptitude hold ~igrub

Il faudra alors surveiller les mises à jour proposées (apt vous préviendra que « X paquets ne seront pas mis à jour ») et les passer manuellement le cas échéant (sans oublier de relancer le script grub-install.sh après coup, bien sûr).

Cette solution a tout de même l'inconvénient de ne pas vous permettre d'exercer votre GRUB-fu de manière régulière ;-)

Références