Installing Archlinux with full system and LVM encryption on LUKS

  • Tutorial
In this post you will read a little about my strange research during forced sick leave. We will talk about several things at once that are not “best practice”, but also possible! So, there will be a tutorial on how to install Archlinux (my favorite distr) so that:

  • without a separate / boot (just to / root)
  • / on lvm
  • lvm inside luks-container
  • with UEFI
  • in the virtual machine.
  • with secure boot (“hard”, hardly possible in a virtual machine)

It is noteworthy that everything will be encrypted, except for the EFI system partition with a single grubx64.efi file - an EFI application for running grub.

If you are interested, welcome under the cat!

At first, I configured this all on my Lenovo X240 laptop, then I already used a virtual machine with OVMF in Proxmox to write the article.

Test bench setup:

Everything is created fairly standard. The image uses my favorite arch, which can always be downloaded from Yandex .

Then a few moments on virtualke in Proxmox regarding UEFI. To test the booth with UEFI (otherwise it will not be so interesting), you need to set OVMF instead of SeaBIOS in the properties of the virtual machine:

Next, add the UEFI disk, respectively, so that you

can start something like this: In the console of the virtual machine, we immediately start the sshd service, set the root password and find out the dhcp-address of the virtual machine:

Next, we can continue working on ssh to make it more convenient.

Disk layout

So, having already connected via ssh, we first set the time so that later it would not turn out that file systems were created in the future:

timedatectl set-ntp true && timedatectl set-timezone Europe/Moscow

We check that everything is correct:

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 inlocal TZ: no

Now we can proceed to the layout of the disk. At this stage, I have a disk / dev / vda, because Virtio controller and it's just an empty disk without a partition table:

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

We will break it into 2 partitions:

  • fat32 drive for UEFI applications (EFI_system_partition)
  • LUKS container with everything else

Use gdisk to create a gpt:

root@archiso ~ # gdisk /dev/vda
GPT fdisk (gdisk) version 1.0.4
Command (? forhelp): o
This option deletes all partitions and creates a new protective MBR.
Proceed? (Y/N): y

Next, create the first partition for EFI with type EF00 (EFI System Partition):

Command (? forhelp): 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): <b>EF00</b>
Changed type of partition to 'EFI System'

Now we create a partition for LUKS, where we will not even bother with the type and leave it as it is:

Command (? forhelp): 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}:
<b>Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'</b>

Let's write the changes and finish with partitioning:

Command (? forhelp): w
Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/vda.
The operation has completed successfully.

Creating a LUKS container and file systems

With the first section (vda1) everything is quite simple. We just need to format it and for now it's all:

root@archiso ~ # mkfs.vfat /dev/vda1
mkfs.fat 4.1 (2017-01-24)

The second partition is the container that you first need to prepare. Format the partition using cryptsetup and set the passphrase:

root@archiso ~ # cryptsetup -v luksFormat /dev/vda2
This will overwrite data on /dev/vda2 irrevocably.
Are you sure? (Type uppercase yes): YES
Enter passphrase for /dev/vda2:
Verify passphrase:
Command successful.

*** I did not bother with the choice of ciphers, with a random mashing with uranium and other things, but simply created a default container.

Next, open the container by pointing to the same passphrase:

root@archiso ~ # cryptsetup luksOpen /dev/vda2 container
Enter passphrase for /dev/vda2:

Now we have an open container accessible through device mapper:

root@archiso ~ # ls -l /dev/mapper | grep container
lrwxrwxrwx 1 root root       7 Aug 14 14:01 container -> ../dm-0

Now we can continue with lvm (I will write in a quick way, since this is not a subject):

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

Next, create file systems on our 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

Now all this can be mounted to install the base system. The installation point will be / mnt, where the root of our future system will begin:

[root@archiso ~]# mount /dev/mapper/rootvg-root /mnt/
[root@archiso ~]# mkdir -p /mnt/{home,boot/efi}

*** I create / boot / efi so that / boot itself remains on / dev / mapper / rootvg-root, and the efi folder is already to mount / dev / vda1 (fat32 efi partition) into it:

