Установка Debian на raid-массив с LUKS шифрованием, корневой файловой системой ZFS и загрузкой с UBS и detached header

LUKS шифрование с отдельным header-файлом на USB 5504x3096 luks-encrypted-brick.jpg
LUKS шифрование с отдельным header-файлом на USB

Вступление издалека

Не так давно опять настальгировал во FreeBSD, всё прекрасно, всё привычно, всё удобно.
Один только момент полностью исключает её из использования на десктопе, по крайней мере у меня.
Почти на всех моих ноутбуках FreeBSD не поддерживает ни спящий, ни ждущий режимы (s2disk/s2ram).
И сделать я с этим ничего не смог, а уж перепробовал многое.

Без ждущего режима совершенно невозможно пользоваться ноутбуком, так как после транспортировки необходимо по новой всё загружать, включать, открывать.
Да и перезагружаю рабочие станции только после обновлений, этого требующих.

Из многих приятных мелочей, которые есть во Фряхе, и которые отсутствуют в Debian это ZFS Boot Environments, это несколько удобней чем, скажем, снапшоты LVM.
А второе это GEOM_ELI, который поддерживает не только, как LUKS, режим ИЛИ пароль, ИЛИ ключ, но так же поддерживает режим И пароль, И ключ.

Подумал, подумал, да и решил развернуть Debian с нуля с учётом всех инструментов, которые использую, опыта, и, что немаловажно, привычек.

Debian пока что остаётся основной моей системой, следовательно единственный способ получения хардварного (физического) ключа шифрования в дополнение к паролю - это сделать его из хедера и разместить на загрузочной USB-флешке.

Привычки


Мириться лучше со знакомым злом, чем бегством к незнакомому стремиться.

mdadm raid1

Это основной слой всех моих дисковых систем и это привычка от которой сложно избавиться.
Даже если в рабочей станции только один диск, то на нём в основе будет деградирующий raid1.
Хотя всегда в системах минимум два диска.
Это позволяет буквально за считанные минуты делать загружаемую и работоспособную копию системы и данных.

1
2
3
4
5
mdadm --grow   /dev/md1 --raid-devices=3
mdadm --manage /dev/md1 --add /dev/sdx1
# watch -n1 cat /proc/mdstat
mdadm --manage /dev/md1 --fail /dev/sdx1
mdadm --manage /dev/md1 --remove /dev/sdx1

Диск может быть подключён по любой шине, мне доводилось клонировать зеркало и по eSATA, и по USB2/3, и по EXPRESSCARD 54, и по Thunderbolt, и даже на loop-файл.

Развернутая на хорошем оборудовании и настроенная под себя система, которой ты можешь доверять ценится на равне с данными.
Потому что на написание небольшого проекта может понадобиться 2-3 месяца работы, а на настройку системы в процессе может уйти и пол года.

Сразу замечу, никакие dd, rsync, cpio и прочее не сможет корректно сделать бэкап работающей системы.

LUKS

Это второй уровень, сразу поверх Raid зеркала.
Его назначение объяснять не надо.

LVM

Иногда бывает нужен “спящий режим” (s2disk), поэтому нужен SWAP, а так как пароль хочется иметь один, то lvm необходим.

Да, mdadm, LVM и LUKS можно заменить одной ZFS, но это же привычки.
За добрых полтора десятка лет с ними не было ни одной проблемы.
Были и сбои питания / ресеты ноутбука, и множественные обновления релизов Debian, сыпались HDD, но система загружалась всегда.

Забегая вперёд

По итогу дисковая система будет выглядеть так:

1
2
3
4
5
6
7
8
9
10
11
12
13
# usb
sda                       8:16   1  14.9G  0 disk
├─sda1                    8:17   1  1000K  0 part
├─sda2                    8:18   1   512M  0 part   /boot/efi
└─sda3                    8:19   1     4G  0 part   /boot

# nvme
nvme0n1                 259:0    0   3.6T  0 disk
└─nvme0n1p1             259:1    0   904G  0 part
  └─md1                   9:1    0 903.9G  0 raid1
    └─md1_crypt         253:0    0 903.9G  0 crypt
      ├─lvm_system-swap 253:1    0    96G  0 lvm
      └─lvm_system-zfs  253:2    0 807.9G  0 lvm    zroot

Установка


