FR

Tous les articles [ Ingénierie ] N° 02 / 16 Jour J 9 min de lecture // Read in English

Rejouer notre démo chez vous : la chaîne complète sur émulateur

Tutoriel pas à pas pour rejouer la démo OC2-Edge sur émulateur : Secure Boot avec OVMF, TPM logiciel swtpm, image signée UKI, faute injectée sans cascade, mise à jour A/B avec rollback et COP dans le navigateur.

Schéma de la chaîne émulée : OVMF, swtpm, UKI signée, rootfs vérifié, COP, avec un point d'injection de faute marqué en accent.

Une démonstration que seul l’éditeur peut exécuter ne prouve rien. La démo OC2-Edge se rejoue donc chez vous, sur émulateur, sans matériel dédié. Cet article décrit la procédure complète : du clone du dépôt jusqu’à un nœud émulé qui démarre en Secure Boot réel, installe un module signé, encaisse une faute injectée sans cascade, applique une mise à jour A/B avec rollback automatique et alimente une COP (Common Operational Picture, image tactique partagée) dans votre navigateur.

Le mot « réel » a ici un sens précis. L’émulateur ne simule pas le Secure Boot : il l’exécute. Le firmware UEFI (Unified Extensible Firmware Interface) est OVMF, le firmware open source du projet EDK II de TianoCore [1]. Il applique la vérification de signature définie par la spécification UEFI [4], exactement comme un firmware de calculateur embarqué. Le TPM (Trusted Platform Module) est swtpm, une implémentation logicielle complète de l’interface TPM 2.0 [2]. La chaîne cryptographique qui refuse une image altérée sur votre machine est la même que celle qui la refuserait sur une cible physique. Seul le silicium diffère.

Ce que la démo montre

Le scénario couvre cinq comportements, dans l’ordre :

  1. démarrage vérifié : OVMF valide la signature de l’UKI (Unified Kernel Image, image unifiée regroupant noyau, initrd et ligne de commande dans un seul binaire UEFI signé [3]), puis le système vérifie l’intégrité du rootfs ;
  2. installation d’un module signé : le nœud vérifie la signature ML-DSA-65 (FIPS 204) du paquet avant de l’exécuter dans son bac à sable ;
  3. faute injectée : un module reçoit un crash provoqué ; la supervision le confine et le redémarre, les autres modules et la COP restent servis ;
  4. mise à jour A/B : le nœud bascule sur une image de mise à jour volontairement défectueuse, échoue à la valider au démarrage et revient seul sur la partition saine ;
  5. COP : le navigateur affiche l’image tactique servie par le nœud, en mode MAQUETTE explicite (données simulées, jamais présentées comme réelles).

Prérequis honnêtes

La démo tourne sur Linux ou sur Windows via WSL2 (Windows Subsystem for Linux, version 2). Les besoins suivants sont des ordres de grandeur, pas des minima mesurés :

  • un processeur x86-64 avec virtualisation matérielle activée (VT-x ou AMD-V) ; sans elle, QEMU [5] fonctionne en émulation pure, nettement plus lente ;
  • de l’ordre de 8 Go de RAM libres, dont environ 4 Go alloués au nœud émulé (valeur indicative) ;
  • de l’ordre de 20 Go d’espace disque pour le dépôt, les images et les artefacts de build (valeur indicative) ;
  • une première exécution complète de l’ordre de 30 à 60 minutes, téléchargements et préparation des images compris (valeur indicative, dépendante de la machine et du réseau) ; les exécutions suivantes sont beaucoup plus courtes.

Paquets requis côté hôte : qemu-system-x86_64, swtpm, git, make et un environnement shell POSIX. Le script de vérification de l’étape 1 contrôle leur présence. Sous WSL2, la virtualisation imbriquée doit être active ; le même script le vérifie.

Étape 1 : cloner le dépôt et vérifier l’environnement

Note : <URL_DU_DEPOT> désigne l’URL publique du dépôt OC2-Edge, disponible depuis la section Open Source du site. Elle n’est pas reproduite ici pour que ce tutoriel reste valable si le dépôt change d’hébergement.

git clone <URL_DU_DEPOT> oc2-edge
cd oc2-edge
./scripts/check-env.sh

