Установка Archlinux c полным шифрованием системы и LVM на LUKS
В данном посте вы прочитаете немного о моих странных изыскания во время вынужденного отпуска по болезни. Речь пойдёт сразу о нескольких вещах, которые не являются «best practice», но так же тоже можно! Итак, здесь будет туториал о том, как установить Archlinux (мой любимый дистр) так, чтобы:
- без отдельного /boot (просто в /root)
- / на lvm
- lvm внутри luks-контейнера
- с UEFI
- в виртуальной машине.
- с secure boot («сложна», в виртуалке вряд ли получится)
Примечательно, что зашифровано будет всё, кроме EFI system partition с единственным файлом grubx64.efi — EFI-приложением для запуска grub.
Если заинтересовались, — добро пожаловать под кат!
Сначала я настроил это всё на моём ноутбуке Lenovo X240, потом для написания статьи пользовался уже виртуальной машиной с OVMF в Proxmox.
Настройка тестового стенда:
Создаётся всё достаточно стандартно. Образом используется мой любимый арч, который можно всегда загрузить с яндекса.
Далее несколько моментов по виртуалке в Proxmox относительно UEFI. Чтобы протестировать работу стенда с UEFI (иначе не будет так интересно), нужно в свойствах виртуальной машины выставить OVMF вместо SeaBIOS:
Далее соответственно добавить UEFI-диск, чтобы получилось примерно так:
Теперь можем стартовать виртуальную машину и начинать процесс установки. В консоли виртуальной машины сразу стартуем сервис sshd, задаём пароль root и узнаём dhcp-адрес виртуальной машины:
Далее мы можем продолжить работу по ssh чтобы было удобнее.
Разметка дисков
Итак, уже подключившись по ssh мы для начала устанавливаем время, чтобы потом не оказалось, что файловые системы созданы в будущем:
timedatectl set-ntp true && timedatectl set-timezone Europe/Moscow
Проверяем, что всё верно:
root@archiso ~ # timedatectl
Local time: Tue 2018-08-14 13:42:03 MSK
Universal time: Tue 2018-08-14 10:42:03 UTC
RTC time: Tue 2018-08-14 10:42:04
Time zone: Europe/Moscow (MSK, +0300)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
Теперь можем приступать к разметке диска. На данном этапе у меня есть диск /dev/vda, т.к. контроллер Virtio и это просто пустой диск без таблицы разделов:
root@archiso ~ # fdisk -l /dev/vda
Disk /dev/vda: 15 GiB, 16106127360 bytes, 31457280 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Разбивать его будем на 2 партиции:
- fat32 диск для UEFI приложений (EFI_system_partition)
- LUKS контейнер со всем остальным
Используем gdisk для создания GPT:
root@archiso ~ # gdisk /dev/vda
GPT fdisk (gdisk) version 1.0.4
Command (? for help): o
This option deletes all partitions and creates a new protective MBR.
Proceed? (Y/N): y
Далее создаём первую партицию для EFI с типом EF00 (EFI System Partition):
Command (? for help): n
Partition number (1-128, default 1):
First sector (34-31457246, default = 2048) or {+-}size{KMGTP}:
Last sector (2048-31457246, default = 31457246) or {+-}size{KMGTP}: +512M
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300): EF00
Changed type of partition to 'EFI System'
Теперь создаём партицию для LUKS, где даже не будем заморачиваться с типом и оставим как есть:
Command (? for help): n
Partition number (2-128, default 2):
First sector (34-31457246, default = 1050624) or {+-}size{KMGTP}:
Last sector (1050624-31457246, default = 31457246) or {+-}size{KMGTP}:
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'
Запишем изменения и закончим с разметкой партиций:
Command (? for help): w
Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!
Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/vda.
The operation has completed successfully.
Создание LUKS-контейнера и файловых систем
C первым разделом (vda1) всё достаточно просто. Нам нужно его просто отформатировать и пока на этом всё:
root@archiso ~ # mkfs.vfat /dev/vda1
mkfs.fat 4.1 (2017-01-24)
Вторая партиция это контейнер, который нужно сначала подготовить. Форматируем партицию через cryptsetup и задаём парольную фразу:
root@archiso ~ # cryptsetup -v luksFormat /dev/vda2
WARNING!
========
This will overwrite data on /dev/vda2 irrevocably.
Are you sure? (Type uppercase yes): YES
Enter passphrase for /dev/vda2:
Verify passphrase:
Command successful.
*** я не стал заморачиваться с выбором шифров, с затиранием рандомом урандомом и прочим, а просто создал контейнер по умолчанию.
Далее открываем контейнер указывая ту же парольную фразу:
root@archiso ~ # cryptsetup luksOpen /dev/vda2 container
Enter passphrase for /dev/vda2:
Теперь у нас есть открытый контейнер, доступной через device mapper:
root@archiso ~ # ls -l /dev/mapper | grep container
lrwxrwxrwx 1 root root 7 Aug 14 14:01 container -> ../dm-0
Теперь мы можем продолжить с lvm (напишу по-быстрому, так как это не сабж):
root@archiso ~ # pvcreate /dev/mapper/container
Physical volume "/dev/mapper/container" successfully created.
root@archiso ~ # vgcreate rootvg /dev/mapper/container
Volume group "rootvg" successfully created
root@archiso ~ # lvcreate -L1G -n swap rootvg
Logical volume "swap" created.
root@archiso ~ # lvcreate -L5G -n root rootvg
Logical volume "root" created.
root@archiso ~ # lvcreate -L2G -n home rootvg
Logical volume "home" created.
root@archiso ~ # lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
home rootvg -wi-a----- 2.00g
root rootvg -wi-a----- 5.00g
swap rootvg -wi-a----- 1.00g
Далее создадим файловые системы на наших lv:
root@archiso ~ # mkfs.ext4 -L root /dev/mapper/rootvg-root
mke2fs 1.44.3 (10-July-2018)
...
Writing superblocks and filesystem accounting information: done
[root@archiso ~]# mkfs.ext4 -L home /dev/mapper/rootvg-home
mke2fs 1.44.3 (10-July-2018)
Creating filesystem with 524288 4k blocks and 131072 inodes
...
Writing superblocks and filesystem accounting information: done
[root@archiso ~]# mkswap -L swap /dev/mapper/rootvg-swap
...
LABEL=swap, UUID=98b0bc31-1c62-4fec-bb97-e1913d1e8cb4
Теперь это всё можно примонтировать для установки базовой системы. Точкой установки будет /mnt, где будет начинаться корень нашей будущей системы:
[root@archiso ~]# mount /dev/mapper/rootvg-root /mnt/
[root@archiso ~]# mkdir -p /mnt/{home,boot/efi}
*** /boot/efi я создаю, чтобы сам /boot остался на /dev/mapper/rootvg-root, а папка efi уже для монтирования в неё /dev/vda1(fat32 efi partition):
[root@archiso ~]# mount /dev/vda1 /mnt/boot/efi/
[root@archiso ~]# mount /dev/mapper/rootvg-home /mnt/home/
[root@archiso ~]# swapon /dev/mapper/rootvg-swap
Проверим текуoие точки монтирования (всегда полезно):
[root@archiso ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
loop0 7:0 0 462.5M 1 loop /run/archiso/sfs/airootfs
sr0 11:0 1 573M 0 rom /run/archiso/bootmnt
vda 254:0 0 15G 0 disk
├─vda1 254:1 0 512M 0 part /mnt/boot/efi
└─vda2 254:2 0 14.5G 0 part
└─container 253:0 0 14.5G 0 crypt
├─rootvg-swap 253:1 0 1G 0 lvm [SWAP]
├─rootvg-root 253:2 0 5G 0 lvm /mnt
└─rootvg-home 253:3 0 2G 0 lvm /mnt/home
Как мы видим, всё честно и теперь время ставить сам арч.
Установка базовой системы
Устанавливаем базовые пакеты из наборов base и base-devel используя пакет pacstrap (им можно поставить всё, что вы хотите и кроме этого):
pacstrap /mnt base base-devel
Всё прекрасно загрузилось, базовая система готова. Вывод я, естественно, убрал. Теперь мы можем настроить эту самую систему, чтобы она загрузилась и работала.
Из базовых вещей сразу сгенерируем fstab:
genfstab -pU /mnt >> /mnt/etc/fstab
Далее сделаем arch-chroot в эту новую систему:
[root@archiso ~]# arch-chroot /mnt
*** arch-chroot очень даже годная утилита, потому как она делает всё сама. Хотя вы всегда можете воспользоваться стандартным chroot, перед этим выполнив всё по инструкции gentoo-handbook wiki.gentoo.org/wiki/Handbook: AMD64/Installation/Base раздел «Mounting the necessary filesystems»
Cразу настроим системное время и hostname:
ln -s /usr/share/zoneinfo/Europe/Moscow /etc/localtime && \
hwclock --systohc && \
echo luks-test > /etc/hostname
Зададим пароль root:
[root@archiso /]# passwd root
New password:
Retype new password:
passwd: password updated successfully
Раскомментируем нужные локали в /etc/locale.gen:
[root@archiso /]# vi /etc/locale.gen
[root@archiso /]# grep -v '^#' /etc/locale.gen
en_US ISO-8859-1
en_US.UTF-8 UTF-8
ru_RU.UTF-8 UTF-8
ru_RU ISO-8859-5
Сгенерируем их:
[root@archiso /]# locale-gen
Generating locales...
en_US.ISO-8859-1... done
en_US.UTF-8... done
ru_RU.UTF-8... done
ru_RU.ISO-8859-5... done
Generation complete
Сразу их настроим для системы и консоли:
[root@archiso /]# echo LANG=en_US.UTF-8 > /etc/locale.conf
[root@archiso /]# echo KEYMAP=ru > /etc/vconsole.conf
[root@archiso /]# echo FOND=cyr-sun16 >> /etc/vconsole.conf
Теперь настроим файл /etc/mkinitcpio.conf, который у нас отвечает за опции, хуки и прочее при генерации initramfs:
vi /etc/mkinitcpio.conf
Самое главное здесь хуки и их порядок:
HOOKS=(base udev autodetect modconf block keymap encrypt lvm2 resume filesystems keyboard fsck)
*** хук resume для загрузки системы после гибернации из swap. На виртуалке он не нужен. Скопировал его с бука.
Теперь мы можем сгенерировать initramfs:
[root@archiso /]# mkinitcpio -p linux
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: 'default'
-> -k /boot/vmlinuz-linux -c /etc/mkinitcpio.conf -g /boot/initramfs-linux.img
==> Starting build: 4.17.14-arch1-1-ARCH
-> Running build hook: [base]
-> Running build hook: [udev]
-> Running build hook: [autodetect]
-> Running build hook: [modconf]
-> Running build hook: [block]
-> Running build hook: [keymap]
-> Running build hook: [encrypt]
-> Running build hook: [lvm2]
-> Running build hook: [resume]
-> Running build hook: [filesystems]
-> Running build hook: [keyboard]
-> Running build hook: [fsck]
==> Generating module dependencies
==> Creating gzip-compressed initcpio image: /boot/initramfs-linux.img
==> Image generation successful
Теперь, когда у нас есть система, нам нужно установить сам загрузчик. Мой выбор пал на grub (2), потому как он как-то роднее и достаточно легко умеет загружать ядро с зашифрованного раздела (ну или я особо не искал другие).
Установим пакет grub:
[root@archiso /]# pacman -S grub dosfstools efibootmgr mtools
Перед генерацией конфига отредактируем дефолтные опции grub:
vim /etc/default/grub
здесь нужно раскомментить одну важную строчку (без коммента, естественно):
# Uncomment to enable booting from LUKS encrypted devices
GRUB_ENABLE_CRYPTODISK=y
и добавить (там пусто по умолчанию) в GRUB_CMDLINE_LINUX:
GRUB_CMDLINE_LINUX="cryptdevice=UUID=5ad7c9ad-fb17-4839-925e-479432516c07:container"
UUID я взял из blkid:
[root@archiso /]# blkid | grep vda2
/dev/vda2: UUID="5ad7c9ad-fb17-4839-925e-479432516c07" TYPE="crypto_LUKS" PARTLABEL="Linux filesystem" PARTUUID="667a1243-17ff-4f03-952c-5afd5e3415cc"
Генерируем конфиг для grub:
[root@archiso /]# grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
WARNING: Failed to connect to lvmetad. Falling back to device scanning.
Found linux image: /boot/vmlinuz-linux
Found initrd image: /boot/initramfs-linux.img
Found fallback initrd image(s) in /boot: initramfs-linux-fallback.img
WARNING: Failed to connect to lvmetad. Falling back to device scanning.
done
Далее устанавливаем сам grub на диск:
[root@archiso /]# grub-install /dev/vda
Installing for x86_64-efi platform.
...
Installation finished. No error reported.
*** можно добавить --recheck --debug, указать архитектуру… но… оно ведь само и так работает)
Теперь отредактируем /etc/crypttab, чтобы сама система знала, что при загрузке надо расшифровывать LUKS раздел. Добавим строчку:
echo "container /dev/vda2 none" >> /etc/crypttab
Которая означает, что надо запрашивать пароль (none) для раздела /dev/vda2 и представлять его уже как container через device mapper.
Теперь мы готовы выйти из chroot и перезагрузить систему:
[root@archiso /]# exit
exit
[root@archiso ~]# reboot
Welcome back!
Теперь обратимся к консоли виртуальной машины чтобы увидеть результат:
На данном этапе у нас запустилось EFI-приложение /boot/efi/EFI/arch/grubx64.efi с /dev/vda1, которое запрашивает у нас пароль, чтобы расшифровать наш контейнер.
Далее, после ввода пароля:
Здесь уже привычное окно grub с нашими опциями загрузки из /boot/grub/grub.cfg.
На данном этапе grub расшифровал наш контейнер и получил доступ к этом самому файлу (/boot/grub/grub.cfg), ядру и initramfs. После выбора опции по умолчанию загрузится ядро, initramfs:
Активно, ядро и дело дошло до хука encrypt, который заново спрашивает нас пароль для расшифровки контейнера (вообще влом 2 раза вводить пароль, но может быть так, что вы от излишка паранойи сделаете 2 контейнера для boot и root:)
И далее уже после полной загрузки системы:
PS: для повышения уровня шизофрении здесь не хватает только secure boot, чтобы подписать наш загрузчик grubx64.efi.
Просто положить ядро и initramfs на /dev/vda1 я счёл безыинтересным, так как 100 раз так уже делал. Другие загрузчики типа SHIM, bootctl и прочее не умеют вытворять подобного (ну и ли я не в курсе — расскажите в комментах)
Полезные материалы по теме и использованные материалы