Инсталлятор Debian не предоставляет возможности кастомной установки с учётом всех требований.
Поэтому решено было устанавливать используя старый добрый debootstrap из под LiveCD системы.
В моём случае это debian-live-13.3.0-amd64-kde.iso.
LiveCD-образ скачан, все хеши и gpg подписи проверены.

Debian LiveCD

sudo -i
apt install apt-transport-https
nano /etc/apt/sources.list

1
2
3
4
# trixie
deb https://deb.debian.org/debian/ trixie main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security/ trixie-security main contrib non-free non-free-firmware
deb https://deb.debian.org/debian/ trixie-updates main contrib non-free non-free-firmware
1
2
3
4
5
6
7
8
9
10
apt update
apt install cryptsetup mdadm \
            lvm2 \
            linux-headers-amd64 \
            debootstrap \
            gdisk \
            zfsutils-linux \
            openssh-server \
            linux-headers-6.12.63+deb13-amd64 \
            linux-headers-6.12.63+deb13-common

Если вы хотите продолжить установку по ssh:

1
2
3
4
# nano /etc/ssh/sshd_config
printf '%s\n' "PermitRootLogin yes" >> /etc/ssh/sshd_config
systemctl restart sshd.service
passwd

Перед установкой весь диск был перезаписан urandom, можете пропустить этот шаг.

1
dd if=/dev/urandom of=/dev/nvme0n1 bs=1M count=4096 oflag=sync status=progress

Разметка диска

1
2
3
4
5
6
7
8
parted -a optimal /dev/nvme0n1
mklabel gpt
unit MiB
p free
mkpart 'raid' 1 925697
set 1 raid on
align-check optimal 1
align-check minimal 1

mdadm

1
2
mdadm --verbose --create /dev/md1 --level=1 --raid-devices=2 missing /dev/nvme0n1p1
mdadm --detail --scan

luks2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
mkdir /tmp/keys
chmod 700 /tmp/keys
sync
dd if=/dev/random of=/tmp/keys/md1_header bs=1M count=16
sync
printf '3' > /proc/sys/vm/drop_caches
chmod 400 /tmp/keys/md1_header
# Wipe 1Gb and test /dev/md1
dd if=/dev/urandom of=/dev/md1 bs=1M count=1024 oflag=sync status=progress
# LUKS2 init
cryptsetup  --verbose \
            --cipher "aes-xts-plain64" \
            --key-size=512 \
            --hash=sha512 \
            --use-random \
            --iter-time=3000 \
            --type luks2 \
            --pbkdf argon2id \
            --pbkdf-memory 4194304 \
            --pbkdf-parallel 4 \
            --verify-passphrase \
            luksFormat /dev/md1 \
            --header /tmp/keys/md1_header \
            --align-payload=8192
# Check
cryptsetup luksDump /tmp/keys/md1_header
# Open
cryptsetup luksOpen /dev/md1 --allow-discards --header /tmp/keys/md1_header md1_crypt

LVM

1
2
3
4
5
6
7
8
9
10
11
12
pvcreate /dev/mapper/md1_crypt
pvdisplay -v
vgcreate lvm_system /dev/mapper/md1_crypt
vgdisplay
vgscan
# vgchange -a y
# swap partition
lvcreate -L98304    -n swap lvm_system
vgdisplay lvm_system
# zroot partition
lvcreate -l100%FREE -n zfs  lvm_system
vgdisplay lvm_system

ZFS

modprobe zfs

Если у вас отображается ошибка, то, вероятно, нет linux-headers.

1
2
3
4
5
6
7
8
modprobe: FATAL: Module zfs not found in directory /lib/modules/6.12.63+deb13-amd64

apt install linux-headers-amd64
# or
# apt install linux-headers-6.12.63+deb13-amd64 linux-headers-6.12.63+deb13-common
apt reinstall zfs-dkms

modprobe zfs

Создание файловых систем ZFS.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
zpool create  -o ashift=12 \
              -o autotrim=on \
              -O acltype=posixacl \
              -O xattr=sa \
              -O dnodesize=auto \
              -O compression=off \
              -O normalization=formD \
              -O utf8only=on \
              -O checksum=fletcher4 \
              -O dedup=off \
              -O atime=off \
              -O canmount=off \
              -O mountpoint=/ \
              -R /mnt \
              zroot /dev/lvm_system/zfs

zfs create  -o canmount=off     -o mountpoint=none  zroot/ROOT
zfs create  -o canmount=noauto  -o mountpoint=/     zroot/ROOT/trixie
zfs mount zroot/ROOT/trixie

