Встановлення Debian на raid-масив із LUKS шифруванням, кореневою файловою системою ZFS та завантаженням з UBS із detached header

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

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

Нещодавно знову ностальгував у FreeBSD, все чудово, все звично, все зручно.
Лише один момент повністю виключає її з використання, принаймні в мене.
Майже на всіх моїх ноутбуках FreeBSD не підтримує ні режим сну, ні режим очікування (s2ram/s2disk).
І зробити я з цим нічого не зміг, а перепробував багато чого.

Без режиму очікування абсолютно неможливо користуватися ноутбуком, так як після транспортування необхідно по новому все завантажувати, включати, відкривати.
Та й перезавантажую робочі станції лише після оновлень, які цього вимагають.

З багатьох приємних дрібниць, які є у Фряхі, і які відсутні в 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|Hibernation), тому потрібен 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