Come usare dm-verity su Linux: una guida completa e pratica

  • dm-verity verifica i blocchi al volo con un albero hash radice firmato, ancorando la catena di fiducia di avvio.
  • La sua distribuzione moderna combina veritysetup, systemd-veritysetup, Secure Boot e UKI per proteggere il kernel, initramfs e cmdline.
  • Android utilizza system-as-root e AVB per passare i parametri dm-verity; FEC e i criteri di reazione migliorano la robustezza.
  • La radice immutabile richiede la separazione dei dati scrivibili (/var, /home) e la pianificazione degli aggiornamenti tramite immagini o schemi A/B.

dm-verity su Linux

Se sei preoccupato per l'integrità del tuo sistema, dm-verity è uno dei pezzi chiave dell'ecosistema Linux per avviare in modo sicuro e rilevare manomissioni dello storage. È nato come parte del device mapper del kernel ed è ora la base per l'avvio verificato in Android, OpenWrt e distribuzioni che richiedono una maggiore sicurezza.

Lungi dall'essere un concetto astratto, dm-verity è configurato e utilizzato con strumenti reali come veritysetup e systemd-veritysetupConvalida i blocchi al volo utilizzando alberi hash e può reagire alla corruzione con policy che vanno dalla registrazione dell'evento al riavvio o all'arresto anomalo del sistema. Diamo un'occhiata più da vicino, senza tralasciare nulla.

Cos'è dm-verity e perché potrebbe interessarti

Verifica dell'integrità con dm-verity

dm-verity è un target device-mapper nel kernel che verifica l'integrità di un dispositivo a blocchi durante la lettura dei datiFunziona calcolando e verificando gli hash di ciascun blocco (solitamente 4K) rispetto a un albero hash precalcolato, in genere utilizzando SHA-256.

Questo design consente I file non possono essere modificati silenziosamente tra i riavvii o durante l'esecuzioneÈ fondamentale per estendere la catena di fiducia dell'avvio al sistema operativo, limitando la persistenza del malware, rafforzando le policy di sicurezza e garantendo la crittografia e i meccanismi MAC durante l'avvio.

Su Android (dalla versione 4.4) e Linux in generale, La fiducia è ancorata all'hash radice dell'albero, firmato e convalidato con una chiave pubblica situata in una posizione protetta (ad esempio, sulla partizione di avvio o in un file UKI firmato Secure Boot). Per rompere un blocco, è necessario rompere l'hash crittografico sottostante.

La verifica avviene a blocchi e su richiesta: La latenza aggiunta è minima rispetto al costo di I/OSe un controllo fallisce, il kernel restituisce un errore di I/O e il file system appare corrotto, il che è normale quando i dati non sono affidabili. Le app possono decidere se continuare o meno in base alla loro tolleranza agli errori.

Come funziona internamente l'albero di verifica

L'albero di verifica è costruito a strati. Il livello 0 contiene i dati grezzi del dispositivo, suddivisi in blocchi da 4K; per ogni blocco viene calcolato un hash SHA-256 (salato). Questi hash vengono quindi concatenati per formare il livello 1. Il livello 1 viene quindi raggruppato in blocchi e sottoposto a rielaborazioni hash per formare il livello 2, e così via fino a quando tutto rientra in un singolo blocco: tale blocco, una volta sottoposto a hash, produce l'hash radice.

Se uno strato non completa esattamente un blocco, Viene riempito con zeri fino a raggiungere 4K Per evitare ambiguità. La dimensione totale dell'albero dipende dalla dimensione della partizione da controllare; in pratica, è solitamente inferiore a 30 MB per le tipiche partizioni di sistema.

Il processo generale è: scegli un sale casuale, hash a 4K, calcola SHA-256 con sale per blocco, concatena per formare livelli, riempie il confine del blocco con zeri e ripete l'operazione con il livello precedente finché non rimane un singolo hash radice. Tale hash radice, insieme al sale utilizzato, alimenta la tabella dm-verity e la firma.

