Debian, SecureBoot et les modules noyaux DKMS🔗

Posted by Médéric Ribreux 🗓 In blog/ Debian/

#security #debian

Introduction

Debian Buster est sortie récemment et prend en charge SecureBoot. J'avais toujours refusé de m'occuper de cette manière de booter mais, à l'occasion de la sortie de la dernière Debian, je me suis dit que je devais essayer de m'intéresser au sujet.

En suivant les instructions officielles, je suis parvenu à booter avec SecureBoot en pratiquement une seule ligne de commande et une manipulation dans le BIOS (j'étais déjà en boot UEFI).

Mais après l'effet whaou, je me suis rendu compte que Wireguard ne fonctionnait plus. En effet, ce dernier n'est (pas encore) inclus dans le noyau Linux et il est fourni via DKMS sous Debian (il est compilé à l'installation du paquet). Or, quand SecureBoot est activé, les modules non signés par la clef Debian ne sont pas activé (une conf du kernel pour ça). Sur mon portable, ça va encore plus loin: le pilote Wifi est en DKMS !

Néanmoins, j'ai lu qu'il devait être possible de signer ces modules pour pouvoir les lancer. Voici ce que j'ai trouvé (qui bien sûr n'est pas une référence)…

Pré-requis

Génération des clefs de signature

Ici, rien de fantoche, on va juste créer un couple clef privée/publique en RSA sur 2048bits. La clef publique sera convertie au format DER puis uploadée dans la base MOK.

Pour des questions de sécurité et aussi de facilités, nous allons stocker ces clefs dans le répertoire /etc/dkms/keys créé en mode 700.

On va donc commencer à générer le couple clef privée/publique. Elle sera valable 10 ans et pour l'identifier, je vais mettre un CN avec le nom de la machine:

# mkdir -p /etc/dkms/keys
# chmod 700 /etc/dkms/keys
# cd /etc/dkms/keys/
# openssl req -new -x509 -newkey rsa:2048 -keyout MOK.key -out MOK.crt -nodes -days 3650 -subj "/CN=nom_de_machine/"

Ensuite, nous allons simplement la convertir au format DER:

# openssl x509 -in MOK.crt -out MOK.der -outform DER

Ok, nos clefs sont disponibles.

Chargement des clefs dans la "base" MOK

Maintenant, il reste à injecter la clef publique dans la base MOK. Pour cela, nous allons utiliser l'outil mokutil qui indique à shim de charger des clefs au prochain reboot:

# mokutil --import /etc/dkms/MOK.der

Attention, mokutil va vous demander un mot de passe pour protéger l'accès à cette clef (uniquement pour son chargement dans la base MOK via shim). Je vous conseille de mettre un mot de passe qui se tape bien en qwerty sinon, vous pourriez très bien ne pas pouvoir le taper au clavier.

Vous pouvez maintenant redémarrer la machine et vous aurez un écran spécifique de shim pour importer la clef publique dans la base MOK. Oui, ça fait bizarre de devoir rebooter mais c'est la méthode sécurisée pour le faire: ça évite qu'un mec qui accès à mokutil puisse balancer des clefs dans votre dos…

Procédure de signature manuelle

Voyons maintenant comment signer nos modules pour les faire prendre en compte par MOK. Il existe tout simplement un outil dédié à ça dans le paquet linux-kbuild.

# cd /usr/lib/modules/4.19.0-5-adm64/updates/dkms/
# /usr/lib/linux-kbuild-/scripts/sign-file sha256 /etc/dkms/keys/MOK.key /etc/dkms/keys/MOK.der ./monmodule.ko

Sous Debian, les modules DKMS ne sont pas compressés donc on peut les signer directement. Si vous voulez faire un test, faites un simple modprobe sur votre module et il devrait se charger tout seul.

Automatisation

Maintenant que nous avons une méthode assez basique, il reste à l'automatiser. En effet, à chaque mise à jour du noyau, il faudrait signer à nouveau les modules. Le faire à la main à chaque fois est fastidieux. Heureusement, dkms possède des éléments permettant de faire le job. Encore faut-il les connaître.

Bon, DKMS est un utilitaire codé en Bash ! En lisant le code et aussi le manuel qui semble complet, on apprend que le fichier /etc/dkms/framework.conf contient des directives de configuration qui s'appliquent à tous les scripts dkms.conf.

En poursuivant l'étude, on voit une variable nommée POST_BUILD qui contient l'emplacement d'un script à lancer après le build des modules. Parfait, ça semble être le bon endroit pour indiquer un script de signature. Néanmoins, le script est lancé sans arguments…

Mais, pas de bol, POST_BUILD, comme d'autres variables ne sont pas accessibles via framework.conf. Le seul "truc" qu'on peut faire, c'est de nommer un fichier .conf avec le même nom que le module et y ajouter notre POST_BUILD. Un peu laborieux si on a beaucoup de modules mais ça ne se fait qu'une fois !

Avant tout ça, il nous faut un script qui puisse se lancer sans arguments et qui récupère les variables définies auparavant dans la chaîne DKMS pour nous permettre de signer le bon module (avec la bonne clef).

#!/bin/bash

# Sign a DKMS module with a key

privKey="/etc/dkms/keys/MOK.key"
pubKey="/etc/dkms/keys/MOK.der"
KERNVER=$(awk -F . '{print $1"."$2}' <<< "$(uname -r)")

cd ../$kernelver/$arch/module/

for kernel_object in *ko; do
echo "Signing kernel_object: $kernel_object"
/usr/lib/linux-kbuild-$KERNVER/scripts/sign-file sha256 $privKey $pubKey "$kernel_object";
done

exit 0

N'oubliez pas de le rendre exécutable par root au moins.

Ensuite, il reste à modifier le fichier /etc/dkms/monmodule.conf et y ajouter la ligne:

POST_BUILD=../../../../../../etc/dkms/sign_module.sh

Au prochain build DKMS du module, votre module sera automatiquement signé (même si en passant par apt install, on ne voit pas forcément le message de notre script).

Conclusions

Ce que j'ai décris ressemble presque à un hack mais ça fonctionne à peu près. Quelle joie de se dire qu'on a une machine sécurisée par SecureBoot mais qui utilise aussi des modules signés avec notre propre clef.

Avec l'implémentation Debian de SecureBoot, il n'y a finalement pas tant de choses à faire que ça et c'est tant mieux. Peut-être qu'un jour il y aura une méthode officielle pour ça ?

Références