SecureBoot, Signer des modules et pourquoi ?

Contexte

J’ai eu l’occasion d’installer Debian sur mon nouveau portable. Vu que Debian 10 prend en charge le démarrage avec UEFI (Unified Extensible Firmware Interface), j’ai commencé à me renseigner sur son intérêt.

Je me rappelle que lors de la sortie de l’UEFI, une vague de protestation s’était levée contre ce système et la main mise de la part de Microsoft. Cependant, comme le précise le site debian.org, l’objectif est bien la protection de l’utilisateur. (SB = SecureBoot)

UEFI Secure Boot is not an attempt by Microsoft to lock Linux out of the PC market here; SB is a security measure to protect against malware during early system boot. Microsoft act as a Certification Authority (CA) for SB, and they will sign programs on behalf of other trusted organisations so that their programs will also run. There are certain identification requirements that organisations have to meet here, and code has to be audited for safety. But these are not too difficult to achieve.

— debian.org

J’ai donc décidé d’installer ma Debian avec UEFI activé.

Cependant, au delà de l’installation initiale, j’ai installé virtualbox pour mon besoin. Et là, les problèmes arrivent car en effet après l’installation de virtualbox, le lancement ne fonctionne pas et affiche l’erreur suivante :

WARNING: The vboxdrv kernel module is not loaded. Either there is no module
         available for the current kernel (4.19.0-8-amd64) or it failed to
         load. Please recompile the kernel module and install it by

           sudo /sbin/vboxconfig

         You will not be able to start VMs until this problem is fixed.

avertissement Il est à noter que des erreurs étaient affichées dans la console lors de l'installation.

A ce stade, deux solutions existent :

  • Désactiver l'UEFI

  • Signer les modules de virtualbox.

Vu que le système de l'UEFI avec le SecureBoot reste intéressant au niveau de la sécurité, j'ai continué à me documenter sur le sujet afin de ne pas désactiver l'UEFI et de revenir à l'ancien mode.

Donc, je me suis interessé sur la deuxième piste, c'est à dire comment faire pour signer les modules et notammment les modules nécessaires pour virtualbox :

  • vboxdrv.ko, vboxpci.ko, vboxnetflt.ko, vboxnetadp.ko

Ce billet est l'occasion de partager le résultat de mes recherches.

Définitions

Commençons par quelques définitions :

  • UEFI : Unified Extensible Firmware Interface

  • SecureBoot : Mécanisme de vérification permettant de s'assurer que le code lancé par UEFI est approuvé.

  • Shim : Simple programme qui est construit pour prendre en charge le bootloader sur les systèmes UEFI.

  • MOK - Machine Owner Key : L'architecture de shim est que l'utilisateur garde le contrôle. Une base de données de certificat externe a été mis en place. Le certificat de la distribution (ici debian) est inclus dans les binaires de shim. L'utilisateur est capable de gérer ses propres certificats.

  • mokutil - Utilitaire qui permet à l'utilisateur de gérer les certificats de MOK.

Génération du certificat

Nous allons généré un certificat. Vis à vis de la sécurité du système, il doit être placé dans un endroit protégé et sécurisé. Pour cela, je propose de créer un répertoire au niveau du répertoire home de l'utilisateur root : /root/module-signing

mkdir /root/module-signing
cd /root/module-signing

Pour la génération du certificat, nous allons utiliser l'outil classique openssl. Nous allons utiliser la commande suivante, cela permet d'avoir notamment le bon format de sortie (DER) :

openssl req -new -x509 -newkey rsa:2048 -keyout MOK.priv -outform DER -out MOK.der -days 36500 -subj "/CN=dali/" -nodes

Il y a deux points à noter :

  • Le champ CN est libre. Nous pouvons mettre ce que nous voulons.

  • La durée de validité est de 36 500 jours, soit 100 ans pour être tranquille.

Enregister le certificat

Maintenant, il va falloir ajouter le certificat dans la base citée précédemment (MOK). Il sera à coté de celui de la distribution, Debian en l'occurence.

