Содержание статьи
Шифрование дисков с помощью cryptsetup/LUKS
Борьба с пиратством набирает новые обороты, правообладатели и госорганы удваивают свои усилия в этом нелегком деле. Полагаю, каждый из нас подумывал о том, чтобы защитить личные файлы от посягательства со стороны «нежданных гостей», да и просто слишком любознательных лиц.
DANGER
Описываемые здесь средства способны необратимо уничтожить твои данные. Трижды проверяй введенные команды, прежде чем нажимать .
Введение
Шифрование будем производить стандартными средствами Ubuntu, причем ключ шифрования, как и раздел /boot, будет вынесен на сменный накопитель. Но зачем шифровать корневой раздел? Ведь можно зашифровать только /home? На то есть несколько причин. Первая — на основе конфигурационных файлов из /etc можно извлечь некоторую информацию, пускай даже она не относится к конфиденциальным данным. Вторая же — если вдруг содержимым диска заинтересуются, ты всегда сможешь сказать, что все так и было и что диск уже был забит псевдослучайными данными. Итак, что для этого понадобится?
- Флешка с MBR
- Ubuntu 12.10
- Чистый жесткий диск
В качестве алгоритма шифрования будем использовать AES, поскольку он принят как стандарт и криптостоек, а в качестве средства — cryptsetup/LUKS. Чтобы иметь возможность добавить свободное место поверх зашифрованного тома, задействуем логические тома (LVM).
Создание шифрованного тома
Загрузившись с LiveCD, необходимо подготовить флешку: создать на ней второй раздел, где будет размещен /boot и ключ шифрования. Создавая раздел /boot с ФС ext2 вторым, мы убиваем двух зайцев — первый раздел будет виден во всех системах, и на нем можно хранить данные, а второй из Windows так просто не увидишь, что прибавляет неудобства любопытствующим. Для разбиения я использовал gparted, но тебе никто не мешает использовать, например, fdisk. После этого нужно подмонтировать новосозданный раздел и сгенерировать ключевой файл:
# mkdir /mnt/boot /mnt/flash # mount /dev/sdf1 /mnt/flash # mount /dev/sdf2 /mnt/boot # dd if=/dev/random of=/mnt/boot/key.bin bs=32 count=1
Если длина ключевого файла в 32 байта твоей паранойе покажется недостаточной (моей показалось, хоть я и понимаю, что фактически пользовательским ключом шифруется мастер-ключ, который занимает те же самые 32 байта), то ты можешь использовать следующую команду:
# dd if=/dev/random of=/mnt/boot/key.bin bs=1 count=512
Для чего считывать по одному байту? Дело в том, что пул случайных чисел в ядре относительно маленький и не всегда содержит достаточное количество рандомных данных, поэтому во время генерации беспорядочно шевели мышью.
![Вывод команды mount](https://xakep.ru/wp-content/uploads/post/61210/mount_out_ubuntu-crypto.jpg)
![](https://xakep.ru/wp-content/uploads/2013/05/cover-3-215x280.jpg)
Хакер #172. Спецвыпуск о Raspberry Pi!
![Просмотр мастер-ключа](https://xakep.ru/wp-content/uploads/post/61210/dmsetup_show_masterkey.jpg)
Далее необходимо заполнить диск псевдослучайными данными. Перед выполнением следующей команды рекомендую трижды проверить, что диск, который ты заполняешь, именно тот, который тебе нужен.
# dd if=/dev/urandom of=/dev/sdd bs=8M
Заполнение необходимо, чтобы уменьшить вероятность успеха криптоанализа, если он будет производиться. Эта операция занимает довольно длительное время, поэтому запасись терпением. И только теперь уже можно запускать утилиту cryptsetup. Инициализируем диск в формате LUKS:
# cryptsetup -h=sha256 -c=aes-cbc-essiv:sha256 -s=256 luksFormat /dev/sdd /mnt/boot/key.bin
Разберем, что делает эта команда. Первый ключ указывает тип хеш-функции, которая будет использоваться для хеширования мастер-ключа. Второй ключ указывает алгоритм и тип шифрования. На этом я остановлюсь чуть подробнее. Что такое CBC? Как известно, AES — блочный шифр, который оперирует блоками по 128, 192 или 256 бит. Но, как правило, шифруют гораздо большие объемы информации. И возникает проблема — как шифровать, чтобы не было видно неслучайного распределения, из которого криптоаналитик может извлечь информацию. Умные люди решили ее таким образом: в первом блоке находится IV — случайный набор битов. И каждый последующий блок открытых данных XOR’ится с предыдущим блоком уже зашифрованных данных. Все вроде хорошо, но в случае шифрования дисков такая схема, по понятным причинам, неприменима (ты же не будешь ждать каждый раз по 10–20 минут, пока система расшифровывает нужный тебе участок?). В LUKS для решения проблемы произвольного доступа к информации применяется ESSIV — шифруются относительно небольшие блоки данных (посекторно), а вектор инициализации генерируется на основе номера сектора и хеша ключа. Такая схема также защищает от некоторых криптоатак. В частности, поэтому я и использую LUKS. Подтвердив свои намерения после запуска предыдущей команды, создаем отображение LUKS-устройства:
# cryptsetup -d=/mnt/boot/key.bin luksOpen /dev/sdd cryptodisk
Первый этап подготовки завершен — теперь в каталоге /dev/mapper появилось устройство cryptodisk, с которым можно обращаться, как с обычным диском.
![У LUKS хорошая документация по архитектуре](https://xakep.ru/wp-content/uploads/post/61210/luks_arch_doc.jpg)
Создаем LVM поверх шифрованного тома
В принципе, теперь можно на вновь созданный криптодиск ставить Ubuntu, но, как я уже писал, чтобы получить возможность увеличить место, поверх него лучше создать том LVM, что мы и сделаем. Проинициализируем физический том и создадим группу томов:
# pvcreate /dev/mapper/cryptodisk # vgcreate vg /dev/mapper/cryptodisk
Теперь в этой группе создадим логические тома:
# lvcreate -L1G -nswap vg # lvcreate -L6G -nroot vg # lvcreate -l 100%FREE -nhome vg
Теперь можно форматировать их в файловые системы. Ты волен выбирать сам, я же использовал как для корневого тома, так и для vg-home старую добрую ext4 — на мой взгляд, мы и так слишком накрутили, чтобы использовать более новые ФС:
# mkswap /dev/mapper/vg-swap # mkfs.ext4 /dev/mapper/vg-root # mkfs.ext4 /dev/mapper/vg-home
Подготовка почти завершена — теперь надо бы записать UUID дисков и разделов, для чего выполни следующую команду:
# ls -l /dev/disk/by-uuid/ >/mnt/flash/by-uuid.txt
Нам осталось «всего лишь» установить Ubuntu на нешифрованный раздел/диск и перенести ее на шифрованный.
Подготовка и перенос Ubuntu
Теперь ставим на нешифрованный диск Ubuntu — конфигурацию делай по своему вкусу за исключением размещения /boot и загрузчика. Их необходимо поместить на флешке, где ты заблаговременно создал соответствующий раздел. После этого загружаемся с флешки, чтобы проверить, все ли встало корректно, устанавливаем с помощью apt-get пакеты lvm2 и cryptsetup — они удалились автоматически после установки Ubuntu — и добавляем/изменяем строчки /etc/fstab. Должно получиться нечто подобное (за исключением строчек с псевдофайловыми системами, которых, впрочем, в современных системах нет):
/etc/fstab UUID=dd7ca139-074a-4b1b-a116-3a42feab7459 /boot ext2 defaults 0 2 /dev/mapper/vg-root / ext4 errors=remount-ro 0 1 /dev/mapper/vg-home /home ext4 defaults 0 1 /dev/mapper/vg-swap none swap sw 0 0
Раздел /boot мы монтируем по UUID для того, чтобы при добавлении новых дисков не возникла путаница с их именами. Теперь идем в файл /etc/crypttab, он у нас примерно следующего содержания:
cryptodisk UUID=c34e4c91-1fa1-4802-88ca-9c3be5c99097 /boot/key.bin luks,cipher=aes-cbc-essiv:sha256
Далее нужно изменить initrd. В файл /etc/initramfs-tools/modules добавляем строчку:
dm_crypt
Создадим скрипт для того, чтобы лишний раз не монтировать флешку:
/etc/initramfs-tools/hooks/cryptokeys ... . /usr/share/initramfs-tools/hook-functions
Создаем каталог в образе initramfs
mkdir ${DESTDIR}/etc/crypto
Копируем ключ и cryptsetup
cp /boot/key.bin ${DESTDIR}/etc/crypto copy_exec /sbin/cryptsetup /sbin
И собственно скрипт для подключения криптодиска (выполняется во время загрузки initrd):
/etc/initramfs-tools/scripts/local-top/cryptokeys ... modprobe -b dm_crypt while ! /sbin/cryptsetup -d=/etc/crypto/key.bin luksOpen /dev/disk/by-uuid/c34e4c91-1fa1-4802-88ca-9c3be5c99097 cryptodisk; do echo "Try again..." done
Цикл while необходим, если в дальнейшем ты добавишь разблокировку тома по паролю. Оба скрипта должны быть выполняемыми, иначе следующая команда их не увидит и будет создавать стандартный образ. Теперь можно давать команду обновления initrd:
# update initrd -u -k all -v
Мы чуть было не забыли про конфиг загрузчика. Имеется два пути его правки: один простой но неправильный — прямое редактирование файла /boot/grub/grub.cfg, второй тоже простой, но на сей раз верный. Некорректность первого способа в том, что при каждом обновлении ядра конфиг перезаписывается с использованием скриптов из /etc/grub.d/. Мы же пойдем другим путем — добавим скрипт, который как раз и будет генерировать правильные строчки в реальном грабовском конфиге. Однако есть одно «но» — при обновлении ядра тебе придется либо менять его каждый раз, либо оставаться на старом (последнее, на мой взгляд, предпочтительнее — см. врезку). Вот так примерно выглядят его строчки:
/etc/grub.d/40_custom menuentry "Ubuntu crypto" { recordfail=1 if [ -n ${have_grubenv} ]; then save_env recordfail; fi set quiet=1 insmod part_msdos insmod ext2 insmod gzio
UUID берем из заранее записанного файла
search --no-floppy --fs-uuid --set=root dd7ca139-074a-4b1b-a116-3a42feab7459
Раздел /boot для Grub считается корневым, поэтому пути к ядру и образу initrd указываются относительно его
linux /vmlinuz-3.5.0-17-generic root=/dev/mapper/vg-root ro initrd /initrd.img-3.5.0-17-generic }
После редактирования этого файла необходимо изменить строчку еще одного файла — /etc/default/grub с тем, чтобы при загрузке данный пункт меню выбирался по умолчанию. Для этого воспользуемся sed’ом.
# sed 's/GRUB_DEFAULT=0/GRUB_DEFAULT="Ubuntu crypto"/g' -i /etc/default/grub
По желанию можно выключить ненужные тебе пункты меню. Для этого просто сними право выполнения со всех ненужных скриптов в /etc/grub.d/. Теперь можно обновить основной конфиг:
# update-grub
И скопировать все файлы на шифрованные разделы — понятно, не на работающей системе. Загружаемся снова с LiveCD, подключаем криптотом, монтируем разделы и набираем следующие команды:
# cp -r --preserve=all /mnt/uncrypted/* /mnt/vg-root/ # cp -r --preserve=all /mnt/uncrypted/home/* /mnt/vg-home/
После копирования можешь пытаться загрузиться с флешки — только выбери пункт меню Ubuntu crypto. Если все прошло нормально, спустя какое-то время ты увидишь приглашение входа в систему. В таком случае могу тебя поздравить — ты уже работаешь в шифрованной системе.
![Разделы на флешке](https://xakep.ru/wp-content/uploads/post/61210/fdisk_flash.jpg)
Добавление/изменение ключей
Допустим, тебе понадобилось изменить ключ — ты его скомпрометировал или просто создал политику смены и хочешь ей неукоснительно следовать. Что для этого нужно? Прежде всего сделать резервную копию заголовка тома LUKS — если все будет нормально, после смены ключа ты ее сможешь уничтожить. Делаем мы ее, понятно, на нешифрованный раздел:
# mount /dev/sdf1 /mnt/flash # cryptsetup luksHeaderBackup /dev/disk/by-uuid/c34e4c91-1fa1-4802-88ca-9c3be5c99097 --header-backup-file /mnt/flash/luksHeader.bin
После этого сделаем копию файла ключа (просто скопируем его) и сгенерируем новый ключ:
# dd if=/dev/random of=/boot/new_key.bin bs=1 count=512
Смотрим текущие кейслоты (места в заголовке шифрованного тома, где хранятся ключи — да-да, их может быть больше одного) и запоминаем номер активного (Enabled). Как правило, это нулевой.
# cryptsetup luksDump /dev/disk/by-uuid/c34e4c91-1fa1-4802-88ca-9c3be5c99097
Добавляем новый ключ в систему:
# cryptsetup luksAddKey /dev/disk/by-uuid/c34e4c91-1fa1-4802-88ca-9c3be5c99097 /boot/new_key.bin --key-file /boot/key.bin
и заменяем старый файл ключа новым:
# mv /boot/new_key.bin /boot/key.bin # update-initramfs -u
Перезагружаемся, чтобы убедиться, что мы ничего не сломали, и удаляем старый ключ:
# cryptsetup luksKillSlot /dev/disk/by-uuid/c34e4c91-1fa1-4802-88ca-9c3be5c99097 0 --key-file /boot/key.bin
Снова перезагружаемся. Если и в этот раз все в порядке, с облегчением вздыхаем. Мы успешно сменили ключ.
Восстановление доступа к закриптованному тому
Предположим, ты потерял ключ. Что делать? Главным образом — не паниковать. Если у тебя есть резервная копия заголовка и ключ, который ты применял до ее создания, но ты не можешь загрузиться с него, то для восстановления доступа производим следующее. Грузимся с LiveCD и монтируем носитель с бэкапом. После этого восстанавливаем заголовок:
# cryptsetup luksHeaderRestore /dev/sdd --header-backup-file /mnt/flash/luksHeader.bin
Подключаем шифрованный том с использованием резервной копии старого ключа:
# cryptsetup -d=/mnt/flash/old_key.bin luksOpen /dev/sdd cryptodisk
И производим восстановительные работы (чрутимся, пробрасываем каталог с резервной копией, монтируем /boot, копируем резервную копию ключа в /boot/key.bin и обновляем initramfs). Перезагружаемся — все готово. Но что, если ты каким-то непостижимым образом затер все свои кейслоты и у тебя нет резервных копий заголовка? Если ты после этого еще и перезагрузился, могу тебя поздравить — твои данные невосстановимы. Если же ты не перезагрузился… что ж, есть шанс, что тебе удастся восстановить том. Для этого тебе прежде всего необходимо извлечь мастер-ключ, который хранится в памяти:
# dmsetup table --target crypt --showkey /dev/mapper/cryptodisk
Самая длинная непрерывная строчка и будет мастер-ключом. Ее нужно скопировать в файл на нешифрованный том и после этого преобразовать в бинарную форму (перед этим убедись, что в файле нет никаких символов конца строки):
# cat master-key.txt | xxd -r -p >master-key.bin
Размер файла master-key.bin в случае тех параметров создания криптотома, которые использовались в статье, должен составлять 32 байта. После извлечения мастер-ключа ты должен загрузиться с LiveCD и проинициализировать том заново с использованием данного мастер-ключа и твоего нового ключа шифрования — надеюсь, как его генерировать, описывать не надо:
# cryptsetup -h=sha256 -c=aes-cbc-essiv:sha256 -s=256 luksFormat /dev/sdd /mnt/boot/key.bin --master-key-file=/mnt/flash/master-key.bin
Производим процедуры по восстановлению, описанные чуть выше.
Сетевая разблокировка зашифрованного тома через SSH
Имеется способ разблокировки тома LUKS по сети через SSH. Кратко опишу, что для этого надо. 1. Добавить возможность разблокировки с использованием ввода пароля. 2. Установить пакет busybox и dropbear — чтобы можно было разблокировать удаленно. 3. Скачать скрипт, разместить его в /etc/initramfs-tools/hooks/dropbear и поставить ему право на выполнение. 4. Настроить сетевые параметры в скрипте. 5. Обновить initrd. Теперь в случае чего ты сможешь разблокировать том LUKS не только локально, но и по сети.
В заключение статьи хочу напомнить: для защиты информации нужно принимать комплексные меры, и шифрование лишь одна из них.
![Начало заголовка шифрованного тома](https://xakep.ru/wp-content/uploads/post/61210/hexdump_luksheader.jpg)
![Заголовок шифрованного тома в читабельном виде](https://xakep.ru/wp-content/uploads/post/61210/cryptsetup_luksDump.jpg)
Запрет обновления ядра
Для выключения обновления ядра создай файл в /etc/apt/preferences.d/ следующего содержания:
Package: linux-generic linux-headers-generic linux-image-generic linux-restricted-modules-generic Pin: version 3.5.0-17.28 Pin-Priority: 1001
Версия, естественно, может отличаться.
INFO
Помимо низкоуровневых средств шифрования, в Linux есть средства уровня файловой системы — такие как EncFS.
Роман Ярыженко, rommanio@yandex.ru