How to install Arch Linux with LUKS encrypted rootfs and boot with EFISTUB

7 minute read

In this tutorial, I’ll show you how to install Arch Linux but with an encrypted rootfs (/) and with a bootloader-less setup (direct UEFI boot)

Requirements:

  • Networking access
  • Machine MUST support UEFI (non-secured mode)
  • You have some knowledge about how things work (partitioning, UEFI, bootloader, …), you’re an Arch Linux user after all.

Note:

  • UEFI boot will save you some precious boot time… meanwhile it will be a pain to fix if something goes wrong and render your machine unbootable (bad kernel/initramfs).
  • If you’re using more than 1 OS, I’d recommend you to just stick with good-ol’ GRUB bootloader.
  • You must disable the secure boot mode of the UEFI because the kernel file is unsigned, you could sign it after the setup is done and then enable secure boot again.

1. Booting into Live Arch Linux

_config.yml

Nothing special, just create a bootable media from Arch Linux ISO and then boot it, if you see a different menu than this with a fancy Arch Linux logo (GRUB menu) then your machine isn’t booted by using UEFI or something goes wrong while you create the boot media, try again until you see such similar menu as above.

Select Arch Linux archiso x86_64 UEFI CD

_config.yml

Here you have the Arch Linux Live running on your PC, this is how the disk is configured on my PC, there’s only 1 20GB physical disk which is sda and it’s totally empty (no partition).

Starting off by partitioning the disk with

cfdisk /dev/sda

_config.yml

Chose GPT here, I don’t know if UEFI will boot with DOS or other kinds of partition table or not but just chose GPT

_config.yml

Create partition as above. Here I have 512MB partition #1 which we will use it for /boot to store the kernel and initramfs, the total of it will be just around 50MB so you could create as low as somewhere 80MB but I want to leave some extra space for backup/alternative kernel and such. The second partition will be for LUKS encrypted rootfs so it will take up the rest.

You need to create a separated /boot partition for UEFI to boot the kernel because UEFI don’t understand what LUKS is thus it can’t be merged into rootfs

Do not create another partition for SWAP because it will be unencrypted, create SWAP as a file later on the encrypted rootfs if you want to use SWAP

Write to the disk and exit cfdisk.

_config.yml

Now we need to turn /dev/sda2 into a LUKS container by

cryptsetup luksFormat /dev/sda2

Open (decrypt) the LUKS container with

cryptsetup open /dev/sda2 new_rootfs

This is how LUKS works: It reads the encrypted data (raw disk format) on your block disk, decrypts it and put it to a virtual block device at /dev/mapper/ so you need to interacts with the virtual block device and not the real block device itself (which containing LUKS). Here I have the decrypted virtual block device at /dev/mapper/new_rootfs

_config.yml

Now we need to format it, it’s necessary for the /boot partition to be FAT32 for UEFI to read it so we need to format /dev/sda1 with

mkfs.vfat -F32 /dev/sda1

And the decrypted sda2 block device (located at /dev/mapper/new_rootfs) which will contain the rootfs, you can use whatever the filesystem you like, as long as you add the necessary module to the kernel initramfs.

mkfs.ext4 /dev/mapper/new_rootfs

_config.yml

Here notices: You need to mount the rootfs first because we will need to mount the /boot over /mnt/boot and it doesn’t make sense if you do it in reverse, the /boot will get ignored and nothing will write to it if you do it in reverse

Start by mounting rootfs:

mount /dev/mapper/new_rootfs /mnt

Create the /boot directory on the rootfs:

mkdir /mnt/boot

Mount sda1 into the /mnt/boot so the pacstrap could install kernel into it:

mount /dev/sda1 /mnt/boot

That’s it for the partition setup, now we will continue the next step on the actual Arch Linux installation.


2. Arch Linux Setup

This step is fairly simple, just do install as you would do it with normal Arch Linux installation with /mnt as rootfs

Starting with installing the base, you could add more package after base-devel if you’d like but here’s the basic.

pacstrap /mnt base base-devel linux

_config.yml

500KB/s??? Pacstrap is dumb and sometimes it cannot figure out what’s the best mirror to download from so here’s how you could manually select the nearest mirror:

_config.yml

Edit the file /etc/pacman.d/mirrorlist and delete every other mirror except the closest one to you, I’m in Vietnam so here I kept the Vietnamese mirror and delete the rest.

_config.yml

Save it, run pacstrap again, see how fast it is now?

_config.yml

Wait for the pacstrap to complete and then generate fstab file with:

genfstab -U /mnt >> /mnt/etc/fstab

_config.yml

Now check if the fstab has properly generated, it must have the rootfs (/) and the /boot in there, these 2 should have been added automatically by genfstab but if it doesn’t, either you’ve done something wrong with the mounting step or genfstab is broken, try to add it manually.