zfs create  -o canmount=on            -o atime=off                                          zroot/data
zfs create                            -o atime=on                                           zroot/home
zfs create  -o mountpoint=/root       -o atime=on                                           zroot/home/root
zfs create  -o canmount=off                                                                 zroot/usr
zfs create  -o mountpoint=/usr/local                                                        zroot/usr/local
zfs create  -o mountpoint=/usr/src    -o exec=off     -o setuid=off     -o compression=lz4  zroot/usr/src
zfs create  -o canmount=off                                                                 zroot/var
zfs create  -o mountpoint=/var/log    -o exec=off                       -o compression=lz4  zroot/var/log
zfs create  -o mountpoint=/var/mail   -o atime=on     -o exec=off                           zroot/var/mail
1
2
3
4
chmod 700 /mnt/root
mkdir /mnt/run
mount -t tmpfs tmpfs /mnt/run
mkdir /mnt/run/lock

Разметка загрузочной USB флкшки

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Wipe USB
dd if=/dev/urandom of=/dev/sda bs=1M count=4096 oflag=sync status=progress

sgdisk -Z
sgdisk -o /dev/sda
# sgdisk --list-types
sgdisk  -a1 -n1:24K:+1000K  -t1:EF02    /dev/sda
sgdisk      -n2:1M:+512M    -t2:EF00    /dev/sda
sgdisk      -n3:514M:+4G    -t3:8300    /dev/sda
sgdisk      -c 1:boot                   /dev/sda
sgdisk  -A  1:set:2                     /dev/sda

# /boot - ext4
mkfs.ext4 /dev/sda3
mkdir     /mnt/boot
mount     /dev/sda3 /mnt/boot
mkdir     /mnt/boot/efi
# (/boot/efi)/EFI - fat32
mkdosfs -F 32 -s 1 -n EFI /dev/sda2
mount     /dev/sda2 /mnt/boot/efi

debootstrap и предварительная настройка

debootstrap trixie /mnt

1
2
3
4
5
6
7
8
mkdir /mnt/etc/zfs
cp /etc/zfs/zpool.cache /mnt/etc/zfs/
# У меня не было кеша
# cp: cannot stat '/etc/zfs/zpool.cache': No such file or directory

hostname deb
hostname > /mnt/etc/hostname
printf '127.0.1.1       deb' >> /mnt/etc/hosts

ip addr show
nano /mnt/etc/network/interfaces.d/eth0

1
2
auto eth0
iface eth0 inet dhcp

nano /mnt/etc/apt/sources.list

1
2
3
4
# trixie
deb http://deb.debian.org/debian/ trixie main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security/ trixie-security main contrib non-free non-free-firmware
deb http://deb.debian.org/debian/ trixie-updates main contrib non-free non-free-firmware

Монтируем виртуальные файловые системы.

1
2
3
4
5
# mkdir /mnt/dev /mnt/proc /mnt/sys
mount --make-private --rbind /dev  /mnt/dev
mount --make-private --rbind /proc /mnt/proc
mount --make-private --rbind /sys  /mnt/sys
mount -t devpts devpts /mnt/dev/pts

Копируем luks header

1
2
3
4
5
6
7
8
9
mkdir /mnt/etc/.crypto_data
cp /tmp/keys/md1_header /mnt/etc/.crypto_data/md1_header
chmod 700 /mnt/etc/.crypto_data
chmod 400 /mnt/etc/.crypto_data/md1_header

# Сделайте вторую временную копию md1_header в /boot на USB
# Если вы не сможете загрузиться с первого раза
# то вам не надо будет извлекать этот файл из образа initrd
cp /tmp/keys/md1_header /mnt/boot/md1_header

Чрутимся в установленную систему

chroot /mnt bash --login

Переводим sources на https.

apt update
apt install apt-transport-https ca-certificates
nano /etc/apt/sources.list

1
2
3
4
# trixie
deb https://deb.debian.org/debian/ trixie main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security/ trixie-security main contrib non-free non-free-firmware
deb https://deb.debian.org/debian/ trixie-updates main contrib non-free non-free-firmware

Устанавливаем и настраиваем пакеты:

1
2
3
4
5
6
7
8
9
apt update
apt install console-setup locales
dpkg-reconfigure locales tzdata keyboard-configuration console-setup

