Dracut secureboot tpm2
Démarrage Sécurisé Dracut UKI, Signature MOK et Déverrouillage TPM2 LUKS
Objectif d'administration système
Ce projet automatise le durcissement physique et cryptographique des postes de travail et ordinateurs portables (groupe Ansible desktop). Il répond à deux besoins :
1. Sécurité physique : Prévention des attaques de type Evil Maid (altération du noyau, de l'initramfs ou de la ligne de commande sur disque non chiffré) grâce au chiffrement intégral LUKS2 et à UEFI Secure Boot.
2. Expérience utilisateur fluide : Déverrouillage transparent au démarrage grâce au coprocesseur matériel TPM 2.0, sans saisie manuelle de mot de passe, avec secours via une clé de récupération séquestrée sur un serveur centralisé sécurisé ou un coffre-fort de mots de passe.
1. Chaîne de Confiance Matérielle (Root of Trust)
Dans une installation Linux chiffrée standard, la partition /boot et l'initramfs restent en clair. Un attaquant ayant un accès physique temporaire à la machine peut injecter un keylogger dans l'initramfs pour capturer la phrase de passe au démarrage suivant.
Pour fermer cette brèche, voici la chaîne de confiance mise en place :
graph TD
Hardware["Composant Matériel : Puce TPM 2.0"] -->|"Mesure PCR 0 (Firmware/UEFI)"| SecureBoot{"UEFI Secure Boot Actif ?"}
SecureBoot -->|"Non"| Abort["Refus de Boot / Échec Matériel"]
SecureBoot -->|"Oui"| MOK["Vérification Clé MOK (Machine Owner Key)"]
MOK -->|"Signature Valide"| UKI["Chargement Unified Kernel Image (UKI .efi)"]
subgraph UKI_Package["Unified Kernel Image - Fichier unique scellé"]
Stub["Systemd Boot Stub"]
Kernel["Noyau Linux vmlinuz"]
Initramfs["Initramfs Dracut (Modules LUKS/TPM2)"]
Cmdline["Ligne de commande kernel (/etc/kernel/cmdline)"]
end
UKI --> UKI_Package
UKI_Package --> PCRCheck{"Mesure PCRs 0+7 Conforme ?"}
PCRCheck -->|"Firmware ou SecureBoot altéré"| FallbackPass["Demande de Clé de Récupération LUKS"]
PCRCheck -->|"Mesures Identiques"| TPMRelease["Libération de la clé LUKS par le TPM2"]
TPMRelease --> RootUnlock["Déchiffrement /dev/mapper/root"]
RootUnlock --> BootOS["Démarrage du Système Debian"]
2. Les Composants Techniques
A. L'Unified Kernel Image (UKI)
Au lieu d'avoir un chargeur (GRUB), un noyau séparé (vmlinuz) et une archive initrd.img, l'UKI compile ces éléments dans un binaire exécutable UEFI unique (.efi).
La ligne de commande du noyau est scellée à l'intérieur du binaire (/etc/kernel/cmdline), ce qui empêche toute modification de paramètres au démarrage (comme init=/bin/bash ou la désactivation de sécurités) :
# /etc/kernel/cmdline (inclus dans l'UKI)
rd.luks.name=UUID_DU_DISQUE=nvme0n1p3_crypt rd.luks.options=tpm2-device=auto,discard root=/dev/mapper/vg0-root rootfstype=ext4 ro console=tty1 systemd.show_status=1 zswap.enabled=1 lockdown=confidentiality
Abstract
Le paramètre lockdown=confidentiality active le mode de verrouillage strict du noyau Linux en présence de Secure Boot, empêchant même le compte root de modifier la mémoire vive du kernel via /dev/mem ou de charger des modules non signés.
B. Machine Owner Key (MOK) et Signature
Plutôt que de dépendre uniquement des clés Microsoft préinstallées dans le firmware, nous générons une autorité de signature locale spécifique à notre infrastructure :
* Clé privée : /etc/ssl/MOK.key (droits stricts 0400 root:root)
* Certificat public : /etc/ssl/MOK.crt et format DER /etc/ssl/MOK.der
* Enrôlement NVRAM : La clé publique est enregistrée dans la mémoire non-volatile de la carte mère via mokutil --import /etc/ssl/MOK.der. L'administrateur valide physiquement l'empreinte lors d'un reboot unique (écran bleu Shim UEFI Key Management).
Chaque UKI généré par Dracut est automatiquement signé avant d'être déposé dans la partition ESP (/boot/efi/EFI/Linux/ ou /boot/efi/EFI/BOOT/BOOTX64.EFI) :
sbsign --key /etc/ssl/MOK.key --cert /etc/ssl/MOK.crt --output /boot/efi/EFI/Linux/linux-signed.efi /boot/efi/EFI/Linux/linux.efi
C. Déverrouillage Matériel TPM 2.0 (PCR 0+7)
Le volume chiffré LUKS2 est scellé dans le TPM 2.0 à l'aide de l'utilitaire systemd-cryptenroll.
Nous lions le déchiffrement aux registres PCR suivants : * PCR 0 : Mesure du BIOS, du firmware UEFI de la carte mère et des microcodes processeur. * PCR 7 : Mesure de l'état d'activation de Secure Boot, de la liste des clés autorisées (PK, KEK, db) et de la clé MOK active.
Conséquence en cas d'attaque : Si le disque dur ou le SSD est volé et branché dans une autre machine, ou si un attaquant tente de désactiver Secure Boot ou d'insérer un firmware corrompu, les mesures des PCR 0 et 7 changent. Le TPM 2.0 refuse catégoriquement de libérer la clé cryptographique.
3. Séquestration de Clé de Secours et Élimination de la Passphrase Faible
L'automatisation du déverrouillage présente un risque opérationnel : si le firmware de la carte mère est mis à jour (mise à jour BIOS/UEFI), les registres PCR changent et la machine ne peut plus démarrer automatiquement.
La procédure automatisée applique une politique stricte en 4 étapes :
-
Génération d'une clé de secours aléatoire haute entropie : Génération d'une clé de 512 bits (64 octets hexadécimaux) et ajout dans un slot de clé LUKS libre :
-
Séquestration sécurisée de la clé de secours : La clé de récupération est immédiatement sauvegardée sur un stockage sécurisé centralisé ou un coffre-fort de mots de passe hors-ligne avec des permissions d'accès restreintes (
0400). -
Validation de l'enrôlement TPM2 : Vérification que le slot LUKS2 de type
systemd-tpm2est bien présent et actif dans les métadonnées (cryptsetup luksDump). -
Destruction du mot de passe initial de provisionnement : Pour interdire toute tentative d'attaque par dictionnaire sur le mot de passe utilisateur d'installation, le slot du mot de passe initial est révoqué via
cryptsetup luksKillSlot. Le disque ne peut plus être ouvert que par le TPM 2.0 de la machine autorisée, ou manuellement via la clé de secours séquestrée.
4. Configuration Dracut (/etc/dracut.conf.d/10-tpm2.conf)
Pour que l'environnement initramfs soit capable d'interagir avec la puce TPM2 et de charger les volumes LVM/LUKS de manière autonome :
# Forcer la génération au format Unified Kernel Image (UKI)
build_uki="yes"
uki="/boot/efi/EFI/Linux/linux.efi"
# Modules Dracut indispensables intégrés dans l'image
add_dracutsysrootdir="yes"
add_dracutmodules+=" tpm2-tss systemd-cryptsetup lvm dm rootfs-block "
# Pilotes noyau obligatoires pour l'accès matériel direct
drivers+=" tpm tpm_tis tpm_crb tpm_tis_core nvme ext4 xfs "
# Compression rapide
compress="zstd"
5. Automatisation des Mises à Jour Kernel via Hook APT
Chaque mise à jour du noyau Debian (linux-image-amd64) doit reconstruire l'UKI et signer immédiatement le nouveau binaire. Un hook APT est déposé dans /etc/kernel/postinst.d/zz-dracut-uki :
#!/bin/sh
set -e
version="$1"
echo "[Kernel Hook] Reconstruction de l'UKI Dracut signée MOK pour le noyau $version..."
dracut --force --kver "$version"
sbsign --key /etc/ssl/MOK.key --cert /etc/ssl/MOK.crt \
--output /boot/efi/EFI/Linux/linux.efi /boot/efi/EFI/Linux/linux.efi
echo "[Kernel Hook] UKI signée avec succès."
6. Procédure de Récupération d'Urgence
Si la machine affiche au boot la demande de déverrouillage manuel suite à une modification matérielle ou un reset BIOS :
- Se connecter sur votre serveur de gestion ou consulter votre gestionnaire de mots de passe de secours.
- Récupérer la clé de secours correspondant à la machine (ex: le fichier
/root/luks-recovery-key-<hostname>.txtou l'entrée correspondante dans votre coffre-fort). - Saisir la chaîne hexadécimale à l'écran de démarrage.
- Une fois la session ouverte, resynchroniser les PCR TPM2 avec l'état actuel de la machine en réexécutant le playbook ci-dessous.
7. Playbook Ansible Prêt à l'Emploi (playbook-dracut-tpm2.yaml)
Ce playbook autonome est prêt à être exécuté sur n'importe quel poste client ou PC portable sous Debian ou distribution dérivée avec LUKS2 et puce TPM2 :
---
- name: Hardening du Boot - Dracut UKI, Signature MOK et TPM2 Auto-Unlock
hosts: all
become: true
vars:
# Registres PCR liés : 0 (firmware/UEFI) et 7 (Secure Boot / MOK)
tpm2_pcrs: "0+7"
# Répertoire de destination de l'exécutable UKI dans l'ESP
esp_linux_dir: "/boot/efi/EFI/Linux"
# Fichier local de sauvegarde de la clé de secours
recovery_key_dest: "/root/luks-recovery-key-{{ inventory_hostname }}.txt"
tasks:
# -------------------------------------------------------------------------
# 1. CONTRÔLE DES PRÉREQUIS MATÉRIELS ET SYSTÈME
# -------------------------------------------------------------------------
- name: Vérifier la présence du matériel TPM2 (/dev/tpmrm0 ou /dev/tpm0)
ansible.builtin.stat:
path: /dev/tpmrm0
register: tpm_dev
- name: Vérifier la présence de /etc/crypttab
ansible.builtin.stat:
path: /etc/crypttab
register: crypttab_stat
- name: Interrompre si le TPM2 ou /etc/crypttab est absent
ansible.builtin.meta: end_host
when: not tpm_dev.stat.exists or not crypttab_stat.stat.exists
- name: Identifier le conteneur LUKS actif dans /etc/crypttab
ansible.builtin.shell: |
grep -v '^#' /etc/crypttab | grep -v '^$' | head -n 1
register: crypttab_entry
changed_when: false
- name: Extraire le nom du mapper et l'UUID LUKS
ansible.builtin.set_fact:
luks_mapper: "{{ crypttab_entry.stdout.split()[0] }}"
luks_target: "{{ crypttab_entry.stdout.split()[1] }}"
luks_uuid: "{{ crypttab_entry.stdout.split()[1] | regex_replace('^UUID=', '') }}"
- name: Vérifier que le volume est bien au format LUKS2
ansible.builtin.command: "cryptsetup isLuks --type luks2 /dev/disk/by-uuid/{{ luks_uuid }}"
changed_when: false
# -------------------------------------------------------------------------
# 2. INSTALLATION DES PAQUETS DE SÉCURITÉ REQUIS
# -------------------------------------------------------------------------
- name: Installer Dracut, les outils TPM2 et sbsigntool
ansible.builtin.apt:
name:
- dracut
- dracut-core
- sbsigntool
- tpm2-tools
- systemd-cryptsetup
- binutils
state: present
update_cache: true
# -------------------------------------------------------------------------
# 3. CONFIGURATION DE LA LIGNE DE COMMANDE KERNEL ET DRACUT
# -------------------------------------------------------------------------
- name: Détecter le point de montage racine et son type de système de fichiers
ansible.builtin.set_fact:
root_device: "{{ (ansible_facts['mounts'] | selectattr('mount', 'equalto', '/') | map(attribute='device') | first) | default('/dev/mapper/' + luks_mapper) }}"
root_fstype: "{{ (ansible_facts['mounts'] | selectattr('mount', 'equalto', '/') | map(attribute='fstype') | first) | default('ext4') }}"
- name: S'assurer du dossier /etc/kernel
ansible.builtin.file:
path: /etc/kernel
state: directory
mode: '0755'
- name: Écrire /etc/kernel/cmdline (scellé dans l'UKI)
ansible.builtin.copy:
dest: /etc/kernel/cmdline
mode: '0644'
content: >-
rd.luks.name={{ luks_uuid }}={{ luks_mapper }}
rd.luks.options=tpm2-device=auto,discard
root={{ root_device }}
rootfstype={{ root_fstype }}
ro console=tty1 systemd.show_status=1 zswap.enabled=1 lockdown=confidentiality
- name: Configurer Dracut pour l'UKI et le TPM2 (/etc/dracut.conf.d/10-tpm2.conf)
ansible.builtin.copy:
dest: /etc/dracut.conf.d/10-tpm2.conf
mode: '0644'
content: |
build_uki="yes"
uki="{{ esp_linux_dir }}/linux.efi"
add_dracutsysrootdir="yes"
add_dracutmodules+=" tpm2-tss systemd-cryptsetup lvm dm rootfs-block "
drivers+=" tpm tpm_tis tpm_crb tpm_tis_core nvme ext4 xfs "
compress="zstd"
# -------------------------------------------------------------------------
# 4. GESTION DE L'AUTORITÉ DE SIGNATURE SECURE BOOT (MOK)
# -------------------------------------------------------------------------
- name: Vérifier si les clés MOK locales existent déjà
ansible.builtin.stat:
path: /etc/ssl/MOK.key
register: mok_key_stat
- name: Générer la paire de clés MOK locale si absente
ansible.builtin.command: >
openssl req -new -x509 -newkey rsa:2048 -nodes -days 3650
-subj "/CN=Machine Owner Key - {{ inventory_hostname }}/"
-keyout /etc/ssl/MOK.key -out /etc/ssl/MOK.crt
when: not mok_key_stat.stat.exists
- name: Convertir le certificat MOK au format DER pour l'UEFI
ansible.builtin.command: openssl x509 -outform DER -in /etc/ssl/MOK.crt -out /etc/ssl/MOK.der
when: not mok_key_stat.stat.exists
- name: Protéger les permissions de la clé privée MOK
ansible.builtin.file:
path: /etc/ssl/MOK.key
mode: '0400'
owner: root
group: root
# -------------------------------------------------------------------------
# 5. DÉVERROUILLAGE TPM2 ET CLÉ DE SECOURS
# -------------------------------------------------------------------------
- name: Vérifier si le TPM2 est déjà enrôlé dans les slots LUKS
ansible.builtin.shell: "cryptsetup luksDump /dev/disk/by-uuid/{{ luks_uuid }} | grep -q 'systemd-tpm2'"
register: tpm_enrolled
changed_when: false
failed_when: false
- name: Enrôler le TPM2 avec les PCRs 0+7
ansible.builtin.command: >
systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs={{ tpm2_pcrs }} /dev/disk/by-uuid/{{ luks_uuid }}
when: tpm_enrolled.rc != 0
- name: Générer et séquestrer une clé de récupération de 512 bits si non existante
ansible.builtin.stat:
path: "{{ recovery_key_dest }}"
register: recovery_file_stat
- name: Créer et enregistrer la clé de secours
when: not recovery_file_stat.stat.exists
block:
- name: Générer une chaîne aléatoire sécurisée de 64 caractères
ansible.builtin.command: openssl rand -hex 32
register: random_key
changed_when: false
- name: Ajouter la clé aléatoire comme clé de secours dans LUKS
ansible.builtin.shell: |
echo -n "{{ random_key.stdout }}" | cryptsetup luksAddKey /dev/disk/by-uuid/{{ luks_uuid }} -
no_log: true
- name: Écrire la clé de secours dans le fichier local sécurisé
ansible.builtin.copy:
dest: "{{ recovery_key_dest }}"
mode: '0400'
owner: root
group: root
content: "{{ random_key.stdout }}\n"
# -------------------------------------------------------------------------
# 6. GÉNÉRATION DE L'UKI ET SIGNATURE MOK
# -------------------------------------------------------------------------
- name: S'assurer que le dossier EFI Linux existe
ansible.builtin.file:
path: "{{ esp_linux_dir }}"
state: directory
mode: '0755'
- name: Compiler l'image UKI avec Dracut
ansible.builtin.command: dracut --force --kver "{{ ansible_kernel }}"
- name: Signer l'UKI générée avec la clé MOK
ansible.builtin.command: >
sbsign --key /etc/ssl/MOK.key --cert /etc/ssl/MOK.crt
--output {{ esp_linux_dir }}/linux.efi {{ esp_linux_dir }}/linux.efi
# -------------------------------------------------------------------------
# 7. HOOK KERNEL AUTOMATISÉ POUR LES MISES À JOUR APT
# -------------------------------------------------------------------------
- name: Déployer le hook APT postinst de reconstruction et signature automatique
ansible.builtin.copy:
dest: /etc/kernel/postinst.d/zz-dracut-uki
mode: '0755'
owner: root
group: root
content: |
#!/bin/sh
set -e
version="$1"
echo "[Kernel Hook] Reconstruction de l'UKI Dracut signée pour $version..."
dracut --force --kver "$version"
sbsign --key /etc/ssl/MOK.key --cert /etc/ssl/MOK.crt \
--output {{ esp_linux_dir }}/linux.efi {{ esp_linux_dir }}/linux.efi
echo "[Kernel Hook] UKI signée avec succès."
Comment l'exécuter :
Première activation de Secure Boot
Si votre clé MOK vient d'être créée, importez-la dans l'UEFI via :
Définissez un mot de passe temporaire à la demande demokutil. Au redémarrage suivant, l'écran bleu UEFI Shim / MOK Management s'affiche : sélectionnez Enroll MOK, puis Continue, entrez le mot de passe temporaire et validez. Votre système démarre ensuite de façon fluide et totalement chiffrée sans mot de passe !