Versioni e algoritmi del formato del disco

Il formato dei blocchi hash su disco ha una versione. La versione 0 era la versione originale utilizzata in Chromium OS: Il sale viene aggiunto alla fine del processo di hashing, i digest vengono memorizzati continuamente e il resto del blocco viene riempito con zeri.

La Per i nuovi dispositivi è consigliata la versione 1: Il sale viene anteposto all'hash e ogni digest viene completato con zeri fino a potenze di due, migliorando l'allineamento e la robustezza. La tabella dm-verity specifica anche l'algoritmo (ad esempio, sha1 o sha256), sebbene per motivi di sicurezza attuali venga utilizzato sha256.

tabella dm-verity e parametri essenziali

La tabella di destinazione dm-verity descrive dove si trovano i dati, dove si trova l'albero hash e come verificarloCampi tipici della tabella:

  • dev: dispositivo con i dati da verificare (tipo di percorso /dev/sdXN o maggiore:minore).
  • hash_dev: dispositivo con l'albero hash (può essere lo stesso; in tal caso, hash_start deve essere al di fuori dell'intervallo controllato).
  • dimensione_blocco_dati: dimensione del blocco dati in byte (ad esempio 4096).
  • dimensione_blocco_hash: dimensione del blocco hash in byte.
  • num_blocchi_dati: numero di blocchi di dati verificabili.
  • blocco_di_avvio_hash: offset (in blocchi hash_block_size) rispetto al blocco radice dell'albero.
  • algoritmo: algoritmo hash (ad esempio sha256).
  • digerire: codifica esadecimale dell'hash del blocco radice (incluso il sale in base alla versione del formato); questo è il valore di cui fidarsi.
  • sale: sale esadecimale.

Inoltre ci sono parametri facoltativi molto utile per regolare il comportamento:

  • ignorare la corruzione: Registra i blocchi corrotti, ma consente di continuare la lettura.
  • riavvio_sulla_corruzione: riavvia in caso di rilevamento di danneggiamento (non compatibile con ignore_corruption e richiede il supporto dello spazio utente per evitare loop).
  • panico_sulla_corruzione: : provoca panico quando viene rilevata la corruzione (non compatibile con le versioni precedenti).
  • riavvio in caso di errore y panico_in_caso_di_errore: stesse reazioni ma per errori I/O.
  • ignora_zero_blocchi: non controlla i blocchi che sono previsti come zeri e restituisce zeri.
  • usa_fec_dal_dispositivo + fec_roots + blocchi fec + fec_start: Abilita Reed–Solomon (FEC) per recuperare i dati quando la verifica fallisce; le aree dati, hash e FEC non devono sovrapporsi e le dimensioni dei blocchi devono corrispondere.
  • controlla_al_più_una_volta: Controlla ogni blocco di dati solo la prima volta che viene letto (riduce il sovraccarico a scapito della sicurezza negli attacchi in tempo reale).
  • descrizione_chiave_hash_sig_root: Riferimento a una chiave nel portachiavi per convalidare una firma PKCS7 dell'hash radice durante la creazione della mappatura (richiede una configurazione del kernel appropriata e portachiavi attendibili).
  • prova_verifica_in_tasklet: Se gli hash sono memorizzati nella cache e le dimensioni I/O lo consentono, controlla la metà inferiore per ridurre la latenza; regolato con /sys/module/dm_verity/parameters/use_bh_bytes per classe I/O.

Firma, metadati e ancoraggio di fiducia

Affinché dm-verity sia affidabile, L'hash radice deve essere attendibile e solitamente firmatoNel sistema operativo Android classico, nella partizione di avvio è inclusa una chiave pubblica, verificata esternamente dal produttore; convalida la firma hash di root e garantisce che la partizione di sistema non sia stata alterata.