apt install linux-headers-amd64 linux-image-amd64
apt install cryptsetup mdadm lvm2 debootstrap gdisk zfsutils-linux openssh-server
apt install dpkg-dev
apt install zfs-initramfs
echo REMAKE_INITRD=yes > /etc/dkms/zfs.conf

Конфиги для mdadm и lvm

mdadm --detail --scan > /etc/mdadm/mdadm.conf
vgcfgbackup

Добавляем hook для initramfs

Этот скрипт добавит файл md1_header в initial ram disk (initrd).

nano /etc/initramfs-tools/hooks/crypto-header.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/sh

PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
# get pre-requisites
prereqs)
prereqs
exit 0
;;
esac

. /usr/share/initramfs-tools/hook-functions

copy_exec /etc/.crypto_data/md1_header /md1_header

chmod 750 /etc/initramfs-tools/hooks/crypto-header.sh

fstab, crypttab, modules

Для ZFS ничего в fstab указывать не надо.
nano /etc/fstab

1
2
3
UUID=b5bd933b-51e9-4693-bf3b-d13307bbd885     /boot           ext4    discard,noatime,nodiratime                      0 2
UUID=4813-4190                                /boot/efi       vfat    defaults                                        0 0
/dev/lvm_system/swap                          none            swap    sw                                              0 0

nano /etc/crypttab

1
2
# <target name>   <source device>   <key file>   <options>     # ,x-initrd.attach
md1_crypt /dev/md1 none luks,discard,header=/md1_header,initramfs

nano /etc/initramfs-tools/modules

1
2
3
4
5
6
md_mod
# raid1
zfs
spl
nls_cp437
nls_ascii

Корректировка точек монтирования ZFS

