Совсем недавно с увеличением количества дисков в полках, столкнулся с проблемой расшифровки дисков с использованием более чем известного метода через /etc/crypttab. Лично я выделяю несколько проблем использования этого метода, а именно то, что файл читается только после загрузки (mount) root-раздела, что негативно сказывается на импорте ZFS, в частности если они были собраны из разделов на *_crypt устройстве, или же mdadm рейды, собранные так же из разделов. Мы же все знаем, что можно использовать parted на LUKS контейнерах? И также проблема раннего старта других служб, когда массивов еще нет, а использовать уже что-то надо (я работаю с кластеризованным Proxmox VE 5.x и ZFS over iSCSI).
И второй вопрос, который я рассмотрю, это чем производить расшифровку (это ключевой момент статьи). И об этом мы поговорим ниже, заходите под кат!
Чаще всего на просторах интернета используют ключевой файл (само-собой добавленный перед этим в слот командой — cryptsetup luksAddKey), либо в редких исключениях (в русско-язычном интернете очень скудная информация) — скрипт decrypt_derived, лежащий в /lib/cryptsetup/script/ (конечно, есть еще способы, но я использовал именно эти два, что и легло в основу статьи). Так же я стремился к полному автономному включению после перезагрузок, без каких либо дополнительных команд в консоли, чтобы у меня всё «взлетало» сразу. Поэтому, зачем ждать? —
Приступаем!
Предполагаем систему, например Debian, установленную на crypto-раздел sda3_crypt и десяток дисков, готовых к шифрованию и созданию чего душе угодно. Мы имеем ключевую фразу (passphrase) для разблокировки sda3_crypt и именно с этого раздела мы будем на запущенной (расшифрованной) системе снимать «хэш» от пароля и добавлять на остальные диски. Всё элементарно, в консоли выполняем:
/lib/cryptsetup/scripts/decrypt_derived sda3_crypt | cryptsetup luksFormat /dev/sdX
где X — это наши диски, разделы и т.д.
После шифрования дисков «хешем» от нашей ключевой-фразы, необходимо узнать UUID, либо ID — смотря кто и к чему привык. Берём данные из /dev/disk/by-uuid и by-id соответственно.
Следующий этап подготовка файлов и мини-скриптов для работы необходимых нам функций, приступаем:
cp -p /usr/share/initramfs-tools/hooks/cryptroot /etc/initramfs-tools/hooks/
cp -p /usr/share/initramfs-tools/scripts/local-top/cryptroot /etc/initramfs-tools/scripts/local-top/
далее
touch /etc/initramfs-tools/hooks/decrypt && chmod +x /etc/initramfs-tools/hooks/decrypt
#!/bin/sh
cp -p /lib/cryptsetup/scripts/decrypt_derived "$DESTDIR/bin/decrypt_derived"
далее
touch /etc/initramfs-tools/hooks/partcopy && chmod +x /etc/initramfs-tools/hooks/partcopy
#!/bin/sh
cp -p /sbin/partprobe "$DESTDIR/bin/partprobe"
cp -p /lib/x86_64-linux-gnu/libparted.so.2 "$DESTDIR/lib/x86_64-linux-gnu/libparted.so.2"
cp -p /lib/x86_64-linux-gnu/libreadline.so.7 "$DESTDIR/lib/x86_64-linux-gnu/libreadline.so.7"
еще немного
touch /etc/initramfs-tools/scripts/local-bottom/partprobe && chmod +x /etc/initramfs-tools/scripts/local-bottom/partprobe
#!/bin/sh
$DESTDIR/bin/partprobe
и последнее, перед update-initramfs, нужно отредактировать файл /etc/initramfs-tools/scripts/local-top/cryptroot, начиная со строки ~360, кусок кода ниже
# decrease $count by 1, apparently last try was successful.
count=$(( $count - 1 ))
message "cryptsetup ($crypttarget): set up successfully"
break
и приводим к такому виду
# decrease $count by 1, apparently last try was successful.
count=$(( $count - 1 ))
/bin/decrypt_derived $crypttarget | cryptsetup luksOpen /dev/disk/by-uuid/ *CRYPT_MAP*
/bin/decrypt_derived $crypttarget | cryptsetup luksOpen /dev/disk/by-id/ *CRYPT_MAP*
message "cryptsetup ($crypttarget): set up successfully"
break
Обратите внимание, что здесь можно использовать либо UUID, либо ID. Главное чтобы нужные драйвера на устройства HDD/SSD были добавлены в /etc/initramfs-tools/modules. Узнать используемый драйвер можно командой udevadm info -a -n /dev/sdX | egrep 'looking|DRIVER'.
Теперь, когда мы закончили и все файлы на месте, запускаем update-initramfs -u -k all -v, в логировании не должно быть ошибок выполнения наших скриптов. Перезагружаемся, вводим ключевую-фразу и немного ждём, в зависимости от количества дисков. Далее система запустится и на конечной стадии запуска, а именно после «маунтинга» root-раздела, будет выполнена команда partprobe — она найдет и подцепит все созданные разделы на LUKS устройствах и любые массивы, будь то ZFS или mdadm соберутся без проблем! И всё это до загрузки основных служб и сервисов, которым нужны эти диски/массивы.
update1: Как заметил AEP, данный способ работает только для LUKS1.
Комментарии (19)
AEP
23.06.2019 21:41В статье используется скрипт
decrypt_derived
. Он вызываетdmsetup table --target crypt --showkeys
, но это работает только для LUKS1. С новым пакетом cryptsetup будет ошибка:
device $1 uses the kernel keyring
Обычный ключевой файл с правами 0400 надежнее.quality Автор
23.06.2019 22:29Спасибо за уточнение, но в продакшене используется на данный момент Debian 9.x, cryptsetup 2.x доступен только с версии Debian Buster (10) в репозитории. Как только соберусь обновляться, сначала тестовый стенд обкатаю, и всё проверю, но думаю найдется способ использовать этот метод и я дополню статью. Обычный ключевой файл можно использовать только с /etc/crypttab, так как лежать он будет в root-разделе, но тогда теряется смысл данной статьи. Да и удалить можно случайно!
AEP
23.06.2019 22:36При обновлении ничего не сломается, проблема проявляется только при создании новых томов LUKS с помощью новых версий cryptsetup. В качестве временного решения можно добавить опцию
--type luks1
кluksFormat
.
rantal
23.06.2019 23:42+2Я в последнее время делаю примерно так:
Подтягиваем пакеты и делаем шифрованный раздел — заготовку под LVM
apt install -y cryptsetup-luks dropbear-initramfs net-tools keyutils cryptsetup luksFormat /dev/sda3 cryptsetup luksOpen /dev/sda3 LVM
Подготавливаемся к удаленной разблокировке по SSH:
echo "IP=наш_ip-адрес::наш_шлюз:наша_маска::наше_имя_интерфейса:off" >> /etc/initramfs-tools/initramfs.conf sed -i "s/^#CRYPTSETUP=$/CRYPTSETUP=y/" /etc/cryptsetup-initramfs/conf-hook
Переносим в dropbear SSH-ключ нашего хоста, чтобы при подключении для разблокировки ssh не ругался на то, что ключ изменился:
/usr/lib/dropbear/dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key /etc/dropbear-initramfs/dropbear_rsa_host_key
Готовим dropbear для удаленной разблокировки пользователем с нашим ssh-ключем:
echo "no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command=\"/bin/cryptroot-unlock\" ssh-rsa НАШ_ПУБЛИЧНЫЙ_КЛЮЧ" >> /etc/dropbear-initramfs/authorized_keys
Подготавливаем crypttab для созданного шифрованного раздела:
blkid /dev/sda3 echo LVM UUID=НАШ_UUID_ИЗ_ВЫВОДА_ПРЕДЫДУЩЕЙ КОМАНДЫ crypt_disks luks,discard,initramfs,keyscript=decrypt_keyctl >> /etc/crypttab
Обновляем initramfs:
update-initramfs -u -k all
После этого хорошо бы проверить, что внутри нашего нового initramfs попала информация о нашем зашифрованном разделе. Я делаю примерно так:
unmkinitramfs /boot/НАШ_INITRAMFS /tmp/initramfs
и проверяю в debian 9 содержимое /tmp/initramfs/conf/conf.d/cryptroot, а в debian 10 — содержимое /tmp/initrams/cryptroot/crypttab
Далее — по накатанной дороге:
pvcreate /dev/mapper/LVM vgcreate vg0 /dev/mapper/LVM ... and so on ...
и работаем с LVM, как нам удобно. В частности, root я обычно кладу туда же (boot оставляю нешифрованным на /dev/sda1).
После всех этих операций получаем следующее:
при перезагрузке на стадии initramfs загрузка приостанавливается, и поднимается dropbear, к которому можно подключиться по ssh от имени пользователя root по ключу, публичную часть которого мы задали на стадии настройки. При подключения по ssh после аутентификации мы получаем предложение ввести passphase, которую мы задали при формировании зашифрованного раздела. Так же passphase можно ввести в обычной консоли с клавиатуры. После ввода корректной passphase производится разблокировка диска, соединение ssh сбрасывается, dropbear останавливается и продолжается нормальная загрузка (при этом зашифрованный диск уже доступен, lvm с него подцепляется автоматически).
Конечно, при желании, в crypttab можно запихнуть не один зашифрованный раздел, а несколько. Например, предположим, что у нас есть желание получить зашифрованный swap на разделе /dev/sda2:
cryptsetup luksFormat /dev/sda2 cryptsetup luksOpen /dev/sda2 swap echo swap /dev/sda3 crypt_disks luks,swap,discard,initramfs,keyscript=decrypt_keyctl >> /etc/crypttab update-initramfs -u -k all
Если при этом задать тот же passphase, что и для первого зашифрованного раздела, то при загрузке его потребуется ввести один раз, т.к. скрипт decrypt_keyctl в initramfs кеширует введенное значение и пытается применить его ко всем дискам, которые требуется расшифровать. Если поставить разные passphase, то придется по очереди ввести их все.
Prototik
24.06.2019 09:41Заметочка для тех, у кого ArchLinux — всё это уже есть из коробки, достаточно прописать нужные параметры ядра:
? cat /boot/cmdline # LUKS (Lol Ur Keys Sick) rd.luks.name=bffa9547-b6a5-43e8-a199-4f92bb225c91=fenix rd.luks.options=timeout=0 # Root fs (I heard kernel panics without it, I want happy kernel) root=/dev/mapper/fenix rootflags=subvol=@archlinux,x-systemd.device-timeout=0 # We love systemd, right? init=/usr/lib/systemd/systemd # But hate it for a little verbosity quiet loglevel=3 rd.systemd.show_status=auto rd.udev.log_priority=3
Если нужен ssh на initrd фазе — пакет mkinitcpio-dropbear или mkinitcpio-tinyssh.
Так же прошу заметить, что эти опции ядра для systemd initrd
HOOKS=(base systemd autodetect modconf block keyboard sd-vconsole sd-encrypt fsck filesystems)
, для busybox initrd название опций другое
kvazimoda24
Главная проблема, как ввести ключевую фразу на сервере? У сервера очень часто не подключены монитор с клавиатурой. Или админ находится не в серверной в момент перезагрузки. Отсюда вижу два варианта решения проблемы: либо запихивать в initramfs ssh сервер, либо отказаться от шифрования раздела root, при этом зашифровав всё остальное.
quality Автор
Это вовсе не проблема. Это отдельный момент, но решается очень просто: apt install dropbear. И добавить открытый ключ в authorized_keys дропбира, иначе зайти не получится. И grub настраиваем на поднятие интерфейса, говоря ядру ip= в cmd_linux. Но не забываем на «инкогнито» сетевые карты прописать драйвер в /etc/initramfs-tools/modules и пересобрать образ. В следующей статье я опишу более интересный способ, и dropbear там будет присутствовать.
AEP
Есть еще способ, о котором я планирую написать подробно позже (но пока отладил только на Arch Linux). Смысл: большинство BIOS'ов (но не все — например, с «Intel Corporation S1200SP/S1200SP, BIOS S1200SP.86B.03.01.0042.013020190050 01/30/2019» фокус не проходит) не чистит память при перезагрузке, так что там можно сохранить второй ключ. При этом первый ввод ключевой фразы осуществляется либо с клавиатуры, либо через ssh, а перед перезагрузкой сервер прочитает второй ключ с зашифрованного диска и сложит себе в память. BIOS его не почистит, а initramfs подхватит.
quality Автор
Интересный момент, согласен, но здесь присутствует уязвимость «холодной перезагрузки» (если не ошибаюсь), когда можно выкрасть ключ. Почистить память достаточно просто, надо включить Check Memory в BIOS. Да, загрузка будет немного дольше, в зависимости от объёма ОЗУ, но память будет чиста, и похитить ключ нельзя (но это конечно можно провернуть, только при физическом доступе к серверу).
YourChief
А от чего призваны защитить все эти манипуляции с шифрованием рут-диска? Можно обесточить сервер, подменить инитрамдрайв и выудить ключ при вводе таким образом.
quality Автор
Это всё теоретически и можно сделать, но увы, только при физическом доступе к серверу, что очень мало-вероятно и на практике невозможно.
YourChief
Так а от чего тогда шифрование? Если физического доступа нет, то никаких проблем нет. Я пытаюсь понять как выглядит случай, когда эти меры помогают.
rPman
Мое мнение, шифрование диска — это дешевый метод защиты от дешевой атаки с доступом к серверам (вытащить незашифрованные диски большого ума не надо), а шифрованные — необходимо подменить загрузчик.
С загрузчиком у пользователя есть варианты, например перенести его на свое устройство, т.е. сервер стартует, грузит загрузчик по сети, загрузчик проверяет как может железо на подмену (вплоть до бенчмарков cpu и перепрошивки в биос своих идентификаторов, в этом случае виртуализацией не спастись), и уже тут тоже есть где разгуляться.
На каждый защитный шаг есть своя атака но итоговый взлом поднимает его цену до небес… т.е. начинает работать принцип неуловимого джо.
YourChief
Про проверку железа — по описанию похоже на то, что делает TPM при сверке PCR, зависящих от платформы и запускаемого кода. Только там ещё можно сделать так, чтобы секрет не расшифровался, если не сходятся регистры, и эта проверка энфорсится внутри самого TPM. Тогда действительно получится прочная защита. А отредактировать рамдрайв много ума не нужно — это обычный CPIO-архив. Усилий потребуется не сильно больше или может даже меньше, чем для настройки всей этой конструкции.
У меня нет дискретного TPM (а софтварный, внутри ME, вызывает сомнения). Поэтому я пошёл по пути подписи всей цепи загрузки. Я использую UEFI Secure Boot с загрузчиком, подписанным моими сертификатами, ядром и рамдрайвом, подписанными моим ключом: github.com/Snawoot/linux-secureboot-kit
dartraiden
Собственно, если злоумышленник получит физический доступ к ПК, то он просто обнулит настройки прошивки, вытащив батарейку, войдёт в настройки и отключит SecureBoot. После чего, всё будет грузиться, невзирая на наличие или отсутствие подписей.
Если же используется шифрование и задействован один из регистров TPM (BIOS configuration), то отключение SecureBoot не позволит расшифровать накопитель, поскольку настройки изменились и «регистры не сойдутся».
Внешний TPM. конечно, хорош, но за неимением его и fTPM лучше, чем ничего, хотя, такие «софтверные» реализации в большей степени подвержены атакам.
YourChief
Да, так и есть, но конкретно в линуксе как раз вопрос стоит ребром: либо то, либо то, потому что TrustedGRUB2 (GRUB с поддержкой TPM) не поддерживает UEFI (а SecureBoot без него невозможен). Учитывая, что MBR boot это просто легаси, я не вижу сейчас вообще рабочих решений для линукса, которые могли бы задействовать TPM. Кстати, прямо сейчас я дописываю пост на эту тему, думаю к ночи опубликую.
dartraiden
В мире Linux поддержка TPM в плачевном состоянии, согласен (низкий поклон Мэтью Гаррету за его вклад в поддержку SecureBoot). Что говорить, значительная часть пользователей до сих пор верит байкам о том, что SecureBoot — изобретение клятого микрософта, чтобы затруднить продвижение Linux на десктопах.
То, что SecureBoot — это их единственная защита от вредоносных OROM-ов, они предпочитают не задумываться.