I metadati Verity aggiungono struttura e controllo della versione. Il blocco dei metadati include un numero magico 0xb001b001 (byte b0 01 b0 01), versione (attualmente 0), la firma della tabella in PKCS1.5 (in genere 256 byte per RSA-2048), la lunghezza della tabella, la tabella stessa e il padding con zeri fino a 32K.

Nelle implementazioni Android, la verifica si basa su fs_mgr e fstab: Aggiungendo un segno di spunta alla voce corrispondente e inserendo la chiave in /boot/verity_key. Se il numero magico non è dove dovrebbe essere, la verifica si interrompe per evitare di controllare la cosa sbagliata.

Operazione di avvio verificata

La protezione risiede nel kernel: Se compromesso prima dell'avvio del kernel, l'attaccante mantiene il controlloEcco perché i produttori solitamente convalidano rigorosamente ogni fase: una chiave inserita nel dispositivo verifica il primo bootloader, che a sua volta verifica il successivo, il bootloader dell'app e, infine, il kernel.

Con il kernel verificato, dm-verity è abilitato quando si monta il dispositivo a blocchi verificatoInvece di eseguire l'hashing dell'intero dispositivo (operazione che sarebbe lenta e sprecherebbe energia), questo viene verificato blocco per blocco durante l'accesso. Un errore causa un errore di I/O e servizi e app reagiscono in base alla loro tolleranza: continuano senza quei dati o si bloccano completamente.

Correzione degli errori in avanti (FEC)

Da Android 7.0, FEC (Reed–Solomon) è incorporato con tecniche di interlacciamento per ridurre lo spazio e aumentare la capacità di recuperare i blocchi danneggiati. Questo funziona in combinazione con dm-verity: se un controllo fallisce, il sottosistema può tentare di correggerlo prima di dichiararlo irrecuperabile.

Prestazioni e ottimizzazione

Per ridurre l'impatto: Abilita l'accelerazione SHA-2 tramite NEON su ARMv7 e le estensioni SHA-2 su ARMv8 dal kernel. Regola i parametri read-ahead e prefetch_cluster in base al tuo hardware; la verifica per blocco in genere aumenta poco il costo di I/O, ma queste impostazioni fanno la differenza.

Introduzione a Linux (systemd, veritysetup) e Android

Configurazione di dm-verity su Linux e Android

Su un Linux moderno con systemd, dm-verity consente una root di sola lettura verificata utilizzando veritysetup (parte di cryptsetup), systemd-veritysetup.generator e systemd-veritysetup@.service. Si consiglia di includere Secure Boot e un'immagine UKI firmata (unified kernel image), sebbene non siano strettamente obbligatori.

Preparazione e partizionamento consigliato

Parte di un sistema funzionale e adattato. Riserva un volume per l'albero hash (L'8-10% della dimensione di root è solitamente sufficiente) e valuta la possibilità di separare /home e /var se devi scrivere. Uno schema tipico include: ESP (per il bootloader), XBOOTLDR (per gli UKI), root (con o senza crittografia), partizione VERITY e, facoltativamente, /home e /var.

Come una radice, EROFS è un'alternativa molto interessante a ext4 o squashfs: È progettato per essere di sola lettura, con ottime prestazioni su flash/SSD, compressione lz4 di default ed è ampiamente utilizzato sui telefoni Android con dm-verity.

File che devono essere scrivibili

Con root ro, alcuni programmi si aspettano di scrivere su /etc o durante l'inizializzazioneÈ possibile spostarlo in /var/etc e creare un collegamento simbolico a qualsiasi elemento che debba essere modificato (ad esempio, le connessioni di NetworkManager in /etc/NetworkManager/system-connections). Si noti che systemd-journald richiede che /etc/machine-id esista nella directory radice (non un collegamento simbolico) per evitare di interrompere gli avvii precoci.

