Установка 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:

rugefhk4s2dhorfq7myeyu_yuam.png

Далее соответственно добавить UEFI-диск, чтобы получилось примерно так:

hpht6w6iadkra0ur4mon_iuvgeu.png

Теперь можем стартовать виртуальную машину и начинать процесс установки. В консоли виртуальной машины сразу стартуем сервис sshd, задаём пароль root и узнаём dhcp-адрес виртуальной машины:

zy1yetqzltmbhk9yb3mxirmfqby.png

Далее мы можем продолжить работу по 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!


Теперь обратимся к консоли виртуальной машины чтобы увидеть результат:

zxhdshkaqy4zc06h3_etdlrwfxu.png

На данном этапе у нас запустилось EFI-приложение /boot/efi/EFI/arch/grubx64.efi с /dev/vda1, которое запрашивает у нас пароль, чтобы расшифровать наш контейнер.

Далее, после ввода пароля:

g3o1px_jx-gved8okzl2ojt2m3y.png

Здесь уже привычное окно grub с нашими опциями загрузки из /boot/grub/grub.cfg.
На данном этапе grub расшифровал наш контейнер и получил доступ к этом самому файлу (/boot/grub/grub.cfg), ядру и initramfs. После выбора опции по умолчанию загрузится ядро, initramfs:

4olhggvohhh5t5wvk8ta0wzeoek.png

Активно, ядро и дело дошло до хука encrypt, который заново спрашивает нас пароль для расшифровки контейнера (вообще влом 2 раза вводить пароль, но может быть так, что вы от излишка паранойи сделаете 2 контейнера для boot и root:)

И далее уже после полной загрузки системы:

spbzdplbl60sycfknr2bjuc5wuk.png

PS: для повышения уровня шизофрении здесь не хватает только secure boot, чтобы подписать наш загрузчик grubx64.efi.

Просто положить ядро и initramfs на /dev/vda1 я счёл безыинтересным, так как 100 раз так уже делал. Другие загрузчики типа SHIM, bootctl и прочее не умеют вытворять подобного (ну и ли я не в курсе — расскажите в комментах)

Полезные материалы по теме и использованные материалы

© Habrahabr.ru