- PVSM.RU - https://www.pvsm.ru -
Привет! 👋
Это моя первая статья здесь, да и вообще-то - первая, поэтому - судите строго. Или не строго. Или не судите. В общем, как хотите.
Пишу её в первую очередь для себя — чтобы систематизировать накопленный опыт. Ну а если кому-то окажется полезно — буду только рад. Если нет... что ж, тоже не расстроюсь.
Сегодня мы поговорим о dm-crypt в Linux — точнее дане не столько о нем, сколько о его использовании в немного необычном ключе: запуск полноценной ОС из зашифрованного контейнера, без выделения отдельного дискового раздела.
Всё, что вы здесь прочитаете — это мой личный опыт, без претензий на истину в последней инстанции. Я не собираюсь вступать в вечные полемики "LUKS против plain", "dm-crypt против VeraCrypt" и тому подобное. Мне просто было интересно попробовать кое-что нестандартное.
Почему?
Потому что могу.
И потому что — как говорится, "руки чесались".
Итак, начать, наверное, следует с того, что я хоть и являюсь довольно давним и убежденным пользователем Linux, отнести себя к профессионалам в области его администрирования и тонкой настройки я не могу. Скорее уже я энтузиаст, которому интересно ковыряться в системах, придумывать себе задачи и самому же их решать
Вот и тут было так же: я вдруг подумал — а что если...
...запустить полноценную Linux-систему не с отдельного раздела, а прямо из файла-образа?
Причём:
образ должен быть зашифрованным (dm-crypt plain),
система должна работать на реальном железе,
без костылей типа внешних загрузчиков,
ну и по возможности без слишком грязного initrd-напильника... хотя последнее вышло как всегда.
Честно?
Просто ради интереса.
Без паранойи, без шапочек из фольги. Чисто "а давай замутим" в лучших традициях заскучавшего технаря.
Плюс идея позволяла бы:
держать разные экспериментальные системы внутри обычных файлов,
использовать полноценное шифрование "на лету",
а при необходимости вообще не оставлять прямых следов на физическом уровне.
Вся магия будет происходить в давно и беззаветно полюбившемся мне дистрибутиве — Arch linux. В принципе, при небольших поправках на специфику то же самое с применимо и к Void linux (проверено лично), и, в общем‑то, к любому другому дистрибутиву, но в этом обзоре рассказ будет вестись именно с прицелом на арч.
В этой статье я использую dm-crypt plain, без LUKS и LUKS2.
Не потому что они плохие — просто потому что захотелось.
Так что если вдруг при чтении вас пробьёт на мысль "А зачем так извращаться?" — ответ прост:
"Потому что захотелось."
Для начала я создал зашифрованный образ нужного размера.
Ничего сложного — старый добрый dd:
dd if=/dev/urandom of=.local/share/newos.img bs=256M count=80 status=progress
Этот образ открываем через cryptsetup, используя внешний файл-ключ:
sudo cryptsetup open --type=plain --key-file=/mnt/snkey ~/.local/share/newos.img root
Форматируем свежесозданный контейнер:
sudo mkfs.ext4 -L root /dev/mapper/root
Дальше устанавливаем туда минимальный Arch Linux с рабочим окружением (у меня — MATE). Сам процесс установки ОС в наш образ в рамках данной статьи я обозревать не буду — он ни чем кардинально не отличается от установки на физический раздел и прекрасно расписан в арчвики и куче других, адаптированных для разного уровня подготовки пользователей, источниках. Но один нюанс осветить, наверное, для понимания стоит:
поскольку наш образ при таком подходе зашифрован, ядро ОС и образ initramfs должны находится где‑то «во вне», за пределами зашифрованного блока, дабы можно было их беспрепятственно запустить. В моем случае для этой цели был выбран esp‑раздел (EFI System Partition), который был примонтирован в качестве /boot раздела в расшифрованную систему. Таким образом, ядро и начальный рам нашей «контейнерной» ОС доступны для загрузки.
В базовой поставке Arch Linux и cryptsetup уже есть хук encrypt, который позволяет загружать систему с зашифрованного раздела.
Но есть проблема: он работает только с устройствами, а не с файлами внутри файловых систем.
Иными словами — он умеет расшифровывать /dev/sdX, но не умеет расшифровывать /mnt/somefile.img на разделе, который никуда не примонтирован.
А мне надо было именно это.
Чтож, не даром говорят: хочешь, чтобы что-то было сделано хорошо - сделай это сам.
Парсить новый параметр ядра src_rootfs.
Монтирова устройство, указанное в src_rootfs.
Находить образ-файл.
Подключать его как loop-устройство (/dev/loop0).
Добавляем новый параметр для ядра:
src_rootfs=<устройство>:/путь/до/образа.img
#!/usr/bin/ash
# Для упрощения жизни объявим функцию, которая будет парсить параметры cmdline ядра
# и распознавать переданное устройство по UUID, PARTUUID, LABEL, PARTLABEL, не и по
# привычному /dev/sdXY:
device_identify() {
local dev="$1"
case "$dev" in
UUID=*)
echo "/dev/disk/by-uuid/${dev#UUID=}"
;;
PARTUUID=*)
echo "/dev/disk/by-partuuid/${dev#PARTUUID=}"
;;
LABEL=*)
echo "/dev/disk/by-label/${dev#LABEL=}"
;;
PARTLABEL=*)
echo "/dev/disk/by-partlabel/${dev#PARTLABEL=}"
;;
/dev/*)
echo "$dev"
;;
# В случае ошибки выводим "страшное" сообщение:
*)
echo "[neoshy] ERROR: Unknown device or device format: $dev" >&2
exit 1
;;
esac
}
run_hook() {
echo "[neoshy] Starting hook"
# Вытаскиваем параметры ядра:
src_rootfs="$(getarg src_rootfs)"
if [ -z "$src_rootfs" ]; then
echo "[neoshy] ERROR: Missing required parameter: src_rootfs" >&2
exit 1
fi
# Получаем "сырое" значение девайса из параметра
src_dev="${src_rootfs%%:*}"
# Получаем путь до образа из этого же параметра:
src_img="${src_rootfs#*:}"
if [ -z "$src_dev" ] || [ -z "$src_img" ]; then
echo "[neoshy] ERROR: Invalid src_rootfs format" >&2
exit 1
fi
# Получаем реальный путь или идентификатор переданного устройства:
real_dev=$(device_identify "$src_dev")
# ...и монтируем его
echo "[neoshy] Mounting device: $real_dev"
mkdir -p /mnt
mount $real_dev /mnt
# На всякий случай проверяем наличие указанного образа:
if [ ! -f "/mnt$src_img" ]; then
echo "[neoshy] ERROR: Image file not found: /mnt$src_img" >&2
exit 1
fi
# ...и, если все ок, подключаем его, как loop-устройство
echo "[neoshy] Attaching loop device for: /mnt$src_img"
losetup --show --find "/mnt$src_img"
echo "[neoshy] Finishing hook"
}
#!/bin/bash
build() {
add_module "loop"
add_binary losetup
add_binary mount
add_runscript
}
help() {
cat <<HELPEOF
Minimal neoshy hook installer:
Mounts src_dev and maps src_img to /dev/loopX before starting the encrypt hook.
HELPEOF
}
Копируем файлы:
hook в /usr/lib/initcpio/hooks/neoshy
install в /usr/lib/initcpio/install/neoshy
Обязательно правим:
MODULES=(<ваши модули> dm_mod dm_crypt xts sha256 exfat)
HOOKS=(<ваши хуки> neoshy encrypt filesystems fsck)
Важно:
Наш хук должен идти до хука encrypt, т. к. encrypt будет использовать луп-девайс, который создает наш хук; и в массив MODULES нужно добавить модули dm_mod, dm_crypt, xts, sha256. По идее, они все должны подтягиваться автоматически хуком autodetect, на практике же у меня этого почему-то не произошло (подозреваю chtroot-окружение), поэтому их пришлось добавить вручную. Плюс сюда же нужно прописать модули для файловой системы, на которой лежит наш контейнер, если это не ext2,3,4., иначе раздел может не примонтироваться
sudo mkinitcpio -p linux
Или подставляйте ядро, которое реально используете (linux-lts, linux-zen и т.п.).
Я использую минималистичный подход:
EFI → ядро → initramfs → система, без всяких grub-ов и других прослоек.
Напомню, что ESP-раздел у меня примонтирован в /esp
ArchTestvmlinuz-linux src_rootfs=UUID=<ваш UUID>/itxst/.local/share/newos.img cryptdevice=/dev/loop0:newos_root cryptkey=PARTUUID=<ваш PARTUUID>:0:3214325 crypto=:::: root=/dev/mapper/newos_root rw initrd=ArchTestinitramfs-linux.img
Что здесь происходит:
src_rootfs — указываем устройство и путь до образа.
cryptdevice — подключаем /dev/loop0 как новое шифрованное устройство.
cryptkey — если нужен файл-ключ.
crypto=:::: — дефолтные параметры шифрования.
root — куда смонтировать расшифрованный контейнер.
Когда всё будет отлажено, можно добавить постоянную запись через efibootmgr:
sudo efibootmgr --create --disk /dev/nvme0n1 --part 1 --loader 'ArchTestvmlinuz-linux' --label "Arch Container Boot" --unicode 'src_rootfs=UUID=<ваш UUID>:/itxst/.local/share/newos.img cryptdevice=/dev/loop0:newos_root cryptkey=PARTUUID=<ваш PARTUUID> crypto=:::: root=/dev/mapper/newos_root rw initrd=ArchTestinitramfs-linux.img' --verbose
На выходе мы получаем:
зашифрованную файловую систему с установленной ОС в одном файле;
полноценную загрузку через EFI без промежуточных загрузчиков;
возможность быстро экспериментировать с разными системами без переразметки диска.
На этом, собственно, с настройкой покончено. Перезагружаемся в EFI-Shell, запускаем из esp-раздела наш скрипт и... вэлкам в изолированный от основной ФС контейнер с нашей свежей арч-установкой. Не знаю, будет ли подобное извращение кому-нибудь полезно, но лично мне в итоге оказалось удобно для тестирования и обкатки новомодных и не очень тенденций в линукс-мире перед их применением на основной системе. Настройка шифрования и загрузки с зашифрованного контейнера в данном случае - просто интерес и разминка для мозгов, но потенциально тоже может быть полезно в некоторых сценариях в наше странное время.
Пока писал статью, вспомнил про старую фичу Wubi (Windows-based Ubuntu Installer).
Там тоже Linux ставился в файл на диске с Windows.
В чём-то подход похож:
Нет отдельного раздела.
ОС живёт в образе.
По большому счету, у нас происходит то же самое, только не на ntfs-разделе (можно и замутить, но лень) и плюс от себя я добавил шифрование этого самого образа.
Если вдруг кому-нибудь станет совсем скучно и захочется поковыряться не занимаясь копипастой — есть готовый пакет в AUR:
paru -S mkinitcpio-hook-neoshy
Или можно забрать с GitHub:
git clone https://github.com/andr31ab/mkinitcpio-hook-neoshy.git
Спасибо за внимание!
Пробуйте, экспериментируйте, шифруйте!
И не забывайте делать бэкапы. 😉
Автор: AndreiAB
Источник [1]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/linux/418158
Ссылки в тексте:
[1] Источник: https://habr.com/ru/articles/905472/?utm_source=habrahabr&utm_medium=rss&utm_campaign=905472
Нажмите здесь для печати.