Signer les modules noyau tiers dkms pour le Secure Boot sous Linux
Table des matières
Debian, Ubuntu, Fedora, RHEL utilisent des signatures numériques approuvées par l'UEFI Secure Boot. Les binaires du chargeur de démarrage (comme GRUB) et le noyau fourni dans la distribution (et ses modules) sont signés avec des clés reconnues par les certificats de la plateforme UEFI préinstallées sur la plupart des cartes mères. Par conséquent, il n'est pas nécessaire de désactiver le Secure Boot pour installer et utiliser ces systèmes.
Cependant, les modules fournis sous forme dkms livrés dans les dépôts comme par exemple les pilotes NVidia, Broadcom, certains pilotes Realtek, ou des logiciels qui ont besoin de modules noyau pour fonctionner comme VirtualBox ... sont compilés sur la machine et ne sont pas signés. Ils ne sont donc pas chargés si le Secure Boot est actif (car oui, Secure Boot empêche de charger des modules non signés, c'est son rôle).
Pour info, dkms signifie Dynamic Kernel Module Support Framework. dkms compile les modules tiers, externes du noyau lors de leur installation ou si un nouveau noyau est installé. Cela assure que ces modules sont toujours disponibles, même après une mise à jour du noyau.
Plutôt que de désactiver ce mécanisme de sécurité, nous allons voir comment générer notre certificat et nos clés de signature et les importer dans l'UEFI de la machine.
Tous les modules (de type dkms) seront ensuite automatiquement signés lors de la compilation sans intervention manuelle.
Ce tutoriel est une adaptation de celui intitulé Fedora : Signer les modules noyau tiers kmod pour le Secure Boot mais s'appliquant aux modules compilés avec DKMS (qui est plus courant dans l'écosystème Linux)
Toutes les manipulations seront réalisées avec les droits root.
Ce tutoriel est réalisé sous Debian, mais les commandes (et les noms des paquets) seront similaire dans les autres distributions Linux.
Dans un premier temps, on installe les outils nécessaires pour la gestion des clés machines, et des modules dkms:
Une fois fait, vous pouvez vérifier le statut du SecureBoot avec la commande :
Sur ma machine, il est actif :
Dans cet exemple, j'installe juste le module r8125 (qui n'est donc pas signé) et présent dans le dépôt non-free :
Le module a été compilé et installé :
Lorsque j'essaie de charger le module :
J'ai une erreur car le module n'est pas signé et reconnu par ma machine :
Certains logiciels veulent que le dossier /var/lib/shim-signed/mok soit présent, donc on va mettre nos clés dedans
Dans un premier temps, on va créer une clé et un certificat avec openssl d'une durée de 100 ans par exemple pour signer les modules :
On utilise du RSA2048 car certains UEFI ne supportent pas correctement le RSA 4096
Deux fichiers sont créés :
/var/lib/shim-signed/mok/mok.priv : la clé privée pour signer les modules
/var/lib/shim-signed/mok/mok.der : la clé publique (certificat) pour vérifier la signature
On va ensuite enregistrer les éléments précédemment générés dans l'UEFI et le module Secure Boot.
Cela permettra au noyau Linux de faire confiance aux pilotes signés avec notre clé publique (certificat) :
On va devoir entrer un mot de passe.
Il n'a pas besoin d'être très long ou complexe. On en aura juste besoin pour importer le certificat dans le secure boot.
Attention, on sera en qwerty pour la saisie, donc on fera simple !
Une fois fait, on va redémarrer le système :
Lorsque l'UEFI s'initialise, on est intercepté pour ajouter le certificat en attente.
On valide par Entrée
Ensuite :
Sélectionner Enroll MOK
Puis Continue
Puis Yes
On saisit le mot de passe renseigné précédemment (rappel on est en qwerty)
Puis Reboot
Après la compilation de modules avec DKMS, on a des scripts qui sont appelés (pour regénérer un inirtd par exemple avec dracut).
On va ajouter le chemin vers les clés précédemment générées pour automatiser la signature.
Vérifier la configuration de dkms :
Par défaut, les clés utilisées sont dans les chemins suivants :
Ces clés ont été générées automatiquement, ce ne sont pas celles qu'on a généré nous même et importées précédemment.
On va modifier et renseigner le chemin vers les clés :
Tous les modules seront signés automatiquement maintenant
Maintenant, tous les modules "dkms" seront compilés et signés automatiquement avec notre clé, que SecureBoot connait.
Si on avait déjà des modules installés, on pourra les identifier via :
Dans l'exemple du tuto :
On les réinstalle :
Dans l'exemple de ce tuto avec le module r8125 :
On regénère aussi l'image initrd.
Pour les distributoons utilisant initramfs-tools (branche Debian) :
Distributions utilisant dracut :
On peut charger le module manuellement avec modprobe :
Ou on peut carrément reboot :
Si notre module est signé correctement, alors il est chargé par le système.
On pourra le constater avec la fameuse commande lsmod :
Ici, le module est bien chargé :
Si vous n'avez pas de modules installés sous forme de "dkms", il est possible de signer les modules "manuellement".
Après avoir généré et importé les certificats et clés, on recherche le chemin du module :
Cela me renvoie :
Il existe un script pour signer les modules.
Sur Debian, son chemin est le suivant (pour un noyau 6.1) et est fourni par le paquet linux-kbuild :
Sur Debian, son chemin est le suivant et il est fourni par le paquet linux-headers :
Sur Fedora, son chemin est le suivant et il est fourni par le paquet kernel-devel :
Pour signer notre module, on utilisera le script, avec les clés privées et publiques créées précédemment :
Pour VirtualBox avec Debian :
Evidemment, ce sont des actions manuelles à faire à chaque mise à jour du noyau et recompilation du module !
On peut charger le module avec succès et vérifier que le module est signé :
- Introduction
- Prérequis
- Exemple avec un module non signé
- Génération des certificats et clés
- Enregistrer les certificats et clés dans l'UEFI
- Paramétrer DKMS pour signer automatiquement les modules compilés
- Installer ou réinstaller des modules
- Vérifier que les modules sont chargés
- Signer des modules installés manuellement
Introduction
Debian, Ubuntu, Fedora, RHEL utilisent des signatures numériques approuvées par l'UEFI Secure Boot. Les binaires du chargeur de démarrage (comme GRUB) et le noyau fourni dans la distribution (et ses modules) sont signés avec des clés reconnues par les certificats de la plateforme UEFI préinstallées sur la plupart des cartes mères. Par conséquent, il n'est pas nécessaire de désactiver le Secure Boot pour installer et utiliser ces systèmes.
Cependant, les modules fournis sous forme dkms livrés dans les dépôts comme par exemple les pilotes NVidia, Broadcom, certains pilotes Realtek, ou des logiciels qui ont besoin de modules noyau pour fonctionner comme VirtualBox ... sont compilés sur la machine et ne sont pas signés. Ils ne sont donc pas chargés si le Secure Boot est actif (car oui, Secure Boot empêche de charger des modules non signés, c'est son rôle).
Pour info, dkms signifie Dynamic Kernel Module Support Framework. dkms compile les modules tiers, externes du noyau lors de leur installation ou si un nouveau noyau est installé. Cela assure que ces modules sont toujours disponibles, même après une mise à jour du noyau.
Plutôt que de désactiver ce mécanisme de sécurité, nous allons voir comment générer notre certificat et nos clés de signature et les importer dans l'UEFI de la machine.
Tous les modules (de type dkms) seront ensuite automatiquement signés lors de la compilation sans intervention manuelle.
Ce tutoriel est une adaptation de celui intitulé Fedora : Signer les modules noyau tiers kmod pour le Secure Boot mais s'appliquant aux modules compilés avec DKMS (qui est plus courant dans l'écosystème Linux)
Prérequis
Toutes les manipulations seront réalisées avec les droits root.
Ce tutoriel est réalisé sous Debian, mais les commandes (et les noms des paquets) seront similaire dans les autres distributions Linux.
Dans un premier temps, on installe les outils nécessaires pour la gestion des clés machines, et des modules dkms:
Code BASH :
apt install dkms mokutil openssl
Une fois fait, vous pouvez vérifier le statut du SecureBoot avec la commande :
Code BASH :
mokutil --sb-state
Sur ma machine, il est actif :
Code :
SecureBoot enabled
Exemple avec un module non signé
Dans cet exemple, j'installe juste le module r8125 (qui n'est donc pas signé) et présent dans le dépôt non-free :
Code BASH :
apt install r8125-dkms
Le module a été compilé et installé :
Code :
Building initial module for 6.1.0-25-amd64
Done.
r8125.ko:
Running module version sanity check.
- Original module
- No original module exists within this kernel
- Installation
- Installing to /lib/modules/6.1.0-25-amd64/updates/dkms/
depmod...
Traitement des actions différées (« triggers ») pour initramfs-tools (0.142) ...
update-initramfs: Generating /boot/initrd.img-6.1.0-25-amd64
Lorsque j'essaie de charger le module :
Code BASH :
modprobe r8125
J'ai une erreur car le module n'est pas signé et reconnu par ma machine :
Code :
modprobe: ERROR: could not insert 'r8125': Key was rejected by service
Génération des certificats et clés
Certains logiciels veulent que le dossier /var/lib/shim-signed/mok soit présent, donc on va mettre nos clés dedans
Code BASH :
mkdir -p /var/lib/shim-signed/mok cd /var/lib/shim-signed/mok
Dans un premier temps, on va créer une clé et un certificat avec openssl d'une durée de 100 ans par exemple pour signer les modules :
Code BASH :
openssl req -nodes -new -x509 -newkey rsa:2048 -keyout mok.priv -outform DER -out mok.der -days 36500 -subj "/CN=$(hostname)/"
On utilise du RSA2048 car certains UEFI ne supportent pas correctement le RSA 4096
Deux fichiers sont créés :
/var/lib/shim-signed/mok/mok.priv : la clé privée pour signer les modules
/var/lib/shim-signed/mok/mok.der : la clé publique (certificat) pour vérifier la signature
Enregistrer les certificats et clés dans l'UEFI
Lançons l'import dans l'UEFI
On va ensuite enregistrer les éléments précédemment générés dans l'UEFI et le module Secure Boot.
Cela permettra au noyau Linux de faire confiance aux pilotes signés avec notre clé publique (certificat) :
Code BASH :
mokutil --import /var/lib/shim-signed/mok/mok.der
On va devoir entrer un mot de passe.
Il n'a pas besoin d'être très long ou complexe. On en aura juste besoin pour importer le certificat dans le secure boot.
Attention, on sera en qwerty pour la saisie, donc on fera simple !
Une fois fait, on va redémarrer le système :
Code BASH :
reboot
Validons l'import côté UEFI
Lorsque l'UEFI s'initialise, on est intercepté pour ajouter le certificat en attente.
On valide par Entrée
Ensuite :
Sélectionner Enroll MOK
Puis Continue
Puis Yes
On saisit le mot de passe renseigné précédemment (rappel on est en qwerty)
Puis Reboot
Paramétrer DKMS pour signer automatiquement les modules compilés
Après la compilation de modules avec DKMS, on a des scripts qui sont appelés (pour regénérer un inirtd par exemple avec dracut).
On va ajouter le chemin vers les clés précédemment générées pour automatiser la signature.
Vérifier la configuration de dkms :
Code BASH :
nano /etc/dkms/framework.conf
Par défaut, les clés utilisées sont dans les chemins suivants :
Code BASH :
mok_signing_key=/var/lib/dkms/mok.key mok_certificate=/var/lib/dkms/mok.pub
Ces clés ont été générées automatiquement, ce ne sont pas celles qu'on a généré nous même et importées précédemment.
On va modifier et renseigner le chemin vers les clés :
Code BASH :
mok_signing_key=/var/lib/shim-signed/mok/mok.priv mok_certificate=/var/lib/shim-signed/mok/mok.der
Tous les modules seront signés automatiquement maintenant
Installer ou réinstaller des modules
Installer de nouveaux modules
Maintenant, tous les modules "dkms" seront compilés et signés automatiquement avec notre clé, que SecureBoot connait.
Réinstaller des modules existants
Si on avait déjà des modules installés, on pourra les identifier via :
Code BASH :
dkms status
Dans l'exemple du tuto :
Code :
r8125/9.011.00, 6.1.0-25-amd64, x86_64: installed
On les réinstalle :
Code BASH :
dkms remove r8125/9.011.00 --all dkms install r8125/9.011.00
Dans l'exemple de ce tuto avec le module r8125 :
Code :
Module r8125 9.011.00 is not installed for kernel 6.1.0-25-amd64 (x86_64). Skipping...
Deleting module r8125-9.011.00 completely from the DKMS tree.
Sign command: /usr/lib/linux-kbuild-6.1/scripts/sign-file
Signing key: /var/lib/shim-signed/mok/mok.priv
Public certificate (MOK): /var/lib/shim-signed/mok/mok.der
Creating symlink /var/lib/dkms/r8125/9.011.00/source -> /usr/src/r8125-9.011.00
Building module:
Cleaning build area...
make -j4 KERNELRELEASE=6.1.0-25-amd64 -C /lib/modules/6.1.0-25-amd64/build M=/var/lib/dkms/r8125/9.011.00/build......
Signing module /var/lib/dkms/r8125/9.011.00/build/r8125.ko
Cleaning build area.
On regénère aussi l'image initrd.
Pour les distributoons utilisant initramfs-tools (branche Debian) :
Code BASH :
update-initramfs -u
Distributions utilisant dracut :
Code BASH :
dracut --force
On peut charger le module manuellement avec modprobe :
Code BASH :
modprobe -v r8125
Ou on peut carrément reboot :
Code BASH :
reboot
Vérifier que les modules sont chargés
Si notre module est signé correctement, alors il est chargé par le système.
On pourra le constater avec la fameuse commande lsmod :
Code BASH :
lsmod | grep r8125
Ici, le module est bien chargé :
Code :
r8125 237568 0
Signer des modules installés manuellement
Si vous n'avez pas de modules installés sous forme de "dkms", il est possible de signer les modules "manuellement".
Après avoir généré et importé les certificats et clés, on recherche le chemin du module :
Code BASH :
modinfo vboxdrv
Cela me renvoie :
Code :
filename: /lib/modules/6.1.0-25-amd64/misc/vboxdrv.ko
version: 7.1.0 r164728 (0x00340001)
license: GPL
description: Oracle VirtualBox Support Driver
author: Oracle and/or its affiliates
srcversion: 1D640EFCE74255CFEC18A1C
depends:
retpoline: Y
name: vboxdrv
vermagic: 6.1.0-25-amd64 SMP preempt mod_unload modversions
parm: disabled:Disable automatic module loading (int)
parm: force_async_tsc:force the asynchronous TSC mode (int)
Il existe un script pour signer les modules.
Sur Debian, son chemin est le suivant (pour un noyau 6.1) et est fourni par le paquet linux-kbuild :
Code BASH :
/usr/lib/linux-kbuild-6.1/scripts/sign-file
Sur Debian, son chemin est le suivant et il est fourni par le paquet linux-headers :
Code BASH :
/usr/src/linux-headers-$(uname -r)/scripts/sign-file
Sur Fedora, son chemin est le suivant et il est fourni par le paquet kernel-devel :
Code BASH :
/usr/src/kernels/$(uname -r)/scripts/sign-file
Pour signer notre module, on utilisera le script, avec les clés privées et publiques créées précédemment :
Code BASH :
$CHEMIN_DE_SIGNFILE/sign-file sha256 /var/lib/shim-signed/mok/mok.priv /var/lib/shim-signed/mok/mok.der $CHEMIN_DU_MODULE
Pour VirtualBox avec Debian :
Code BASH :
/usr/lib/linux-kbuild-6.1/scripts/sign-file sha256 /var/lib/shim-signed/mok/mok.priv /var/lib/shim-signed/mok/mok.der /lib/modules/6.1.0-25-amd64/misc/vboxdrv.ko
Evidemment, ce sont des actions manuelles à faire à chaque mise à jour du noyau et recompilation du module !
On peut charger le module avec succès et vérifier que le module est signé :
Code BASH :
modinfo vboxdrv
Code :
filename: /lib/modules/6.1.0-25-amd64/misc/vboxdrv.ko
version: 7.1.0 r164728 (0x00340001)
license: GPL
description: Oracle VirtualBox Support Driver
author: Oracle and/or its affiliates
srcversion: 1D640EFCE74255CFEC18A1C
depends:
retpoline: Y
name: vboxdrv
vermagic: 6.1.0-25-amd64 SMP preempt mod_unload modversions
sig_id: PKCS#7
signer: debsecure
sig_key: 59:64:32:0C:5E:10:DF:ED:79:AD:BA:8C:6C:F9:4B:4E:6D:92:52:58
sig_hashalgo: sha256
signature: 68:57:46:E2:83:0A:FE:AE:80:82:E6:6C:49:FE:D6:36:C1:3F:A6:D1:
6C:F4:5D:B9:64:EA:97:4D:F2:4D:CE:A0:B3:C4:3A:8B:85:2D:81:3F:
9E:6E:96:55:E2:59:55:05:92:BE:35:36:7B:3B:62:F7:82:F9:D6:71:
21:5C:75:45:A3:D2:2D:90:4C:20:27:9F:18:09:65:27:2D:69:D3:12:
ED:EF:8C:96:41:CB:C9:84:D6:14:B2:D5:AC:48:BE:91:47:94:E6:FC:
FC:D4:BD:10:3A:76:AF:25:5D:26:94:30:FF:FB:FC:B7:80:F9:38:87:
02:ED:D0:A9:DA:2B:2A:04:29:23:0F:5E:72:1C:A6:09:46:36:2D:C4:
E0:E1:BC:14:D6:A6:C8:15:9C:53:05:5F:FA:AF:25:4F:43:F8:3E:60:
FE:24:A8:B7:88:F7:8E:78:B5:20:DA:98:E4:06:A0:EE:71:8F:52:39:
50:D0:D6:EB:13:8D:0F:BD:F8:1E:91:82:55:1F:D0:F2:28:E2:36:B5:
00:B7:CE:CF:C5:9A:B5:63:A5:29:3A:34:F7:0C:D6:40:15:D2:3E:3E:
DC:41:7B:6A:CE:FC:A1:82:6E:79:18:0A:19:E6:E7:CB:96:68:9D:6E:
9B:99:AF:7D:A5:DD:09:F9:F0:4B:72:7D:58:20:B2:E1
parm: disabled:Disable automatic module loading (int)
parm: force_async_tsc:force the asynchronous TSC mode (int)