Per scoprire quali cambiamenti nell'esecuzione, usa dracut-overlayroot: sovrappone un tmpfs alla radice e tutto ciò che viene scritto appare in /run/overlayroot/u. Aggiungi il modulo a /usr/lib/dracut/modules.d/, includi overlayroot in dracut e imposta overlayroot=1 sulla riga del kernel; in questo modo vedrai cosa migrare in /var.

Esempi utili: pacman e NetworkManager

In Arch è conveniente Spostare il database Pacman in /usr/lib/pacman In modo che rootfs esegua sempre il mirroring dei pacchetti installati. Quindi, reindirizza la cache su /var/lib/pacman e crea il link. Per modificare la lista dei mirror senza toccare la root, spostala in /var/etc e crea comunque il link.

Con NetworkManager, sposta le connessioni di sistema in /var/etc/NetworkManager e il collegamento da /etc/NetworkManager/system-connections. In questo modo la root rimane immutabile e la configurazione rimane dove dovrebbe essere scrivibile.

Costruzione di verità e test

Da un live e con tutto perfetto e montato in ro, crea l'albero e roothash con formato veritysetup: Quando eseguito, stampa la riga Root Hash, che puoi salvare in roothash.txt. Eseguilo per testarlo con veritysetup, apri root-device root verity-device $(cat roothash.txt) e monta /dev/mapper/root.

Se preferisci, prima genera l'albero in un file (verity.bin) e poi scriverlo nella partizione VERITY. Il set risultante è: immagine root, albero di verity e hash di root che verrà bloccato all'avvio.

Configurare la riga del kernel

Aggiungi questi parametri: systemd.verity=1, roothash=contents_of_roothash.txt, systemd.verity_root_data=ROOT-PATH (ad esempio LABEL=OS) e systemd.verity_root_hash=VERITY-PATH (ad esempio LABEL=VERITY). Impostare systemd.verity_root_options su restart-on-corruption o panic-on-corruption per policy rigorose.

Altre opzioni consigliate: ro (se non usi EROFS/squashfs), rd.emergency=riavvio y rd.shell=0 (impedisce shell non autorizzate se l'avvio fallisce) e blocco = riservatezza per proteggere la memoria del kernel dall'accesso.

Partizioni aggiuntive con verity

Non solo la radice: È possibile definire altre mappature in /etc/veritytab e systemd-veritysetup@.service li assemblerà all'avvio. Ricorda: è più facile montare in modalità RW una partizione non root, e un utente root potrebbe disabilitare Verity su quelle partizioni, quindi il valore di sicurezza in quel caso è inferiore.

Sicurezza: Secure Boot, UKI e moduli firmati

dm-verity non è una soluzione miracolosa. Firma l'UKI e abilita Secure Boot con le tue chiavi per impedire a chiunque di sovrascrivere kernel/initramfs/cmdline (che include l'hash di root). Strumenti come sbupdate-git o sbctl aiutano a mantenere le immagini firmate e la catena di avvio intatta.

Se si abilita il blocco del kernel o la verifica della firma del modulo, I moduli DKMS o out-of-tree devono essere firmati Altrimenti non verranno caricati. Valuta la possibilità di utilizzare un kernel personalizzato con supporto alla firma per la tua pipeline (vedi moduli kernel firmati).

Crittografia, TPM e misurazione

dm-verity protegge l'integrità, non riservatezzaÈ possibile lasciare root non crittografato se non contiene segreti e la catena di avvio è protetta. Se si utilizzano file chiave di root per sbloccare altri volumi, è consigliabile crittografarli.

Con TPM 2.0, systemd-cryptenroll consente di associare le chiavi ai PCR 0,1,5,7 (firmware, opzioni, GPT, stato di avvio sicuro). Aggiungere rd.luks.options=LUKS_UUID=tpm2-device=auto e assicurarsi di includere il supporto TPM2 nell'initramfs. systemd-boot misura il kernel.efi in PCR4, utile per invalidare le chiavi se l'UKI o la sua riga di comando cambiano.

Aggiornamenti e modelli di distribuzione

