Pull to refresh

Comments 19

Главная проблема, как ввести ключевую фразу на сервере? У сервера очень часто не подключены монитор с клавиатурой. Или админ находится не в серверной в момент перезагрузки. Отсюда вижу два варианта решения проблемы: либо запихивать в initramfs ssh сервер, либо отказаться от шифрования раздела root, при этом зашифровав всё остальное.

Это вовсе не проблема. Это отдельный момент, но решается очень просто: apt install dropbear. И добавить открытый ключ в authorized_keys дропбира, иначе зайти не получится. И grub настраиваем на поднятие интерфейса, говоря ядру ip= в cmd_linux. Но не забываем на «инкогнито» сетевые карты прописать драйвер в /etc/initramfs-tools/modules и пересобрать образ. В следующей статье я опишу более интересный способ, и dropbear там будет присутствовать.
Есть еще способ, о котором я планирую написать подробно позже (но пока отладил только на Arch Linux). Смысл: большинство BIOS'ов (но не все — например, с «Intel Corporation S1200SP/S1200SP, BIOS S1200SP.86B.03.01.0042.013020190050 01/30/2019» фокус не проходит) не чистит память при перезагрузке, так что там можно сохранить второй ключ. При этом первый ввод ключевой фразы осуществляется либо с клавиатуры, либо через ssh, а перед перезагрузкой сервер прочитает второй ключ с зашифрованного диска и сложит себе в память. BIOS его не почистит, а initramfs подхватит.
Интересный момент, согласен, но здесь присутствует уязвимость «холодной перезагрузки» (если не ошибаюсь), когда можно выкрасть ключ. Почистить память достаточно просто, надо включить Check Memory в BIOS. Да, загрузка будет немного дольше, в зависимости от объёма ОЗУ, но память будет чиста, и похитить ключ нельзя (но это конечно можно провернуть, только при физическом доступе к серверу).
А от чего призваны защитить все эти манипуляции с шифрованием рут-диска? Можно обесточить сервер, подменить инитрамдрайв и выудить ключ при вводе таким образом.
Это всё теоретически и можно сделать, но увы, только при физическом доступе к серверу, что очень мало-вероятно и на практике невозможно.
Так а от чего тогда шифрование? Если физического доступа нет, то никаких проблем нет. Я пытаюсь понять как выглядит случай, когда эти меры помогают.
Мое мнение, шифрование диска — это дешевый метод защиты от дешевой атаки с доступом к серверам (вытащить незашифрованные диски большого ума не надо), а шифрованные — необходимо подменить загрузчик.

С загрузчиком у пользователя есть варианты, например перенести его на свое устройство, т.е. сервер стартует, грузит загрузчик по сети, загрузчик проверяет как может железо на подмену (вплоть до бенчмарков cpu и перепрошивки в биос своих идентификаторов, в этом случае виртуализацией не спастись, кажется штатно в ядре можно включить запуск только подписанных бинарников, чтобы это взломать нужно модифицировать ядро, которое после этого нужно как то подписать уже ключами производителя материнки/мейнтейнера ос), и уже тут тоже есть где разгуляться.

На каждый защитный шаг есть своя атака но итоговый взлом поднимает его цену до небес… т.е. начинает работать принцип неуловимого джо.
Про проверку железа — по описанию похоже на то, что делает TPM при сверке PCR, зависящих от платформы и запускаемого кода. Только там ещё можно сделать так, чтобы секрет не расшифровался, если не сходятся регистры, и эта проверка энфорсится внутри самого TPM. Тогда действительно получится прочная защита. А отредактировать рамдрайв много ума не нужно — это обычный CPIO-архив. Усилий потребуется не сильно больше или может даже меньше, чем для настройки всей этой конструкции.

У меня нет дискретного TPM (а софтварный, внутри ME, вызывает сомнения). Поэтому я пошёл по пути подписи всей цепи загрузки. Я использую UEFI Secure Boot с загрузчиком, подписанным моими сертификатами, ядром и рамдрайвом, подписанными моим ключом: github.com/Snawoot/linux-secureboot-kit
У меня нет дискретного TPM (а софтварный, внутри ME, вызывает сомнения). Поэтому я пошёл по пути подписи всей цепи загрузки
Одно (шифрование с задействованием TPM) другому (SecureBoot) не мешает, они, скорее, дополняют друг друга.

Собственно, если злоумышленник получит физический доступ к ПК, то он просто обнулит настройки прошивки, вытащив батарейку, войдёт в настройки и отключит SecureBoot. После чего, всё будет грузиться, невзирая на наличие или отсутствие подписей.

Если же используется шифрование и задействован один из регистров TPM (BIOS configuration), то отключение SecureBoot не позволит расшифровать накопитель, поскольку настройки изменились и «регистры не сойдутся».

Внешний TPM. конечно, хорош, но за неимением его и fTPM лучше, чем ничего, хотя, такие «софтверные» реализации в большей степени подвержены атакам.
Да, так и есть, но конкретно в линуксе как раз вопрос стоит ребром: либо то, либо то, потому что TrustedGRUB2 (GRUB с поддержкой TPM) не поддерживает UEFI (а SecureBoot без него невозможен). Учитывая, что MBR boot это просто легаси, я не вижу сейчас вообще рабочих решений для линукса, которые могли бы задействовать TPM. Кстати, прямо сейчас я дописываю пост на эту тему, думаю к ночи опубликую.
В мире Linux поддержка TPM в плачевном состоянии, согласен (низкий поклон Мэтью Гаррету за его вклад в поддержку SecureBoot). Что говорить, значительная часть пользователей до сих пор верит байкам о том, что SecureBoot — изобретение клятого микрософта, чтобы затруднить продвижение Linux на десктопах.

То, что SecureBoot — это их единственная защита от вредоносных OROM-ов, они предпочитают не задумываться.
В статье используется скрипт decrypt_derived. Он вызывает dmsetup table --target crypt --showkeys, но это работает только для LUKS1. С новым пакетом cryptsetup будет ошибка:

device $1 uses the kernel keyring

Обычный ключевой файл с правами 0400 надежнее.
Спасибо за уточнение, но в продакшене используется на данный момент Debian 9.x, cryptsetup 2.x доступен только с версии Debian Buster (10) в репозитории. Как только соберусь обновляться, сначала тестовый стенд обкатаю, и всё проверю, но думаю найдется способ использовать этот метод и я дополню статью. Обычный ключевой файл можно использовать только с /etc/crypttab, так как лежать он будет в root-разделе, но тогда теряется смысл данной статьи. Да и удалить можно случайно!
При обновлении ничего не сломается, проблема проявляется только при создании новых томов LUKS с помощью новых версий cryptsetup. В качестве временного решения можно добавить опцию --type luks1 к luksFormat.
Я в последнее время делаю примерно так:

Подтягиваем пакеты и делаем шифрованный раздел — заготовку под 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, то придется по очереди ввести их все.

Вы используете /etc/crypttab, представьте, что будет, когда туда записать более 50 дисков? Идея была в расшифровке дисков до «маунта» root-раздела, а не после.

Ещё раз, диски расшифровывается ДО монтирования root-раздела на стадии работы initramfs.

Заметочка для тех, у кого 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 название опций другое

Sign up to leave a comment.

Articles