
Si vous êtes préoccupé par l’intégrité de votre système, dm-verity est l'un des éléments clés de l'écosystème Linux Pour démarrer en toute sécurité et détecter les altérations du stockage. Initialement intégré au mappeur de périphériques du noyau, il constitue désormais la base du démarrage vérifié sous Android, OpenWrt et les distributions recherchant une sécurité renforcée.
Loin d’être un concept abstrait, dm-verity est configuré et utilisé avec de vrais outils comme veritysetup et systemd-veritysetupIl valide les blocs à la volée grâce à des arbres de hachage et peut réagir aux corruptions grâce à des politiques allant de la journalisation de l'événement au redémarrage ou au plantage du système. Examinons cela de plus près, sans laisser de détails.
Qu'est-ce que dm-verity et pourquoi cela pourrait vous intéresser
dm-verity est une cible de mappage de périphériques dans le noyau qui vérifie l'intégrité d'un périphérique de bloc lors de la lecture des donnéesIl fonctionne en calculant et en vérifiant les hachages de chaque bloc (généralement 4 Ko) par rapport à un arbre de hachage précalculé, généralement à l'aide de SHA-256.
Cette conception permet Les fichiers ne peuvent pas être modifiés silencieusement entre les redémarrages ou pendant l'exécutionIl est essentiel d’étendre la chaîne de démarrage de confiance au système d’exploitation, de limiter la persistance des logiciels malveillants, de renforcer les politiques de sécurité et de garantir le cryptage et les mécanismes MAC pendant le démarrage.
Sur Android (depuis 4.4) et Linux en général, La confiance est ancrée dans le hachage racine de l'arbre, signé et validé avec une clé publique située dans un emplacement protégé (par exemple, sur la partition de démarrage ou dans un fichier UKI signé Secure Boot). Pour casser un bloc, il faudrait casser le hachage cryptographique sous-jacent.
La vérification se fait par bloc et à la demande : La latence ajoutée est minime par rapport au coût d'E/SSi une vérification échoue, le noyau renvoie une erreur d'E/S et le système de fichiers apparaît corrompu, ce qui est normal lorsque les données sont peu fiables. Les applications peuvent décider de poursuivre ou non leur exécution en fonction de leur tolérance aux pannes.
Comment fonctionne l'arbre de vérification en interne
L'arbre de vérification est construit en couches. La couche 0 correspond aux données brutes de l'appareil, divisées en blocs de 4 KUn hachage SHA-256 (salé) est calculé pour chaque bloc. Ces hachages sont ensuite concaténés pour former la couche 1. Cette couche est ensuite regroupée en blocs et rehachée pour former la couche 2, et ainsi de suite jusqu'à ce que tout tienne dans un seul bloc : ce bloc, une fois haché, produit le hachage racine.
Si une couche ne complète pas exactement un bloc, Il est complété par des zéros jusqu'à atteindre 4K Pour éviter toute ambiguïté, la taille totale de l'arborescence dépend de la taille de la partition vérifiée ; en pratique, elle est généralement inférieure à 30 Mo pour les partitions système classiques.
Le processus général est le suivant : choisissez un sel aléatoire, hachez à 4K, calculez SHA-256 avec du sel par bloc, concatène pour former des niveaux, complète la limite du bloc avec des zéros et répète l'opération avec le niveau précédent jusqu'à ce qu'il ne reste qu'un seul hachage racine. Ce hachage racine, ainsi que le sel utilisé, alimentent la table dm-verity et la signature.
Versions et algorithme des formats de disque
Le format des blocs de hachage sur le disque a une version. La version 0 était la version originale utilisée dans Chromium OS:Le sel est ajouté à la fin du processus de hachage, les résumés sont stockés en continu et le reste du bloc est complété par des zéros.
La La version 1 est recommandée pour les nouveaux appareilsLe sel est ajouté au hachage, et chaque condensé est complété par des zéros jusqu'à des puissances de deux, améliorant ainsi l'alignement et la robustesse. La table dm-verity spécifie également l'algorithme (par exemple, SHA1 ou SHA256), bien que SHA256 soit utilisé pour des raisons de sécurité.
tableau dm-verity et paramètres essentiels
La table cible dm-verity décrit où se trouvent les données, où se trouve l'arbre de hachage et comment vérifierChamps de table typiques :
- dev: périphérique avec les données à vérifier (type de chemin /dev/sdXN ou supérieur:inférieur).
- hash_dev: périphérique avec l'arbre de hachage (peut être le même ; si c'est le cas, hash_start doit être en dehors de la plage vérifiée).
- taille_du_bloc_de_données: taille du bloc de données en octets (par exemple 4096).
- taille_du_bloc_de_hachage: taille du bloc de hachage en octets.
- nombre_blocs_de_données: nombre de blocs de données vérifiables.
- bloc_de_début_hachage: décalage (en blocs hash_block_size) par rapport au bloc racine de l'arbre.
- algorithme: algorithme de hachage (par exemple sha256).
- résumé: codage hexadécimal du hachage du bloc racine (y compris le sel selon la version du format) ; cette valeur est celle à laquelle il faut faire confiance.
- de sel: sel hexadécimal.
De plus, il y a paramètres facultatifs très utile pour ajuster le comportement :
- ignorer_la_corruption:Enregistre les blocs corrompus, mais permet à la lecture de continuer.
- redémarrage_sur_corruption: redémarrage en cas de détection de corruption (non compatible avec ignore_corruption et nécessite la prise en charge de l'espace utilisateur pour éviter les boucles).
- panique_contre_la_corruption: : provoque la panique lors de la détection d'une corruption (non compatible avec les versions précédentes).
- redémarrage_sur_erreur y panique_sur_erreur: mêmes réactions mais pour les erreurs d'E/S.
- ignorer_zero_blocks: ne vérifie pas les blocs attendus comme des zéros et renvoie des zéros.
- utiliser_fec_depuis_le_périphérique + racines_fec + blocs_fec + fec_start: Activez Reed–Solomon (FEC) pour récupérer les données lorsque la vérification échoue ; les zones de données, de hachage et de FEC ne doivent pas se chevaucher et les tailles de bloc doivent correspondre.
- vérifier_au_plus_une_fois: Vérifie chaque bloc de données uniquement la première fois qu'il est lu (réduit la surcharge au détriment de la sécurité lors des attaques en direct).
- racine_hash_sig_key_desc: Référence à une clé dans le trousseau pour valider une signature PKCS7 du hachage racine lors de la création du mappage (nécessite une configuration de noyau appropriée et des trousseaux de clés de confiance).
- essayer_vérifier_dans_tasklet: Si les hachages sont mis en cache et que la taille des E/S le permet, vérifie la moitié inférieure pour réduire la latence ; ajusté avec /sys/module/dm_verity/parameters/use_bh_bytes par classe d'E/S.
Signature, métadonnées et ancrage de confiance
Pour que dm-verity soit fiable, Le hachage racine doit être fiable et généralement signéDans Android classique, une clé publique est incluse dans la partition de démarrage, qui est vérifiée en externe par le fabricant ; elle valide la signature de hachage racine et garantit que la partition système n'a pas été modifiée.
Les métadonnées Verity ajoutent une structure et un contrôle de version. Le bloc de métadonnées comprend un nombre magique 0xb001b001 (octets b0 01 b0 01), version (actuellement 0), la signature de la table dans PKCS1.5 (généralement 256 octets pour RSA-2048), la longueur de la table, la table elle-même et le remplissage de zéros jusqu'à 32 Ko.
Dans les implémentations Android, la vérification repose sur fs_mgr et fstabCochez l'entrée correspondante et placez la clé dans /boot/verity_key. Si le numéro magique n'est pas à sa place, la vérification s'arrête pour éviter de vérifier une erreur.
Opération de démarrage vérifiée
La protection réside dans le noyau : Si le noyau est compromis avant son démarrage, l'attaquant conserve le contrôleC'est pourquoi les fabricants valident généralement strictement chaque étape : une clé gravée dans l'appareil vérifie le premier chargeur de démarrage, qui vérifie le suivant, le chargeur de démarrage de l'application et enfin le noyau.
Avec le noyau vérifié, dm-verity est activé lors du montage du périphérique de bloc vérifiéAu lieu de hacher l'intégralité de l'appareil (ce qui serait lent et gaspillerait de l'énergie), il est vérifié bloc par bloc au fur et à mesure des accès. Une défaillance provoque une erreur d'E/S, et les services et applications réagissent selon leur tolérance : ils continuent sans ces données ou plantent complètement.
Correction d'erreurs directe (FEC)
Depuis Android 7.0, FEC (Reed–Solomon) est incorporé aux techniques d'entrelacement Pour réduire l'espace et augmenter la capacité de récupération des blocs endommagés. Ceci fonctionne en conjonction avec dm-verity : si une vérification échoue, le sous-système peut tenter de la corriger avant de la déclarer irrécupérable.
Performances et optimisation
Pour réduire l’impact : Activer l'accélération SHA-2 par NEON sur ARMv7 et les extensions SHA-2 sur ARMv8 Depuis le noyau. Ajustez les paramètres de lecture anticipée et de prélecture du cluster en fonction de votre matériel ; la vérification par bloc augmente généralement peu le coût des E/S, mais ces paramètres font toute la différence.
Premiers pas sur Linux (systemd, veritysetup) et Android
Sur un Linux moderne avec systemd, dm-verity permet une racine vérifiée en lecture seule En utilisant Veritysetup (élément de cryptsetup), systemd-veritysetup.generator et systemd-veritysetup@.service. Il est recommandé d'inclure Secure Boot et une image de noyau unifiée (UKI) signée, bien que cela ne soit pas strictement obligatoire.
Préparation et partitionnement recommandé
Partie d'un système fonctionnel et ajusté. Réserver un volume pour l'arbre de hachage (8 à 10 % de la taille de la racine sont généralement suffisants) et pensez à séparer /home et /var si vous devez écrire. Un schéma typique inclut : ESP (pour le chargeur de démarrage), XBOOTLDR (pour les UKI), la racine (avec ou sans chiffrement), la partition VERITY et, éventuellement, /home et /var.
En tant que racine, EROFS est une alternative très intéressante à ext4 ou squashfs:Il est en lecture seule par conception, avec de très bonnes performances sur flash/SSD, compression lz4 par défaut, et largement utilisé sur les téléphones Android avec dm-verity.
Fichiers devant être accessibles en écriture
Avec root ro, certains programmes s'attendent à écrire sur /etc ou pendant l'initialisationVous pouvez le déplacer vers /var/etc et créer un lien symbolique pour tout ce qui doit être modifié (par exemple, les connexions NetworkManager dans /etc/NetworkManager/system-connections). Notez que systemd-journald requiert la présence de /etc/machine-id dans le répertoire racine (et non un lien symbolique) pour éviter tout problème lors des démarrages prématurés.
Pour savoir quels changements surviennent dans l'exécution, utiliser dracut-overlayroot: superpose un système de fichiers temporaire (tmpfs) à la racine, et tout ce qui est écrit apparaît dans /run/overlayroot/u. Ajoutez le module à /usr/lib/dracut/modules.d/, incluez overlayroot dans dracut et définissez overlayroot=1 sur la ligne du noyau ; ainsi, vous verrez ce qui doit être migré vers /var.
Exemples utiles : pacman et NetworkManager
Dans Arch, c'est pratique Déplacez la base de données Pacman vers /usr/lib/pacman Pour que le système de fichiers racine reflète toujours les paquets installés. Ensuite, redirigez le cache vers /var/lib/pacman et liez-le. Pour modifier la liste des miroirs sans toucher à la racine, déplacez-la vers /var/etc et liez-la quand même.
Avec NetworkManager, déplacer les connexions système vers /var/etc/NetworkManager et un lien depuis /etc/NetworkManager/system-connections. Cela permet de préserver l'intégrité de la racine et de conserver la configuration accessible en écriture.
Construction de la vérité et test
À partir d'un live et avec tout parfait et monté en ro, créez l'arbre et le roothash avec format de configuration de vérité: Lors de son exécution, il affiche la ligne de hachage racine, que vous pouvez enregistrer dans roothash.txt. Exécutez-le pour tester avec veritysetup open root-device root verity-device $(cat roothash.txt) et montez /dev/mapper/root.
Si tu préfères, génère d'abord l'arbre dans un fichier (verity.bin) et l'écrire sur la partition VERITY. L'ensemble résultant est : l'image racine, l'arborescence Verity et le hachage racine à épingler au démarrage.
Configurer la ligne du noyau
Ajoutez ces paramètres : systemd.verity=1, roothash=contents_of_roothash.txt, systemd.verity_root_data=ROOT-PATH (par exemple, LABEL=OS) et systemd.verity_root_hash=VERITY-PATH (par exemple, LABEL=VERITY). Définissez systemd.verity_root_options sur restart-on-corruption ou panic-on-corruption pour des politiques strictes.
Autres options recommandées : ro (si vous n'utilisez pas EROFS/squashfs), rd.emergency=redémarrage y rd.shell=0 (empêcher les shells non autorisés en cas d'échec du démarrage), et confinement = confidentialité pour protéger la mémoire du noyau contre tout accès.
Partitions supplémentaires avec verity
Pas seulement la racine : Vous pouvez définir d'autres mappages dans /etc/veritytab et systemd-veritysetup@.service les assemblera au démarrage. N'oubliez pas : il est plus facile de monter une partition non root en mode RW, et un utilisateur root pourrait désactiver Verity sur ces partitions, ce qui réduit la sécurité.
Sécurité : Secure Boot, UKI et modules signés
dm-verity n’est pas une solution miracle. Signez l'UKI et activez Secure Boot avec vos propres clés Pour empêcher quiconque de surcharger kernel/initramfs/cmdline (qui inclut le hachage racine). Des outils comme sbupdate-git ou sbctl permettent de conserver les images signées et la chaîne de démarrage intacte.
Si vous activez le verrouillage du noyau ou la vérification de la signature du module, Les modules DKMS ou hors arbre doivent être signés sinon ils ne se chargeront pas. Envisagez un noyau personnalisé avec prise en charge de la signature pour votre pipeline (voir les modules du noyau signés).
Chiffrement, TPM et mesure
dm-verity protège l'intégrité, non-confidentialitéVous pouvez laisser la racine non chiffrée si elle ne contient aucun secret et si la chaîne de démarrage est protégée. Si vous utilisez des fichiers de clés de la racine pour déverrouiller d'autres volumes, il est conseillé de les chiffrer.
Avec TPM 2.0, systemd-cryptenroll permet de lier les clés aux PCR 0,1,5,7 (firmware, options, GPT, état du démarrage sécurisé). Ajoutez rd.luks.options=LUKS_UUID=tpm2-device=auto et assurez-vous d'inclure la prise en charge TPM2 dans l'initramfs. systemd-boot mesure le kernel.efi dans PCR4, ce qui est utile pour invalider les clés si l'UKI ou sa ligne de commande change.
Mises à jour et modèles de déploiement
Une racine en lecture seule vérifiée Il n'est pas mis à jour avec le gestionnaire de paquets de manière traditionnelle. L'idéal est de construire de nouvelles images avec des outils comme le projet Yocto et les publier. systemd dispose de systemd-sysupdate et systemd-repart pour un téléchargement et un flashage d'images robustes.
Une autre stratégie est Schéma A/BVous conservez deux racines et deux vérités. Copiez la racine active vers la racine inactive, appliquez les modifications et réexécutez la vérification. Revenez au prochain démarrage. Si vous utilisez UKI, pensez à mettre à jour le hachage racine dans la ligne de commande ou à reconstruire l'UKI signé.
Pour une persistance facultative, utiliser OverlayFS sur la racine vérifiée avec upper dans tmpfs ou disk. Vous pouvez également utiliser systemd.volatile=overlay pour une persistance temporaire. Flatpak facilite l'installation d'applications dans /var et /home sans toucher à /.
Il existe des packages automatisés (par exemple verity-squash-root dans AUR) qui construisent une racine squashfs et signer le roothash avec le noyau et l'initramfs, vous permettant de choisir entre le mode persistant ou éphémère et de conserver le dernier système de fichiers racine comme sauvegarde. Remarque : l'ajout de la persistance à une racine vérifiée a des cas d'utilisation restreints ; essayez de conserver les données d'application sur des partitions distinctes.
Android : système en tant que root, AVB et superpositions de fournisseurs
Depuis Android 10, RootFS arrête de fonctionner sur le disque RAM et s'intègre à system.img. (système en tant que root). Les appareils lancés avec Android 10 utilisent toujours ce schéma et nécessitent un disque RAM pour dm-linear. BOARD_BUILD_SYSTEM_ROOT_IMAGE est défini sur false dans cette version afin de distinguer l'utilisation d'un disque RAM de l'activation directe de system.img.
Android 10 intègre partitions dynamiques et une première étape d'initialisation Ce qui active la partition système logique ; le noyau ne la monte plus directement. Les OTA système uniquement nécessitent une conception « système en tant que root », obligatoire sur les appareils Android 10.
En aucun cas A/B, garder la récupération séparée du démarrageContrairement à A/B, il n'y a pas de sauvegarde boot_a/boot_b, donc la suppression de la récupération dans non-A/B peut vous laisser sans mode de récupération si une mise à jour de démarrage échoue.
Le noyau monte system.img sur /converity via deux chemins : vboot 1.0 (correctifs pour que le noyau analyse les métadonnées Android dans /system et dérive les paramètres dm-verity ; la ligne de commande inclut root=/dev/dm-0, skip_initramfs et init=/init avec dm=…) ou vboot 2.0/AVB, où le chargeur de démarrage intègre libavb, lit le descripteur de l'arbre de hachage (dans vbmeta ou system), construit les paramètres et les transmet au noyau dans la ligne de commande, avec le support FEC et des indicateurs comme restart_on_corruption.
Avec le système comme root, n'utilisez pas BOARD_ROOT_EXTRA_FOLDERS Pour les dossiers racines spécifiques à l'appareil : ils disparaîtront lors du flashage d'un GSI. Définissez des montages spécifiques sous /mnt/vendor/. , que fs_mgr crée automatiquement, et les référence dans le fstab de l'arborescence des périphériques.
Android permet une superposition de fournisseur depuis /product/vendor_overlay/: init montera dans /vendor les sous-répertoires qui répondent aux exigences du contexte SELinux et à l'existence de /vendor/ . Nécessite CONFIG_OVERLAY_FS=yy, sur les noyaux plus anciens, le patch override_creds=off.
Mise en œuvre typique : installe les fichiers précompilés dans device/ / /vendor_overlay/, ajoutez-les à PRODUCT_COPY_FILES avec find-copy-subdir-files dans $(TARGET_COPY_OUT_PRODUCT)/vendor_overlay, définissez les contextes dans file_contexts pour etc et app (par exemple vendor_configs_file et vendor_app_file) et autorisez le montage sur ces contextes dans init.te. Testez avec atest vfs_mgr_vendor_overlay_test dans userdebug.
Dépannage : message de corruption dm-verity sur Android
Sur les appareils dotés d'emplacements A/B, changez d'emplacement ou Flashage de vbmeta/boot sans cohérence avec le roothash Cela peut déclencher l'avertissement suivant : dm-verity corrompu, votre appareil n'est pas fiable. Des commandes telles que fastboot flash –disable-verity –disable-verification vbmeta vbmeta.img désactivent la vérification, mais laissent le système sans garantie d'intégrité.
Certains chargeurs de démarrage prennent en charge fastboot OEM désactiver_dm_verity et son opposé, enable_dm_verity. Cela fonctionne sur certains modèles, mais pas sur d'autres ; et peut nécessiter kernel/magisk avec des options ajustées. À utiliser à vos risques et périls : la prudence est de mise. aligner boot, vbmeta et système, signez ou régénérez l'arbre et assurez-vous que le hachage racine attendu correspond à celui configuré.
Si après l'avertissement vous pouvez continuer à appuyer sur le bouton d'alimentation, le système démarre, mais vous n'avez plus de chaîne de confiance intactePour supprimer le message sans sacrifier la sécurité, restaurez les images signées d'origine ou reconstruisez/vérifiez vbmeta avec l'arbre de hachage correct, au lieu de désactiver Verity.
Plateformes i.MX et OpenWrt
Sur i.MX6 (par exemple sabresd), configurer le noyau avec le support DM_VERITY et FECGénérez l'arbre avec Veritysetup, stockez le hachage racine de manière sécurisée et transmettez les paramètres appropriés dans la ligne de commande ou intégrez-le via initramfs avec systemd-veritysetup. Si vous n'utilisez pas dm-crypt, CAAM n'est pas nécessaire pour Verity ; l'accent est mis sur l'intégrité.
Dans OpenWrt et dans systèmes Linux embarqués avec OpenEmbedded, Des efforts sont en cours pour intégrer dm-verity et SELinux (Tâches Bootlin révisées avec l'intention d'intégrer la prise en charge). C'est une solution naturelle : les routeurs et les équipements réseau bénéficient d'une racine immuable, vérifiée et renforcée par MAC.
Construction manuelle de l'arbre et des métadonnées (vue détaillée)
cryptsetup peut générer l'arbre pour vous, mais si vous préférez comprendre le format, la définition de ligne de table compacte inclut : nom de mappage, périphérique de données, tailles de bloc de données et de hachage, taille de l'image en blocs, position de départ du hachage (image du bloc + 8 si concaténé), hachage racine et sel. Après avoir généré les couches concaténées (de haut en bas, à l'exclusion de la couche 0), vous écrivez l'arborescence sur le disque.
Pour tout emballer, composez la table dm-verity, signez-la (typiquement RSA-2048) et regroupez signature+table dans les métadonnées Avec un en-tête versionné et un numéro magique. Ensuite, il concatène l'image système, les métadonnées Verity et l'arbre de hachage. Dans fstab, il marque fs_mgr comme verify et place la clé publique dans /boot/verity_key pour valider la signature.
Optimiser avec Accélérations SHA-2 pour votre CPU et ajustez la lecture anticipée/le prélecture du cluster. Sur le matériel ARM, les extensions NEON SHA-2 (ARMv7) et SHA-2 (ARMv8) réduisent considérablement la charge de vérification.
Dans tout déploiement, n'oubliez pas que la valeur de hachage racine doit être protégée: qu'il soit compilé dans une UKI signée, dans la partition de démarrage signée ou validé par le chargeur de démarrage via AVB. Tout ce qui suit hérite de cette confiance.
Avec tout ce qui précède en place, dm-verity devient une base solide pour les systèmes immuables, mobiles et embarqués, prenant en charge les mises à jour transactionnelles, les superpositions de configuration et un modèle de sécurité moderne qui réduit la surface d'attaque et empêche la persistance sans sacrifier les performances.