Una radice di sola lettura verificata Non viene aggiornato con il gestore dei pacchetti nel modo tradizionaleL'ideale è costruire nuove immagini con strumenti come il progetto Yocto e pubblicarli. systemd dispone di systemd-sysupdate e systemd-repart per un download e un flashing delle immagini più efficaci.

Un'altra strategia è Schema A/B: Mantieni due root e due verity. Copia la root attiva in quella inattiva, applica le modifiche e ripristina la verity. Riattiva al prossimo avvio. Se stai usando UKI, ricorda di aggiornare l'hash della root nella riga di comando o di ricostruire l'UKI firmato.

Per la persistenza facoltativa, utilizzare OverlayFS sulla radice verificata con upper in tmpfs o disk. Puoi anche passare systemd.volatile=overlay per la persistenza temporanea. Flatpak semplifica l'installazione di app in /var e /home senza toccare /.

Esistono pacchetti automatizzati (ad esempio verity-squash-root in AUR) che creano una radice squashfs e firmare il roothash con kernel e initramfs, consentendo di scegliere tra modalità persistente o temporanea e di conservare l'ultimo rootfs come backup. Nota: l'aggiunta della persistenza a una root verificata ha casi d'uso limitati; provare a persistere i dati dell'app su partizioni separate.

Android: sistema come root, AVB e overlay del fornitore

Da Android 10, RootFS smette di essere eseguito sul disco RAM e si integra con system.img. (system-as-root). I dispositivi che si avviano con Android 10 utilizzano sempre questo schema e richiedono un ramdisk per dm-linear. BOARD_BUILD_SYSTEM_ROOT_IMAGE è impostato su false in questa build per distinguere tra l'utilizzo di un ramdisk e l'attivazione diretta di system.img.

Android 10 incorpora partizioni dinamiche e un init di prima fase che attiva la partizione logica di sistema; il kernel non la monta più direttamente. Le OTA solo di sistema richiedono un design system-as-root, obbligatorio sui dispositivi Android 10.

In nessun A/B, mantenere il ripristino separato dall'avvioA differenza di A/B, non esiste un backup boot_a/boot_b, quindi la rimozione del ripristino in modalità non A/B può comportare la perdita della modalità di ripristino se un aggiornamento di avvio non riesce.

Il kernel monta system.img su /converity tramite due percorsi: vboot 1.0 (patch per il kernel per analizzare i metadati Android in /system e derivare i parametri dm-verity; la riga di comando include root=/dev/dm-0, skip_initramfs e init=/init con dm=…) oppure vboot 2.0/AVB, dove il bootloader integra libavb, legge il descrittore hashtree (in vbmeta o system), costruisce i parametri e li passa al kernel nella riga di comando, con supporto FEC e flag come restart_on_corruption.

Con il sistema come root, non usare BOARD_ROOT_EXTRA_FOLDERS per le cartelle radice specifiche del dispositivo: queste scompariranno durante il flashing di un GSI. Definire mount specifici in /mnt/vendor/ , che fs_mgr crea automaticamente e farvi riferimento nel file fstab dell'albero dei dispositivi.

Android consente un overlay del fornitore da /product/vendor_overlay/: init monterà in /vendor le sottodirectory che soddisfano i requisiti del contesto SELinux e l'esistenza di /vendor/ Richiede CONFIG_OVERLAY_FS=yy, sui kernel più vecchi, la patch override_creds=off.

Implementazione tipica: installa i file precompilati nel dispositivo/ / /sovrapposizione_fornitore/, aggiungili a PRODUCT_COPY_FILES con find-copy-subdir-files in $(TARGET_COPY_OUT_PRODUCT)/vendor_overlay, definisci i contesti in file_contexts per etc e app (ad esempio vendor_configs_file e vendor_app_file) e consenti il ​​mounton su tali contesti in init.te. Testa con atest vfs_mgr_vendor_overlay_test in userdebug.