Le script check-env.sh vérifie les binaires requis, la virtualisation matérielle et l’espace disque. Il s’arrête avec un message explicite au premier prérequis manquant. Ne poursuivez pas tant qu’il ne termine pas sans erreur : chaque étape suivante suppose un environnement validé.

Étape 2 : préparer le firmware et les clés de démonstration

make demo-prepare

Cette cible effectue trois choses :

  1. elle récupère les binaires OVMF avec Secure Boot activé (variante secboot d’EDK II [1]) ;
  2. elle génère une hiérarchie de clés de démonstration (PK, KEK, db, au sens de la spécification UEFI [4]) et l’enrôle dans une copie locale des variables du firmware ;
  3. elle construit ou récupère l’image du nœud : une UKI signée avec la clé de démonstration, et un rootfs immuable dont l’arbre de hachage est scellé dans l’UKI.

Point important : les clés de démonstration sont générées localement, sur votre machine, à chaque préparation. Elles ne protègent rien et ne doivent protéger rien. La gestion de clés réelle du projet (clés de racine hors ligne, signatures ML-DSA-65) fait l’objet d’une documentation séparée dans le dépôt ; la démo en reproduit la topologie, pas les secrets.

Étape 3 : démarrer le TPM logiciel

mkdir -p ./run/tpm
swtpm socket --tpm2 \
  --tpmstate dir=./run/tpm \
  --ctrl type=unixio,path=./run/tpm/swtpm.sock \
  --daemon

swtpm expose un TPM 2.0 complet sur une socket locale [2]. QEMU s’y connecte et le présente au système invité comme un TPM matériel. Le nœud émulé s’en sert comme un nœud physique : mesure de la chaîne de démarrage dans les PCR (Platform Configuration Registers), scellement d’états sur ces mesures. Si vous utilisez la cible make demo-boot de l’étape suivante, ce lancement est effectué pour vous ; la commande est donnée ici pour montrer qu’il n’y a rien d’autre derrière.

Étape 4 : démarrer le nœud en Secure Boot

make demo-boot

La cible enveloppe un appel QEMU dont la forme est la suivante (chemins simplifiés) :

qemu-system-x86_64 \
  -machine q35,smm=on -m 4G \
  -drive if=pflash,format=raw,readonly=on,file=./out/OVMF_CODE.secboot.fd \
  -drive if=pflash,format=raw,file=./out/OVMF_VARS.demo.fd \
  -chardev socket,id=chrtpm,path=./run/tpm/swtpm.sock \
  -tpmdev emulator,id=tpm0,chardev=chrtpm \
  -device tpm-tis,tpmdev=tpm0 \
  -drive file=./out/oc2-node.img,format=raw \
  -netdev user,id=n0,hostfwd=tcp::8443-:443 \
  -device virtio-net-pci,netdev=n0

Séquence attendue : OVMF démarre, vérifie la signature de l’UKI contre la base db enrôlée à l’étape 2, puis transfère le contrôle au stub UEFI de l’UKI (systemd-stub [6]), qui mesure ses composants dans le TPM avant de lancer le noyau. Le système monte ensuite le rootfs en lecture seule et vérifie son intégrité bloc par bloc contre l’arbre de hachage scellé dans l’UKI. Le nœud atteint alors son état nominal. La console série affiche la progression ; l’état du nœud se lit aussi par la commande de contrôle :

./scripts/oc2ctl status

L’état attendu est un nœud en posture nominale, partition A active, aucun module tiers chargé.

Étape 5 : installer un module signé

./scripts/oc2ctl module install ./out/modules/demo-tracker.oc2pkg
./scripts/oc2ctl module list

Le nœud vérifie la signature ML-DSA-65 (FIPS 204) du paquet, contrôle que la clé signataire figure dans la politique de confiance de l’instance, puis démarre le module dans son environnement d’isolation (namespaces, cgroups, filtre seccomp ; l’article n° 5 détaille ces couches [voir /blog/mille-crashs-par-nuit/]). La liste des modules doit montrer le module en état actif, avec l’identité de sa clé signataire.

Étape 6 : injecter une faute, observer l’absence de cascade

./scripts/oc2ctl fault inject --module demo-tracker --kind crash
./scripts/oc2ctl status --watch

Comportement attendu, sans autre intervention : le module passe en état défaillant, la supervision journalise l’événement, confine la défaillance au périmètre du module et le redémarre selon sa politique. Pendant toute la séquence, les autres services du nœud et la COP restent joignables. C’est le comportement dont l’article n° 5 décrit la vérification systématique : la propriété d’anti-cascade n’est pas une intention, c’est une exigence testée chaque nuit.