I’d recommend using UUID when interacting with fstab to prevent unexpected behavior

_config.yml

Here you have to chroot into the installed rootfs, what is chroot? It allows you to “join” into the installed rootfs environment as you were booted into it, meanwhile, the kernel still is from the host (Arch Linux Live), all the changes you made in here will be saved into your final installation disk (/dev/mapper/new_rootfs).

Chroot into the installed directory with

arch-chroot /mnt

Now when you’re inside the chroot, declaring the language and generate the locale with

echo LANG=en_US.UTF-8 > /etc/locale.conf
echo "en_US.UTF-8 UTF-8" > /etc/locale.gen
locale-gen

I only do 1 step of adding locale because I’ve forgotten the rest when taking screenshots for this tutorial. You could set up the Desktop environment and install things here if you like, I’ll skip it because that’s not what this tutorial is for.

_config.yml

Now the important step: While in chroot, edit the file /etc/mkinitcpio.conf and add encrypt to HOOKS as the picture above.

You will need to add keymap before the encrypt if you want to use keymap other than the US standard keymap.

Save the mkinitcpio.conf and to the next step

_config.yml

Now you will need to regenerate the initramfs for the kernel to be able to decrypt the encrypted rootfs with the added encrypt module.

First, check your installed kernel version with

ls /usr/lib/modules

You can see the only installed kernel is 4.19.2-arch1-1-ARCH (ignore the extramodules-ARCH)

NOTE: Do not use uname -r to check for kernel version here, this command will return the current kernel which is used to boot and it’s from Arch Linux Live, it may or may not be the same version as the kernel which you’ve installed to the rootfs

Get the name of the install kernel in /boot (vmlinuz-* for the kernel, initramfs-*.img for the initramfs) for use in the next step, if you use other kernels like Zen then the name will be different

Generate new initramfs with

mkinitcpio -g /boot/initramfs-linux.img -k 4.19.2-arch1-1-ARCH

Modify the command as needed

_config.yml

Now exit from chroot to the Arch Linux Live, I’m paranoid here so I’ve done another sync to make sure everything was flushed down the disk and then reboot, continue to boot with the Arch Linux bootable media.


3. Configure EFI to boot the kernel

_config.yml

In the Arch Linux boot menu, chose UEFI Shell x86_64 v2 this time to get into the EFI Shell

_config.yml

Here’s how the EFI Shell looks like, you could press ESC to skip startup.nsh if you’ve configured it to boot something else.

_config.yml

Here types map to see which partition the EFI has recognized, it’s kinda similar to Linux, here I have FS0 and FS1

_config.yml

Use ls fsX: (with X is the FS# from the mapping table above)

What you’re trying to find here is the partition which contains the kernel files (/boot)

Here we found that the fs0 is the one that we need, it has vmlinuz and initramfs

_config.yml

We need to add a new boot entry at 0 points to vmlinuz-linux so it will get booted first with the command:

bcfg boot add 0 fs0:\vmlinuz-linux "Arch Linux"

_config.yml

Now create a new text file for storing the cmdline for the kernel with

edit fs0:\cmdline.txt

Here you put the necessary kernel cmdline to boot your rootfs, in this example we have

 cryptdevice=/dev/sda2:new_rootfs root=/dev/mapper/new_rootfs rw initrd=\initramfs-linux.img 

NOTICE: Add extra spaces at the beginning of the line in the file. There is a byte order mark at the beginning of the line that will squash any character next to it which will cause an error when booting.

If you use SSD for rootfs, you could add a flag to allow discarding on LUKS encrypted drive with :allow-discards added after the cryptdevice, it will becomes cryptdevice=/dev/sda2:new_rootfs:allow-discards

If you want to use intel-ucode, you need to add an extra initrd before the actual linux initramfs, it will be something like this:

 cryptdevice=/dev/sda2:new_rootfs root=/dev/mapper/new_rootfs rw initrd=\intel-ucode.img initrd=\initramfs-linux.img 

Press F2 to save and then F3 to exit.

_config.yml

Now insert the cmdline to the boot entry #0 with

bcfg boot -opt 0 fs0:\cmdline.txt

reboot your machine with reset, now you don’t need the Arch Linux bootable media anymore, you could pull it out and let your machine boot normally.

_config.yml

Here you can see it’s booting and the kernel asked you to typing password for unlocking the rootfs

_config.yml

That’s it, that’s how you configure your machine to boot the kernel with UEFI meanwhile use LUKS for the rootfs. Comment below if you have any questions or stuck on somewhere along the line.


Resources:

EFISTUB

dm-crypt/Encrypting an entire system

How to Install Arch Linux

Updated: