Préambule
Dans cet article je vais montrer comment setup une boot chain assez propre sur Fedora avec :
- Secure Boot activé
- mes propres clés Secure Boot avec
sbctl - les clés Microsoft gardées pour éviter de casser la compatibilité
- un UKI booté directement
- une UKI mesurée dans le TPM
- un disque LUKS unlock via TPM2 + PIN
- une policy PCR11 signée pour éviter de devoir refaire le setup à chaque update kernel
En gros, si quelqu’un modifie l’UKI, change la cmdline, boot un autre kernel, ou casse la policy attendue, le TPM ne donne pas le secret et on retombe sur la passphrase/recovery key LUKS.
Pour la définition de certains termes c’est ici
Contexte
⚠️
Petit disclaimer avant de continuer.Dans ce setup, je garde les clés privées Secure Boot et la clé privée utilisée pour signer la PCR policy directement sur la machine. C’est pratique pour automatiser les mises à jour, reconstruire les UKI et signer les nouvelles mesures PCR11, mais ce n’est pas le modèle le plus propre niveau sécurité.
Dans l’idéal, les clés privées ne devraient pas rester stockées sur le PC qu’elles servent à protéger. Si quelqu’un compromet la machine avec assez de privilèges, il pourrait potentiellement récupérer ces clés ou les utiliser pour signer des binaires EFI / UKI, ou encore signer une nouvelle policy PCR autorisant un état de boot qu’on ne voulait pas autoriser.
Une approche plus propre serait de stocker ces clés de signature sur un support matériel externe, par exemple une YubiKey ou un autre support matériel sécurisé compatible avec la signature cryptographique. L’objectif est que la clé privée ne quitte jamais ce support, et qu’elle soit utilisée uniquement au moment de signer une nouvelle UKI ou une nouvelle PCR policy.
Il serait aussi possible d’aller plus loin en supprimant les clés Microsoft de la base Secure Boot, afin que seuls les binaires signés avec mes propres clés soient autorisés à booter. Je ne l’ai pas fait ici pour garder une compatibilité plus simple avec certains binaires EFI, mais c’est une option intéressante pour durcir encore la chaîne de boot.
Pour l’instant, je garde volontairement une approche plus simple et plus facile à reproduire, parce que le but de cet article est d’abord de comprendre et mettre en place la chaîne Secure Boot + UKI + TPM2 + LUKS.
Si je me procure une YubiKey plus tard, je ferai sûrement un autre tuto dédié pour expliquer comment déplacer les clés de signature dessus et rendre le setup plus propre niveau OPSEC.
Avant de rentrer dans les commandes, il faut comprendre pourquoi faire ce setup.
Le but ici ce n’est pas juste d’activer Secure Boot pour avoir un joli SecureBoot enabled dans le terminal. Le vrai sujet, c’est la protection des données personnelles.
Le problème classique, c’est le scénario “evil maid” : quelqu’un peut avoir accès à ton PC pendant que tu n’es pas là, modifier le bootloader, modifier l’initramfs, changer la ligne de commande kernel, ou préparer un boot piégé pour capturer ton mot de passe LUKS au prochain démarrage.
Donc l’idée de ce setup, c’est d’avoir une chaîne de boot plus propre :
- Secure Boot vérifie que le binaire EFI lancé est bien signé.
- L’UKI permet d’avoir le kernel, l’initramfs et la cmdline dans un seul fichier EFI signé.
- Le TPM mesure l’état du boot dans des PCR.
- LUKS ne se déverrouille via le TPM que si l’état mesuré correspond à ce qu’on attend.
- Et on ajoute quand même un PIN TPM pour éviter qu’un simple boot automatique suffise.
En gros, je veux que le disque ne se déverrouille pas juste parce que “c’est mon PC”. Je veux qu’il se déverrouille seulement si la machine boot dans un état connu, avec un firmware attendu, Secure Boot actif, une UKI signée, et une mesure TPM valide.
Ce n’est pas une sécurité magique. Si l’OS est déjà compromis pendant qu’il tourne, ce setup ne va pas sauver la session. Si quelqu’un connaît ta passphrase LUKS, pareil. Par contre, ça renforce énormément la partie “avant boot” et ça rend les attaques physiques beaucoup plus compliquées.
Requirements
J’étais sur Fedora avec :
|
|
Installation des outils :
|
|
Pour sbctl, sur mon Fedora le paquet n’était pas dispo directement donc j’ai utilisé un COPR :
|
|
État de départ
Déjà je check l’état de Secure Boot :
|
|
Chez moi, au début, j’étais en setup mode :
|
|
Donc parfait, on peut enroll nos propres clés.
Mon disque était déjà en LUKS :
|
|
Setup secure boot avec ses propres clés
On crée les clés Secure Boot :
|
|
Ce qui donne :
|
|
Ensuite j’enroll mes clés + les clés Microsoft :
|
|
Pourquoi garder Microsoft ?
Parce que sinon tu peux te retrouver à casser certains bootloaders, certaines options firmware, Windows Boot Manager, certains firmwares ou périphériques qui s’attendent encore aux clés Microsoft.
Après reboot :
|
|
Résultat attendu :
|
|
Le kernel passe aussi en lockdown mode :
|
|
Ici [integrity] veut dire que le mode lockdown actif est integrity.
Signer les EFI Fedora
Avant de passer à l’UKI, j’ai signé les binaires EFI classiques :
|
|
Puis :
|
|
On peut voir les fichiers signés :
|
|
Il restait aussi des fichiers IA32, mais vu que ma machine boot en x64 je m’en fous un peu.
Création de l’UKI
Maintenant on passe au truc intéressant : l’UKI.
Une UKI, c’est une Unified Kernel Image. En gros au lieu d’avoir :
|
|
on va avoir un fichier .efi qui contient directement :
|
|
Donc le firmware boot directement un fichier EFI Linux.
Config de base :
|
|
Puis config de kernel-install :
|
|
On génère l’UKI pour le kernel actuel :
|
|
On check :
|
|
Chez moi ça a donné :
|
|
On vérifie la signature Secure Boot :
|
|
Résultat attendu :
|
|
Entry EFI pour booter l’UKI
J’ai ensuite créé une entrée EFI directe :
|
|
Puis check :
|
|
Après reboot sur cette entrée :
|
|
Là le point important c’est :
|
|
Donc on boot bien sur l’UKI et systemd-stub mesure l’UKI dans le TPM.
Avoir une entrée stable linux.efi
Le problème avec les UKI versionnées, c’est que le nom change à chaque kernel update.
Donc j’ai fait un hook kernel-install qui copie toujours la dernière UKI vers :
|
|
Comme ça mon entrée EFI reste stable.
|
|
Puis :
|
|
On vérifie :
|
|
Ensuite je crée une entrée EFI stable :
|
|
Puis je mets seulement cette entrée en boot order :
|
|
À adapter avec ton ID EFI évidemment.
LUKS + TPM2
Au début j’ai tenté un truc simple :
|
|
Sauf que ce n’est pas pratique.
Pourquoi ?
Parce que PCR11 change dès que l’UKI change. Donc à chaque update kernel, ton TPM ne déverrouille plus LUKS.
C’est logique mais chiant.
Ce qu’on veut, c’est :
|
|
En gros :
- PCR7 vérifie l’état Secure Boot / clés firmware
- PCR11 vérifie l’UKI, mais via une policy signée, donc les updates kernel restent possibles
PCR signature pour éviter de casser à chaque update
Pour ça, il faut une clé de signature PCR.
J’ai d’abord essayé RSA 4096. Mauvaise idée dans mon cas : mon TPM n’a pas aimé.
Ensuite j’ai essayé EC prime256v1.
Ça avait l’air plus propre, mais au boot j’avais :
|
|
En décodant :
|
|
Résultat :
|
|
Donc mon TPM annonçait bien ecdsa, mais il refusait le scheme exact utilisé ici par systemd/TPM pour VerifySignature.
Finalement la solution qui marche chez moi : RSA 2048.
|
|
Vérification :
|
|
Résultat :
|
|
Ajout de la PCR signature dans uki.conf
Dans /etc/kernel/uki.conf, j’ai ajouté :
|
|
Chez moi le fichier final ressemble à ça :
|
|
On régénère :
|
|
On check l’UKI :
|
|
On doit voir :
|
|
Enroll LUKS avec TPM2 + PIN + PCR7 + PCR11 signée
Ajout d’une recovery key :
|
|
Ensuite on wipe l’ancien token TPM :
|
|
Et on enroll le nouveau :
|
|
Ce que ça veut dire :
|
|
On bind directement sur PCR7, donc l’état Secure Boot / clés firmware.
|
|
On bind PCR11 via une policy signée. Donc PCR11 peut changer avec les updates kernel, tant que la nouvelle UKI contient une signature valide générée par notre clé PCR.
Vérification :
|
|
On veut voir :
|
|
Résultat final
Après reboot, j’ai :
|
|
avec :
|
|
Et au boot :
|
|
Donc le flow final est :
|
|
Et si quelqu’un modifie linux.efi ?
Cas 1 : modification bête du fichier.
|
|
Cas 2 : attaquant remplace par une autre UKI signée par une clé autorisée Secure Boot.
|
|
Cas 3 : attaquant a aussi ta clé PCR privée.
|
|
Là, il manque encore le PIN TPM, mais oui, la clé PCR privée devient très sensible.
Est-ce que les updates kernel vont casser le setup ?
Normalement non.
C’est justement le but de la PCR11 signée.
À chaque update kernel :
kernel-installgénère une nouvelle UKIukifysigne l’UKI Secure Bootukifyajoute la.pcrsig- mon hook copie la dernière UKI vers
/boot/efi/EFI/Linux/linux.efi - l’entrée EFI stable continue de booter
linux.efi
Donc je n’ai pas besoin de refaire systemd-cryptenroll à chaque update kernel.
Par contre, si je change :
- les clés Secure Boot
- la clé PCR
/etc/systemd/tpm2-pcr-*.pem - la config
/etc/kernel/uki.conf - la cmdline kernel
- l’état Secure Boot dans le BIOS
- le TPM est reset
- ou certaines options firmware qui changent PCR7
là oui, je peux devoir rentrer la passphrase/recovery et refaire le token TPM.
Update : rebuild automatique de l’UKI après certaines updates
En fait, kernel-install est bien appelé automatiquement quand le paquet kernel-core est installé ou supprimé.
On peut le voir avec :
|
|
Dans mon cas, kernel-core contient bien un truc du genre :
|
|
Ça c’est OK.
Mais il y a un cas un peu plus relou : les updates de modules.
Par exemple kernel-modules ne relance pas forcément kernel-install de base. Il peut juste faire :
|
|
Sauf que moi je ne boot pas vraiment sur /boot/initramfs-*.img.
Je boot sur :
|
|
Donc si Fedora régénère seulement l’initramfs classique, mais pas l’UKI, mon fichier réellement booté peut rester ancien.
L’idée n’est pas de hooker dracut directement.
Mauvaise idée :
|
|
Le point d’entrée propre reste kernel-install.
Donc j’ai créé un petit script qui rebuild l’UKI du kernel courant :
|
|
Le flock sert juste à éviter que le script parte plusieurs fois en parallèle si plusieurs hooks se déclenchent dans la même transaction.
Ensuite j’installe le plugin DNF5 actions :
|
|
Puis j’ajoute une action post-transaction DNF5 :
|
|
Pourquoi je n’ai pas mis kernel-core* ?
Parce que kernel-core appelle déjà kernel-install via les scriptlets RPM Fedora.
Là je cible surtout les cas où des modules bougent :
|
|
C’est peut-être un peu bourrin, mais je préfère ça plutôt qu’avoir un initramfs classique à jour et une UKI bootée qui ne l’est pas.
Pour tester, j’ai fait :
|
|
D’abord le scriptlet Fedora de kernel-modules fait son truc :
|
|
Puis mon action DNF déclenche le rebuild UKI :
|
|
Maintenant le flow est plus propre :
|
|
Il y a un petit défaut : dans certains cas ça peut faire deux dracut.
Par exemple avec kernel-modules, Fedora peut déjà faire :
|
|
puis mon hook fait :
|
|
Ce n’est pas très grave. C’est juste un peu plus long.
Le point important pour moi, c’est que le fichier réellement booté reste cohérent :
|
|
Et que ce fichier contient toujours une .pcrsig valide pour que le TPM puisse unlock LUKS après update.
Commandes utiles de vérification
Secure Boot :
|
|
UKI :
|
|
TPM PCR :
|
|
LUKS token :
|
|
Test manuel du token TPM :
|
|
Les fichiers à backup
Très important :
|
|
À backup hors de la machine :
|
|
Et évidemment garder la recovery key LUKS.
Définitions
Secure Boot
Secure Boot est une fonctionnalité UEFI qui permet au firmware de vérifier la signature des binaires lancés au boot. En gros, le firmware ne lance pas n’importe quel fichier .efi : il vérifie d’abord si ce fichier est signé par une clé de confiance.
UKI
UKI veut dire Unified Kernel Image.
Normalement, sur beaucoup de setups Linux, le boot ressemble à un truc du genre :
|
|
Avec une UKI, on regroupe tout dans un seul fichier EFI :
|
|
Donc au lieu d’avoir plein de morceaux séparés, on a un seul fichier .efi bootable, signé, et mesurable par le TPM. C’est plus propre pour Secure Boot, parce que la ligne de commande kernel et l’initramfs font partie de l’image signée.
TPM2
Le TPM, pour Trusted Platform Module, est une puce de sécurité présente sur beaucoup de machines modernes. Elle peut stocker ou protéger des secrets, mais surtout elle peut les libérer seulement si certaines conditions sont respectées.
Dans ce setup, le TPM ne contient pas “la passphrase LUKS en clair”. Il protège un secret utilisé pour déverrouiller LUKS, et ce secret peut être lié à l’état de la machine au boot.
Donc si l’état attendu change, par exemple Secure Boot désactivé, UKI modifiée, mauvais kernel, mauvaise cmdline, etc., le TPM peut refuser de libérer le secret.
PCR
PCR veut dire Platform Configuration Register.
Ce sont des registres du TPM qui contiennent des mesures de l’état de la machine. Ce ne sont pas des fichiers qu’on édite à la main. Ce sont plutôt des valeurs calculées progressivement pendant le boot.
L’idée importante : si un élément mesuré change, la valeur du PCR change aussi.
Dans mon setup, les deux PCR importants sont surtout :
|
|
PCR7 permet de lier le déverrouillage à l’état Secure Boot. PCR11 permet de lier le déverrouillage à l’UKI bootée.
PCR signature
Le problème, c’est que PCR11 change quand l’UKI change. Et comme l’UKI change à chaque update kernel, un enroll TPM naïf sur PCR11 casserait le déverrouillage LUKS à chaque mise à jour.
La solution propre, c’est d’utiliser une PCR policy signée.
Au lieu de dire au TPM :
|
|
on lui dit plutôt :
|
|
Comme ça, on peut mettre à jour son kernel, générer une nouvelle UKI, signer les nouvelles mesures PCR, et garder un système maintenable.
Conclusion
Au final le setup marche bien, mais le plus relou c’était clairement TPM2 + PCR signature.
Le piège principal :
|
|
Donc dans mon cas :
|
|
Maintenant j’ai une boot chain clean :
|
|
Et c’est exactement ce que je voulais.