Установка Debian на raid-массив с LUKS шифрованием, корневой файловой системой ZFS и загрузкой с UBS и detached header
Вступление издалека
Не так давно опять настальгировал во 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.