Когда говорят о целостности данных на диске, обычно имеют в виду что-то одно: данные либо в порядке, либо нет. Но за этой простой формулировкой скрываются два принципиально разных вопроса. Первый: "Не изменились ли данные с момента, когда мы их записали?" Второй: "Не были ли они подменены кем-то намеренно?" Звучит похоже, но решения требуют совершенно разной архитектуры. Именно поэтому в Linux существуют два отдельных механизма устройственного отображения, dm-verity и dm-integrity, и путаница между ними стоит дорого, особенно когда речь заходит о безопасности продакшен-систем.
Оба инструмента живут в подсистеме device mapper ядра Linux. Оба работают на уровне блочного устройства, прозрачно для файловой системы, которая находится сверху. Но на этом сходство и заканчивается.
Как dm-verity выстраивает цепочку доверия от загрузчика до файла
dm-verity появился в ядре Linux версии 3.4 в 2012 году. Его история началась в Google: именно команда Chromium OS разработала этот механизм, чтобы гарантировать, что системный раздел устройства не изменился с момента сборки образа. Android использует dm-verity для защиты системного раздела начиная с версии 4.4, то есть сегодня этот механизм работает на миллиардах устройств по всему миру.
Идея проста и элегантна. При создании защищённого образа утилита veritysetup строит дерево хешей, где листовые узлы содержат хеши каждого блока данных, а промежуточные узлы хешируют своих потомков. Вершина дерева, корневой хеш, становится единственным значением, которое нужно защитить и проверить. Если хоть один байт в любом блоке данных будет изменён, корневой хеш окажется другим, и ядро немедленно вернёт ошибку ввода-вывода при попытке прочитать затронутый блок.
Создание защищённого раздела выглядит так:
veritysetup format /dev/sda1 /dev/sda2
Первый аргумент это раздел с данными, второй это раздел для хранения дерева хешей. Утилита выводит корневой хеш, который нужно сохранить в подписанном загрузчиком параметре ядра. Активация устройства с проверкой:
veritysetup open /dev/sda1 verified_root /dev/sda2 \
<root_hash>
После этого /dev/mapper/verified_root ведёт себя как обычное блочное устройство, но любое обращение к блоку, чей хеш не совпадает с ожидаемым, завершается ошибкой. Проверка происходит лениво, только при первом чтении блока в страничный кэш, что даёт минимальные накладные расходы. Для 10 гигабайт данных дерево хешей занимает около 81 мегабайта.
Критически важная деталь: dm-verity работает только с устройствами в режиме чтения. Записать что-либо на защищённый раздел невозможно физически. Это не ограничение реализации, а архитектурное решение. Если данные неизменны, их целостность можно гарантировать навсегда. Как только появляется запись, гарантия исчезает.
Что dm-verity не умеет и почему это честно
Честно говоря, dm-verity намеренно не решает ряд задач. Он не защищает от физического вскрытия устройства и замены чипа хранилища, если при этом обновить и дерево хешей. Он не обеспечивает конфиденциальность: данные на диске хранятся в открытом виде, и любой, кто получит физический доступ, сможет их прочитать. Он не защищает от атак во время работы системы, если злоумышленник уже получил права root.
Всё это не недостатки, а честные границы инструмента. dm-verity решает конкретную задачу: доказать, что загруженная система является именно той, которую собрали и подписали. Ни больше ни меньше. Проверить статус работающего устройства:
veritysetup status verified_root
Вывод покажет алгоритм хеширования, размер блока, корневой хеш и текущее состояние. Если хоть один блок вернул несовпадение хеша, это отразится в счётчике ошибок.
Зачем нужен dm-integrity, если уже есть dm-verity
dm-integrity отвечает на другой вопрос: что происходит с данными, которые система активно пишет и читает прямо сейчас? Тихая порча данных, silent data corruption, это явление, о котором мало говорят, но которое встречается куда чаще, чем принято думать. Контроллер диска может вернуть устаревшие данные после сбоя питания. Бит может перевернуться в оперативной памяти во время передачи. Прошивка накопителя может молча записать не туда.
dm-integrity хранит тег целостности для каждого сектора и проверяет его при каждом чтении. В отличие от dm-verity, это устройство поддерживает запись. Именно здесь начинается вся сложность.
Инициализация устройства с внутренним хешированием на базе crc32c:
integritysetup format /dev/sdb --integrity crc32c
integritysetup open /dev/sdb integrity_disk --integrity crc32c
Для криптографически стойкой защиты с использованием HMAC:
integritysetup format /dev/sdb \
--integrity hmac-sha256 \
--integrity-key-size 32 \
--integrity-key-file /etc/integrity.key
integritysetup open /dev/sdb integrity_disk \
--integrity hmac-sha256 \
--integrity-key-size 32 \
--integrity-key-file /etc/integrity.key
Проверить состояние активного устройства:
integritysetup status integrity_disk
Режимы журналирования и настоящая цена надёжности
Запись с контролем целостности сложнее, чем простая запись. Если данные записаны, а тег целостности ещё нет, и в этот момент пропадает питание, при следующей загрузке устройство обнаружит несовпадение и вернёт ошибку, хотя данные физически на месте. Это ложная тревога, которая может быть катастрофой для базы данных.
Чтобы решить эту проблему, dm-integrity предлагает три режима работы. В журнальном режиме, который является режимом по умолчанию, данные и теги сначала записываются в журнал, потом атомарно применяются. Это гарантирует согласованность, но каждая запись происходит дважды, что снижает пропускную способность записи примерно вдвое. В режиме битовой карты, доступном с ядра 5.2, ведётся карта регионов, для которых теги ещё не синхронизированы. После сбоя эти регионы пересчитываются. Быстрее, но без гарантии обнаружения порчи именно в момент сбоя. Режим без журнала даёт нативную скорость, но не является отказоустойчивым.
Активация в режиме битовой карты для SSD, где важна долговечность и скорость:
integritysetup open /dev/sdb integrity_disk \
--integrity crc32c \
--integrity-bitmap-mode
На практике журнальный режим снижает скорость последовательной записи примерно на 60 процентов по сравнению с устройством без dm-integrity. Режим битовой карты снижает примерно на 10 процентов. Для SSD битовая карта также вдвое уменьшает количество операций записи, что напрямую продлевает срок службы накопителя.
Связка dm-integrity и dm-crypt как аутентифицированное шифрование
По-настоящему интересная история начинается, когда dm-integrity работает не сам по себе, а в паре с dm-crypt. В этой конфигурации dm-crypt генерирует теги целостности на основе HMAC и передаёт их в dm-integrity через механизм bio_integrity_payload. Результат - аутентифицированное шифрование на уровне блочного устройства: данные зашифрованы, и любая попытка их подменить будет обнаружена немедленно.
LUKS2 поддерживает эту связку напрямую через cryptsetup. Создать том с аутентифицированным шифрованием:
cryptsetup luksFormat --type luks2 \
--cipher aes-gcm-random \
--integrity aead \
/dev/sdc
cryptsetup open /dev/sdc encrypted_integrity
Просмотреть параметры существующего тома:
cryptsetup luksDump /dev/sdc
Разница между этой связкой и простым dm-crypt принципиальна. Классическое шифрование защищает конфиденциальность, но не целостность: злоумышленник с физическим доступом может изменить зашифрованные блоки, и расшифровка вернёт мусор вместо ошибки. С dm-integrity любая модификация зашифрованных данных возвращает явную ошибку ввода-вывода, а не тихо испорченные данные.
Когда что выбирать и как они дополняют друг друга
Выбор между двумя инструментами определяется не тем, какой из них "лучше", а тем, какую угрозу нужно закрыть.
dm-verity подходит, когда раздел создаётся один раз и должен оставаться неизменным навсегда: системный раздел встраиваемого устройства, образ контейнера, read-only корневая файловая система сервера. Он встраивается в цепочку доверия от загрузчика через Secure Boot к подписанному ядру и подписанному образу. Любое отклонение от эталона обнаруживается до того, как системой успеют воспользоваться.
dm-integrity решает задачи там, где данные живут и изменяются: базы данных, файловые хранилища, любые системы, где тихая порча данных хуже явной ошибки. В самостоятельном режиме с crc32c он защищает от случайной физической порчи. В связке с dm-crypt и HMAC он даёт полноценную защиту от целенаправленной подмены зашифрованных данных.
Разумный подход для сервера с чувствительными данными: использовать оба инструмента в своих зонах ответственности. Системный раздел защищается через dm-verity с корневым хешем в подписанном образе ядра. Раздел данных защищается через LUKS2 с aead, что внутри означает именно связку dm-crypt и dm-integrity. Первый инструмент следит за тем, что система не тронута. Второй следит за тем, что данные не испорчены и не подменены в процессе работы.
Люди, впервые сталкивающиеся с этой темой, нередко спрашивают: зачем два инструмента, если можно сделать один, который умеет всё? Ответ прост: инструмент, который пытается делать всё сразу, обычно не делает ни одну вещь достаточно хорошо. dm-verity быстр именно потому, что никогда не пишет. dm-integrity гибок именно потому, что решает только задачу тегирования секторов, не занимаясь шифрованием и деревьями хешей одновременно. Эта специализация, а не универсальность, и делает их надёжными инструментами для продакшена.