Étape 7 : mise à jour A/B et rollback automatique

make demo-update-rollback

Le scénario applique sur la partition B une image de mise à jour construite pour échouer sa validation. Le nœud redémarre sur B, constate l’échec du démarrage vérifié, décompte ses essais et rebascule seul sur la partition A, saine. Comportement attendu : après la séquence, oc2ctl status montre la partition A active et un événement de rollback dans le journal. Aucune intervention manuelle n’est requise ; c’est le point du mécanisme. La distribution des mises à jour réelles passe par un dépôt TUF (The Update Framework [7]), qui protège les métadonnées de mise à jour elles-mêmes ; la démo utilise un dépôt TUF local.

Étape 8 : ouvrir la COP

./scripts/oc2ctl cop url

La commande affiche l’URL locale de la COP (par défaut sur le port redirigé à l’étape 4). Ouvrez-la dans un navigateur standard : aucun client lourd, aucune extension. La COP démarre en mode MAQUETTE, affiché en permanence à l’écran : les pistes visibles sont simulées et présentées comme telles. Le mode LIVE existe dans le produit mais n’a pas de sens sur un nœud de démonstration sans capteurs.

Casser la chaîne volontairement

Une chaîne de confiance se juge à ce qu’elle refuse. Le dépôt fournit des cibles de sabotage ; chacune modifie un artefact, relance le scénario et vous laisse observer le refus. Les comportements décrits ci-dessous sont les comportements attendus ; les messages exacts dépendent des versions du firmware et du système, et ne sont pas reproduits ici.

UKI altérée.

make sabotage-uki
make demo-boot

La cible inverse des octets dans l’UKI signée. Comportement attendu : OVMF refuse d’exécuter le binaire, dont la signature ne correspond plus au contenu [4]. Le nœud ne démarre pas ; il n’existe pas d’état « démarré mais douteux » sur cette voie. La restauration passe par make demo-prepare.

Rootfs modifié.

make sabotage-rootfs
make demo-boot

La cible modifie des blocs du rootfs sans toucher l’UKI. La signature de l’UKI reste valide, le démarrage commence, puis la vérification d’intégrité du rootfs détecte la divergence entre les blocs lus et l’arbre de hachage scellé. Comportement attendu : le nœud refuse l’état nominal et adopte une posture dégradée définie par sa politique, dans laquelle il signale l’atteinte à l’intégrité au lieu de servir des données depuis un système de fichiers suspect.

Paquet falsifié.

make sabotage-package
./scripts/oc2ctl module install ./out/modules/demo-tracker.oc2pkg

La cible modifie le paquet du module après signature. Comportement attendu : la vérification ML-DSA-65 échoue, le nœud rejette l’installation, journalise le rejet et ne charge rien. Le paquet falsifié n’atteint jamais l’environnement d’exécution.

Limites de l’émulation

La démo prouve la chaîne logique et cryptographique, pas les performances ni le comportement sur silicium. Les temps de démarrage mesurés sous QEMU n’engagent à rien pour une cible embarquée. Le projet couvre l’émulation x86-64 et ARM64 ; la campagne sur Jetson est outillée mais n’a pas encore produit de résultats silicium, et le dépôt le dit tel quel. Chaque comportement décrit dans cet article correspond à un test automatisé du dépôt public (~930 tests au vert) : si votre exécution diverge de la description, l’un des deux a tort, et cela s’appelle un rapport de bug bienvenu.

Sources

  1. TianoCore, « EDK II » (OVMF, paquet OvmfPkg), https://github.com/tianocore/edk2
  2. S. Berger, « swtpm : Software TPM Emulator », https://github.com/stefanberger/swtpm
  3. UAPI Group, « UAPI.5 Unified Kernel Images », https://uapi-group.org/specifications/specs/unified_kernel_image/
  4. UEFI Forum, « UEFI Specification », https://uefi.org/specifications
  5. QEMU Project, « QEMU Documentation », https://www.qemu.org/docs/master/
  6. freedesktop.org, « systemd-stub », https://www.freedesktop.org/software/systemd/man/latest/systemd-stub.html
  7. The Update Framework, « TUF Specification », https://theupdateframework.io/