Risoluzione dei problemi: messaggio di danneggiamento dm-verity su Android

Sui dispositivi con slot A/B, cambiare slot o Flashing di vbmeta/boot senza coerenza roothash Questo potrebbe attivare l'avviso: dm-verity corruption, il dispositivo non è attendibile. Comandi come fastboot flash –disable-verity –disable-verification vbmeta vbmeta.img disabilitano la verifica, ma lasciano il sistema senza alcuna garanzia di integrità.

Alcuni bootloader supportano fastboot oem disable_dm_verity e il suo opposto, enable_dm_verity. Funziona su alcuni modelli, ma non su altri; e potrebbe richiedere kernel/magisk con flag modificati. Usatelo a vostro rischio e pericolo: la linea d'azione prudente è allinea boot, vbmeta e sistema, firmare o rigenerare l'albero e assicurarsi che l'hash radice previsto corrisponda a quello configurato.

Se dopo l'avviso si riesce a continuare a premere il tasto di accensione, il sistema si avvia, ma non hai più una catena di fiducia intattaPer rimuovere il messaggio senza compromettere la sicurezza, ripristinare le immagini firmate originali o ricostruire/verificare vbmeta con l'hashtree corretto, anziché disabilitare verity.

Piattaforme i.MX e OpenWrt

Su i.MX6 (ad esempio sabresd), configurare il kernel con supporto DM_VERITY e FEC, generare l'albero con veritysetup, memorizzare l'hash di root in modo sicuro e passare i parametri appropriati nella riga di comando o integrare tramite initramfs con systemd-veritysetup. Se non si utilizza dm-crypt, non è necessario CAAM per verity; l'attenzione è rivolta all'integrità.

In OpenWrt e in sistemi Linux embedded con OpenEmbedded, Ci sono sforzi per integrare dm-verity e SELinux (I lavori di Bootlin sono stati rivisti con l'intenzione di incorporare il supporto). È una scelta naturale: i router e le apparecchiature di rete traggono vantaggio da una root immutabile, verificata e protetta da MAC.

Costruzione manuale di alberi e metadati (vista dettagliata)

cryptsetup può generare l'albero per te, ma se preferisci capire il formato, la definizione della riga della tabella compatta include: nome di mappatura, dispositivo dati, dimensioni del blocco dati e dell'hash, dimensione dell'immagine in blocchi, posizione hash_start (immagine del blocco + 8 se concatenato), hash radice e sale. Dopo aver generato i livelli concatenati (dall'alto verso il basso, escluso il livello 0), si scrive l'albero su disco.

Per imballare tutto, comporre la tabella dm-verity, firmarla (tipicamente RSA-2048) e raggruppare signature+table nei metadati con un'intestazione versionata e un numero magico. Quindi, concatena l'immagine di sistema, i metadati di verity e l'albero hash. In fstab, contrassegna fs_mgr come verify e inserisce la chiave pubblica in /boot/verity_key per convalidare la firma.

Ottimizza con Accelerazioni SHA-2 per la tua CPU e regolare read-ahead/prefetch_cluster. Su hardware ARM, NEON SHA-2 (ARMv7) e le estensioni SHA-2 (ARMv8) riducono significativamente il sovraccarico di verifica.

In ogni distribuzione, ricorda che il valore hash della radice deve essere protetto: sia che venga compilato in un file UKI firmato, nella partizione di avvio firmata o convalidato dal bootloader tramite AVB. Tutto ciò che segue eredita tale trust.

Con tutto quanto sopra in atto, dm-verity diventa una solida base per sistemi immutabili, mobili e incorporati, supportando aggiornamenti transazionali, sovrapposizioni di configurazione e un moderno modello di sicurezza che riduce la superficie di attacco e previene la persistenza senza sacrificare le prestazioni.

Cos'è il progetto Yocto?
Articolo correlato:
Che cos'è il progetto Yocto: una guida completa incorporata

Aggiungi come fonte preferita in Google