1
2
3
4
5
6
7
8
9
10
11
mkdir /etc/zfs/zfs-list.cache
touch /etc/zfs/zfs-list.cache/zroot
zed -F &
# Verify
cat /etc/zfs/zfs-list.cache/zroot
# Switch to foreground
fg
# Press Ctrl+C & remove '/mnt'
sed -Ei "s|/mnt/?|/|" /etc/zfs/zfs-list.cache/*
# Make snapshot
zfs snapshot zroot/ROOT/trixie@install

Настройка GRUB и initrd

apt install cryptsetup cryptsetup-initramfs
apt install systemd-timesyncd
apt install grub-pc
apt install dosfstools

Если увидите эту ошибку, то это нормально в случае с ZFS root.

1
2
cryptsetup: ERROR: Couldn't resolve device zroot/ROOT/trixie
cryptsetup: WARNING: Couldn't determine root device

update-initramfs -u -k all

nano /etc/default/grub

1
2
3
4
5
GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR=`( . /etc/os-release && echo ${NAME} )`
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX="root=ZFS=zroot/ROOT/trixie net.ifnames=0 biosdevname=0"

update-grub

legacy BIOS MBR

grub-install /dev/sda

UEFI

1
2
3
4
5
6
7
8
apt install grub-efi-amd64 shim-signed
update-grub
grub-install \
            --target=x86_64-efi \
            --efi-directory=/boot/efi \
            --bootloader-id=debian \
            --recheck \
            --no-floppy

Пользователи

1
2
3
4
5
6
7
8
# Root password
passwd root

# Regular user
groupadd --gid 1000 youruser
adduser --home /home/youruser --ingroup youruser --uid 1000 youruser
chmod -R ugo-xX,u=rwX,go-rwXx /home/youruser
usermod -aG cdrom,floppy,audio,dip,video,plugdev,netdev,scanner,bluetooth,lpadmin youruser

Exit & reboot

^D или Ctrl+D

Вы вышли из чрута устанавливаемой системы и снова в LiveCD. Размонтируйте все файловые системы ZFS.

1
2
3
4
mount | grep -v zfs | tac | awk '/\/mnt/ {print $3}' | xargs -i{} umount -lf {}
zfs umount -a
umount /mnt
zpool export -a

reboot, и, возможно, вы сможете загрузиться в вашу новую систему с загрузочной USB-флешки.

Немного детальней про GRUB UEFI

Загрузочная USB-флешка

blkid /dev/sda*

1
2
3
4
/dev/sdb:  PTUUID="70f483d1-859e-44de-9a93-ea46acdc886b" PTTYPE="gpt"/dev/sdb1: PARTLABEL="boot" PARTUUID="da022c89-87be-421a-bc48-cfd30136666e"
/dev/sdb1: PARTLABEL="boot" PARTUUID="110dda04-a5e1-4c56-832c-9573c82c59ee"
/dev/sdb2: LABEL_FATBOOT="EFI" LABEL="EFI" UUID="4813-4190" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="a88f91e5-2f12-411f-8d32-2ea944c231b6"
/dev/sdb3: UUID="b5bd933b-51e9-4693-bf3b-d13307bbd885" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="0bae36b2-e05a-42d9-9567-589b9499d114"

Содержание файловой системы vfat для /EFI

tree /boot/efi

1
2
3
4
5
6
7
8
9
10
11
12
13
/boot/efi
└── EFI
    ├── BOOT
    │   ├── BOOTX64.EFI
    │   ├── grubx64.efi
    │   └── mmx64.efi
    └── debian
        ├── BOOTX64.CSV
        ├── fbx64.efi
        ├── grub.cfg
        ├── grubx64.efi
        ├── mmx64.efi
        └── shimx64.efi

cat /boot/efi/EFI/debian/grub.cfg

1
2
3
search.fs_uuid b5bd933b-51e9-4693-bf3b-d13307bbd885 root hd1,gpt3 
set prefix=($root)'/grub'
configfile $prefix/grub.cfg

fs_uuid соответствует /dev/sdb3.

Если вы загрузились успешно


Можете удалить md1_header из /boot, потому что он и так содержится внутри всех /boot/initrd.img*.
rm -f /boot/md1_header

И сделать некоторое количество копий вашей загрузочной флешки.
Сами решайте, dd или копирование файлов, помните про grub-install.
Без этой флешки, а точнее без md1_header ваши данные превратятся в кирпич.

Если вы не смогли загрузиться


Загружаетесь опять в LiveCD.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
nano /etc/apt/sources.list
# trixie
deb http://deb.debian.org/debian/ trixie main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security/ trixie-security main contrib non-free non-free-firmware
deb http://deb.debian.org/debian/ trixie-updates main contrib non-free non-free-firmware
apt update
apt install cryptsetup mdadm lvm2 linux-headers-generic debootstrap gdisk openssh-server linux-headers-6.12.63+deb13-amd64 linux-headers-6.12.63+deb13-common
apt install linux-headers-6.12.63+deb13-amd64 linux-headers-6.12.63+deb13-common
apt install zfsutils-linux zfs-dkms

# nano /etc/ssh/sshd_config
# printf '%s\n' "PermitRootLogin yes" >> /etc/ssh/sshd_config
# systemctl restart sshd.service

mdadm --detail --scan
mdadm --stop /dev/md127
mdadm --assemble /dev/md1 /dev/nvme0n1p1

mkdir /tmp/usb
mount /dev/sda3 /tmp/usb
cp /tmp/usb/md1_header /tmp/md1_header
umount /dev/sda3

cryptsetup luksOpen /dev/md1 --allow-discards --header /tmp/md1_header md1_crypt

pvscan
vgchange -a y
modprobe zfs

zpool import -f zroot
# zpool import -d /dev/mapper/md1_crypt zroot

zfs set mountpoint=legacy zroot/ROOT/debian
mount -t zfs zroot/ROOT/debian /mnt
# or
# zfs mount zroot/ROOT/debian

mount --rbind /dev  /mnt/dev
mount --rbind /proc /mnt/proc
mount --rbind /sys  /mnt/sys
mount -t devpts devpts /mnt/dev/pts

mount /dev/sda3 /mnt/boot
mount /dev/sda2 /mnt/boot/efi

chroot /mnt bash --login

И проделайте необходимые шаги.

Debuging initrd.img


Что бы удостовериться, что /md1_header был скопирован hook-скриптом в initrd, можно использовать unmkinitramfs.

1
2
3
4
5
6
mkdir /tmp/debug
cp /boot/initrd.img-6.12.63+deb13-amd64 /tmp/debug/
cd /tmp/debug/
unmkinitramfs -v initrd.img-6.12.63+deb13-amd64 .
# header exists?
ls -la ./md1_header

Надеюсь, что я ничего не забыл. По мотивам Debian Trixie Root on ZFS.

Оригинальный пост на My WebArt Установка Debian на raid-массив с LUKS шифрованием, корневой файловой системой ZFS и загрузкой с UBS и detached header