Шифрование диска - это одна из тех тем, где разрыв между "поставил галочку при установке" и "понимаю что происходит" особенно опасен. Ноутбук с включённым LUKS, пароль к которому записан на стикере, приклеенном к крышке - не анекдот, а реальная история из корпоративной практики. Понять механизм значит не только настроить правильно, но и не совершить ошибок, которые превращают шифрование в ложное чувство защищённости.
dm-crypt - это подсистема ядра Linux, реализующая прозрачное шифрование блочных устройств. Слово "прозрачное" здесь техническое: операционная система и приложения работают с виртуальным блочным устройством через device mapper, совершенно не зная о том, что каждый сектор на реальном диске зашифрован. Шифрование происходит в ядре, в пространстве kernel space, с минимальным участием пользовательских процессов. LUKS - это стандарт хранения метаданных шифрования поверх dm-crypt. Именно LUKS отвечает за управление ключами, хранение параметров шифра и обеспечение возможности сменить пароль без перешифрования всего диска.
Как dm-crypt и LUKS работают вместе на уровне ядра
Архитектура dm-crypt строится на подсистеме Device Mapper - универсальном механизме ядра для создания виртуальных блочных устройств. Device Mapper принимает запросы на чтение и запись, перенаправляет их через цепочку "таргетов" и возвращает результат. dm-crypt - это один из таргетов, который перехватывает операции и применяет к данным криптографическое преобразование через Crypto API ядра.
Когда приложение записывает данные на /dev/mapper/encrypted, dm-crypt шифрует каждый сектор отдельно с уникальным вектором инициализации (IV), после чего передаёт зашифрованные данные на реальное блочное устройство. При чтении процесс обратный. С точки зрения файловой системы поверх dm-crypt ничего особенного не происходит - она видит обычное блочное устройство.
LUKS добавляет к этому заголовок в начале зашифрованного устройства. Заголовок хранит не сам ключ шифрования, а мастер-ключ, зашифрованный через один или несколько "слотов". Каждый слот может содержать отдельный пароль или ключевой файл. Именно поэтому LUKS поддерживает до 8 независимых паролей для одного устройства в LUKS1 и до 32 слотов в LUKS2 - менять пароль можно без перешифрования данных, поскольку меняется только зашифрованная копия мастер-ключа в слоте.
# Установка cryptsetup
apt install cryptsetup # Debian/Ubuntu
dnf install cryptsetup # Fedora/RHEL
# Посмотреть доступные шифры в ядре
cat /proc/crypto | grep -A2 "name"
cryptsetup benchmark
Создание LUKS-контейнера и выбор параметров шифрования
Выбор параметров при создании контейнера - это решение, которое невозможно изменить после без полного перешифрования. Здесь стоит потратить время на осознанный выбор, а не принять дефолты вслепую.
LUKS2 - текущий стандарт, рекомендованный для всех новых развёртываний. Он использует argon2id в качестве функции получения ключа из пароля (KDF) по умолчанию. argon2id намеренно требователен к памяти и времени, что делает перебор паролей значительно дороже для атакующего. LUKS1 использует PBKDF2 и остаётся актуальным только для совместимости с GRUB, который пока имеет ограниченную поддержку LUKS2.
Наиболее распространённая комбинация - AES-XTS с ключом 512 бит (256 бит для AES плюс 256 бит для XTS). Режим XTS спроектирован специально для шифрования дисков и устраняет слабости более старых режимов (CBC, ECB) в контексте блочного хранилища:
# Создать LUKS2-контейнер с явными параметрами
cryptsetup luksFormat --type luks2 \
--cipher aes-xts-plain64 \
--key-size 512 \
--hash sha256 \
--pbkdf argon2id \
--iter-time 3000 \
/dev/sdb
# Открыть контейнер и создать маппер
cryptsetup luksOpen /dev/sdb encrypted_disk
# Создать файловую систему на виртуальном устройстве
mkfs.ext4 /dev/mapper/encrypted_disk
# Смонтировать
mount /dev/mapper/encrypted_disk /mnt/secure
Параметр --iter-time задаёт время в миллисекундах, которое cryptsetup тратит на настройку KDF при создании слота. Больше времени - больше итераций argon2id - медленнее перебор паролей. На сервере с мощным CPU и редкими перезагрузками можно поставить 5000-10000 мс. На слабом одноплатном компьютере придётся снизить до 500-1000 мс.
Управление ключевыми слотами и смена паролей
Возможность иметь несколько независимых паролей для одного устройства - одна из важнейших практических возможностей LUKS. Системный диск сервера может иметь административный пароль и пароль для процедуры восстановления в отдельных слотах. Ноутбук разработчика - личный пароль и пароль для системного администратора компании.
# Просмотреть состояние заголовка и слотов
cryptsetup luksDump /dev/sdb
# Добавить второй пароль в новый слот
cryptsetup luksAddKey /dev/sdb
# Добавить ключевой файл как альтернативу паролю
cryptsetup luksAddKey /dev/sdb /path/to/keyfile.bin
# Изменить пароль в конкретном слоте
cryptsetup luksChangeKey /dev/sdb
# Удалить пароль из слота (нужен любой действующий пароль)
cryptsetup luksRemoveKey /dev/sdb
# Принудительно уничтожить конкретный слот по номеру
cryptsetup luksKillSlot /dev/sdb 1
Критически важная операция, о которой нередко забывают при первоначальной настройке - резервная копия заголовка LUKS. Заголовок хранится в начале зашифрованного устройства. Если несколько секторов в начале диска окажутся повреждены (плохие блоки, случайная запись, fsck применённый к зашифрованному устройству напрямую), данные станут навсегда недоступны даже при наличии верного пароля. Резервная копия заголовка - это страховка от таких сценариев:
# Создать резервную копию заголовка
cryptsetup luksHeaderBackup /dev/sdb \
--header-backup-file /secure/location/sdb-luks-header.img
# Восстановить заголовок из резервной копии
cryptsetup luksHeaderRestore /dev/sdb \
--header-backup-file /secure/location/sdb-luks-header.img
Резервную копию заголовка нужно хранить отдельно от самого зашифрованного диска. Если оба окажутся у атакующего, он получит полный доступ при знании пароля - никакой дополнительной защиты заголовок сам по себе не даёт.
Автомонтирование и интеграция с systemd через crypttab
Для системных дисков и томов, которые должны монтироваться автоматически, существует файл /etc/crypttab. Он играет для зашифрованных устройств ту же роль, что /etc/fstab для файловых систем - описывает, что и как нужно открыть при загрузке.
# Формат записи в /etc/crypttab:
# имя устройство ключ опции
# Открывать с запросом пароля при каждой загрузке
encrypted_disk /dev/sdb none luks
# Открывать автоматически с ключевым файлом
data_disk /dev/sdc /root/keys/data.key luks
# Открывать с UUID устройства (надёжнее имени)
secure_home UUID=a1b2c3d4-... none luks,timeout=30
После добавления записи в crypttab соответствующая запись в /etc/fstab монтирует устройство через /dev/mapper/имя:
# /etc/fstab
/dev/mapper/encrypted_disk /mnt/secure ext4 defaults 0 2
systemd генерирует юниты для каждой записи в crypttab автоматически. Управлять ими можно как обычными службами, что особенно удобно при диагностике проблем с монтированием на серверах без физического доступа.
Шифрование корневой файловой системы и роль initramfs
Полнодисковое шифрование с зашифрованным корневым разделом требует особой архитектуры загрузки. Ядро и initramfs должны оставаться незашифрованными на отдельном разделе /boot, потому что именно initramfs содержит инструменты для ввода пароля и открытия зашифрованного корня до передачи управления системе.
При установке с LUKS на корневой раздел дистрибутив автоматически настраивает initramfs с поддержкой cryptsetup. На Debian-based системах это делается через update-initramfs, на RHEL-based через dracut. После любых изменений в /etc/crypttab или параметрах шифрования нужно пересобрать initramfs:
# Debian/Ubuntu
update-initramfs -u -k all
# RHEL/Fedora
dracut --force
# Проверить что dm_crypt модуль включён в initramfs
lsinitramfs /boot/initrd.img-$(uname -r) | grep dm-crypt
Отдельного внимания заслуживает поддержка LUKS в GRUB. Загрузчик должен открыть зашифрованный раздел ещё до запуска ядра, если /boot находится внутри LUKS-контейнера. GRUB поддерживает LUKS1 без ограничений, поддержка LUKS2 появилась позже и требует использования PBKDF2 вместо argon2id - именно поэтому при шифровании раздела /boot рекомендуется создавать его в формате LUKS1.
Аппаратное ускорение AES и влияние на производительность
Производительность шифрования на уровне блочного устройства - вопрос, который волнует больше всего тех, кто ещё не пробовал. На практике современные процессоры с поддержкой AES-NI (Intel с 2010 года, AMD с 2011 года) выполняют шифрование AES со скоростью, практически равной скорости самого диска. На NVMe-накопителе с пропускной способностью 3 ГБ/с зашифрованное устройство покажет 2.8-3 ГБ/с - потери измеряются единицами процентов.
Убедиться в наличии AES-NI и проверить производительность шифрования:
# Проверить поддержку AES-NI процессором
grep -m1 aes /proc/cpuinfo
# Замерить производительность шифрования разными шифрами
cryptsetup benchmark
# Проверить что dm-crypt использует аппаратное ускорение
dmsetup table /dev/mapper/encrypted_disk
На системах без AES-NI (старые ARM-платы, некоторые встраиваемые системы) ситуация иная. Там накладные расходы шифрования могут достигать 30-50% от пропускной способности диска. В таких сценариях стоит рассмотреть ChaCha20-Poly1305 через capi:rfc7539(chacha20,poly1305) - он эффективнее на устройствах без аппаратного ускорения AES.
dm-crypt и LUKS - это зрелый, хорошо задокументированный стек шифрования, который прошёл проверку временем в производственных системах любого масштаба. Понимание архитектуры мастер-ключей и слотов, осознанный выбор параметров KDF, обязательное резервирование заголовка и правильная интеграция с initramfs превращают его из "галочки при установке" в реальный рубеж защиты данных.