mokutil --import MOK.der

Pour vérifier, nous avons utilisé la commande suivante :

mokutil --list-new

Par la suite, il est nécessaire de redémarrer l'ordinateur. Un écran shim s'affichera pour permettre d'importer réellement le certificat. Il sera demandé le mot de passe de la clé privée. D'où l'intérêt de choisir un mot de passe qui est facile à saisir quelque soit le clavier. (Nous sommes en phase de démarrage, donc pas de clavier français chargé)

Les étapes seront les suivantes :

  • Affichage de l'écran de Shim, appuyer sur un touche,

  • Choisir l'item "Enroll MOK",

  • Choisir le certificat,

  • Affichage des informations du certificat sélectionné,

  • Confirmation de l'enregistrement (enroll) en sélectionnant Yes,

  • Saisir le mot de passe de la clé privée,

  • Confirmer avant le redémarrage du système.

Vérification :

Vous pouvez utiliser la commande suivante après le redémarrage pour vérifier que le certificat est bien présente.

mokutil --list-enrolled

Ci-dessous, un exemple en gardant uniquement l'émetteur. Dans mon cas, j'ai bien deux certificats :

  • mon certificat

  • certificat de la distribution

mokutil --list-enrolled | grep Issuer
        Issuer: CN=dali
        Issuer: CN=Debian Secure Boot CA
                CA Issuers - URI:https://dsa.debian.org/secure-boot-ca

Maintenant que le certificat est en place, nous allons pouvoir signer les modules

Signer les modules

Pour signer, il faut exécuter le programme sign-file. L'emplacement est différent suivant la distribution et la version du noyau.

Pour Debian 10, il se trouve à cet emplacement : /usr/lib/linux-kbuild-4.19/scripts

La commande a exécuter pour chaque module est la suivante :

/usr/lib/linux-kbuild-4.19/scripts/sign-file ${hash_algo} ${key} ${cert} ${module}

La commande possède quatres arguments :

  • hash_algo : Algorithme de hashage. (ex sha256)

  • key : Clé privée

  • cert : Certificat

  • module : Module concerné

La clé privée est protégée par un mot de passe, il est nécessaire de définir la variable KBUILD_SIGN_PIN et de la valoriser avec le mot de passe.

Charger les modules

Une fois signée, les modules peuvent être chargés via la commande modprobe.

Il est intéressant de vérifier que le module est bien signé et par quel certificat. Nous allons utiliser la commande modinfo

Voici le résultat avant la signature du module :

modinfo vboxdrv
filename:       /lib/modules/4.19.0-8-amd64/misc/vboxdrv.ko
version:        6.0.18 r136238 (0x00290008)
license:        GPL
description:    Oracle VM VirtualBox Support Driver
author:         Oracle Corporation
srcversion:     43F4A2EEB843DF199ED0E7C
depends:
retpoline:      Y
name:           vboxdrv
vermagic:       4.19.0-8-amd64 SMP mod_unload modversions

Voici le résultat après la signature du module. Nous avons les propriétés supplémentaires : sig_id, signer, sig_key; sig_hashalgo, signature

filename:       /lib/modules/4.19.0-8-amd64/misc/vboxdrv.ko
version:        6.0.18 r136238 (0x00290008)
license:        GPL
description:    Oracle VM VirtualBox Support Driver
author:         Oracle Corporation
srcversion:     43F4A2EEB843DF199ED0E7C
depends:
retpoline:      Y
name:           vboxdrv
vermagic:       4.19.0-8-amd64 SMP mod_unload modversions
sig_id:         PKCS#7
signer:         dali
sig_key:        8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45
sig_hashalgo:   sha256
signature:      8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		8F:05:22:A2:E8:7E:9D:69:7E:19:93:D8:F4:1B:4D:DC:B8:7A:F2:45:
		CD:3D:41:65:AF:E8:B1:0F:CF:ED:6A:E6:D1:7B:0A:FF
parm:           force_async_tsc:force the asynchronous TSC mode (int)

Références