[root@archiso ~]# mount /dev/vda1 /mnt/boot/efi/
[root@archiso ~]# mount /dev/mapper/rootvg-home /mnt/home/
[root@archiso ~]# swapon /dev/mapper/rootvg-swap

Check current mount points (always useful):

[root@archiso ~]# lsblk
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

As we see, everything is fair and now is the time to put the arch itself.

Base system installation

Install the base packages from the base and base-devel sets using the pacstrap package (they can put everything you want and besides this):

pacstrap /mnt base base-devel

Everything is perfectly loaded, the basic system is ready. I, naturally, removed a conclusion. Now we can configure this very system to load and work.

From the basic things we will immediately generate fstab:

genfstab -pU /mnt >> /mnt/etc/fstab

Next, we will make the arch-chroot in this new system:

[root@archiso ~]# arch-chroot /mnt

*** The arch-chroot is a very useful utility, because it does everything by itself. Although you can always use the standard chroot, before doing this, follow the instructions for the gentoo-handbook : AMD64 / Installation / Base section “Mounting the necessary

files systems ”. Set up the system time and hostname:

ln -s /usr/share/zoneinfo/Europe/Moscow /etc/localtime && \
hwclock --systohc && \
echo luks-test > /etc/hostname

Set the root password:

[root@archiso /]# passwd root
New password:
Retype new password:
passwd: password updated successfully

Uncomment the necessary locales in /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

Generate them:

[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

Immediately configure them for the system and the console:

[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

Now we will configure the /etc/mkinitcpio.conf file, which is responsible for options, hooks, etc., when generating the initramfs:

vi /etc/mkinitcpio.conf

The most important thing here is the hooks and their order:

HOOKS=(base udev autodetect modconf block keymap encrypt lvm2 resume filesystems keyboard fsck)

*** resume hook for booting system after hibernation from swap. On virtualk it is not needed. I copied it from beech.

Now we can generate the 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

Now that we have a system, we need to install the bootloader itself. I chose grub (2), because it is somehow more familiar and quite easy to load the kernel from an encrypted partition (well, or I didn’t particularly look for others).

Install the grub package:

[root@archiso /]# pacman -S grub dosfstools efibootmgr mtools

Before generating the config, edit the default grub options:

vim /etc/default/grub

here you need to uncomment one important line (without comment, of course):

# Uncomment to enable booting from LUKS encrypted devices

and add (there is empty by default) in GRUB_CMDLINE_LINUX:


UUID I took from 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"

We generate a config for 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.

Next, install grub itself on the disk:

[root@archiso /]# grub-install /dev/vda
Installing for x86_64-efi platform.
Installation finished. No error reported.

*** you can add --recheck --debug, specify the architecture ... but ... because it works by itself)

Now let's edit / etc / crypttab so that the system itself knows that it is necessary to decrypt the LUKS partition when loading. Add a line:

echo"container /dev/vda2 none" >> /etc/crypttab

Which means that you need to request a password (none) for the / dev / vda2 section and present it as container through device mapper.

Now we are ready to exit the chroot and reboot the system:

[root@archiso /]# exitexit
[root@archiso ~]# reboot
Welcome back!

Now let's turn to the virtual machine console to see the result:

At this stage, we run the EFI application /boot/efi/EFI/arch/grubx64.efi with / dev / vda1, which asks us for a password in order to decrypt our container.

Further, after entering the password:

Here is the usual grub window with our boot options from /boot/grub/grub.cfg.
At this stage, grub decrypted our container and accessed this file itself (/boot/grub/grub.cfg), the kernel, and initramfs. After selecting the default option, the kernel will boot, initramfs:

Active, the kernel and it came to the encrypt hook, which again asks us for the password to decrypt the container (in general, enter the password 2 times, but it may be that you are going to make 2 containers from excess paranoia boot and root :)

And further, after the system has been fully booted:

PS: to increase the level of schizophrenia, only a secure boot is missing in order to sign our grubx64.efi loader.

I just found the kernel and initramfs on / dev / vda1 to be uninteresting, since I did it 100 times. Other boot loaders such as SHIM, bootctl, etc. do not know how to do this (well, and I don’t know - tell in the comments)

Only registered users can participate in the survey. Sign in , please.

How do you spend your free time?

Also popular now: