A Barbarian’s Guide to Full-Disk Encryption with Arch Linux
Even after more than two decades of using Linux, I still do not feel like a venerable graybeard. While I am phenotypically getting there—tempus fugit and all that—I feel that my use of Linux as a daily driver is not refined, but more akin to that of a barbarian of the Dungeons & Dragons kind. In this spirit, I want to provide some of my own notes for setting up a new system with full-disk encryption. Let’s first dispense with some totally-not-contrived questions:
-
What? We are setting up Arch Linux on a new computer. We will make sure that its hard drive is encrypted. To decrypt it, we have to enter a passphrase at boot or resume time.
-
Why? Devices get lost. If your data is encrypted, you only have to worry about small stuff like extinction-level events as opposed to having to think that someone gains access to your precious data.
-
How? We will use LUKS, the Linux Unified Key Setup, to set up an encrypted swap partition and a root partition with a btrfs filesystem. Except for typing the passphrase for booting the system, you will not feel that the encryption is there.
-
But? Look, this is how I like to use the system. Remember that I am a barbarian. I have heard some people talk about alternatives to LUKS but they used long, complicated words. I know LUKS. It works. We will use LUKS. I also know that there are other filesystems. I do not care. We will use
btrfs.1
Prerequisites
I am assuming that you managed to boot your computer from an Arch Linux installation medium. My recommendation is to always get a new ISO image since chances are higher that all required drivers are present. It will also simplify our bootstrapping later on.
Your next step is to get a WiFi or network connection working somehow. The Arch Wiki can help; I am not covering it here because this step depends so much on your specific hardware.
Setting up the Partitions
A word of caution: This is a barbarian’s guide, so we are not going
to do anything like a fancy dual-boot setup. It is certainly possible
to also do full-encryption of the Arch partition in such a context, but
I want to keep things simple. If you absolutely have to do dual-boot,
make sure to install Windows first because it may could will
definitely destroy your bootloader otherwise. (You want to
dual-boot Windows, right? It’s always Windows.)
In what follows, I will assume that your hard drive is represented as
/dev/nvme0n1. Make sure to check your drives with lsblk before you
proceed with the setup. If you are sure, we can now proceed to create
the partitions via fdisk.
Make sure you have a GPT (GUID Partition Table). This should be the
default but you can get a new one via g at the fdisk prompt.
I always create the following three partitions:
- An EFI partition with 1 GiB.
- A Linux partition with ~90% the size of the hard drive.
- A swap partition that is slightly larger than the amount of my RAM.
None of these choices are particularly smart or principled; except for
the EFI one, which is very generous in terms of its size. After creating
the partitions via n at the fdisk prompt, your partition table
should look like this:
Disk /dev/nvme0n1: 953.87 GiB, 1024209543168 bytes, 2000409264 sectors
Disk model: SAMSUNG MZVL21T0HCLR-00BL7
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 28538CBB-AAE0-40F4-8015-B90B4C6D9F1A
Device Start End Sectors Size Type
/dev/nvme0n1p1 2048 2099199 2097152 1G EFI System
/dev/nvme0n1p2 2099200 1910507519 1908408320 910G Linux filesystem
/dev/nvme0n1p3 1910507520 2000408575 89901056 42.9G Linux swap
Save this table via w and quit fdisk using q.
Setting up the Filesystems
We will create an encrypted root and swap partition:
-
Use
cryptsetup -v luksFormat /dev/nvme0n1p2to prepare the root partition. This command prompts for a passphrase. Make sure to remember it and type it correctly. If you are not a barbarian, feel free to fiddle with the options ofcryptsetup; but for me, the default options are okay and I see no reason for changing them. Or, to put it a bit bluntly: My setup is supposed to deter criminals, not Mossad. If you want to deter Mossad, you are going to have a bad time. To quote James Mickens:If your adversary is the Mossad, YOU’RE GONNA DIE AND THERE’S NOTHING THAT YOU CAN DO ABOUT IT.
-
Do the same thing for the swap partition, i.e.,
/dev/nvme0n1p3. -
Open the root partition via
cryptsetup open /dev/nvme0n1p2 root. This an excellent test of your ability to type your passphrase. If you are a barbarian like me, you will need multiple attempts. -
Turn this into a
btrfsfilesystem viamkfs.btrfs /dev/mapper/root. -
Open the swap partition with
cryptsetup open /dev/nvme0n1p3 swap. -
Turn this into a
swaparea viamkswap /dev/mapper/swapand enable it viaswapon /dev/mapper/swap. -
Mount the root partition with
mount /dev/mapper/root /mnt. -
Create
btrfssubvolumes for the root filesystem and your home directory, respectively:btrfs subvolume create /mnt/@ # root btrfs subvolume create /mnt/@home # homeThis will enable you to use the
btrfssnapshot functionality if you so desire. If not, it will just mean that you have to type a couple of commands more. Notice that I am not creating a subvolume for/var/logor some related directories because I do not intend to use this one specifically for any snapshots; if anything, I’ll only do snapshots of my home directory.You can also add additional subvolumes later so you are not losing out on anything here.
-
Unmount the root filesystem via
umount /mnt.
Bootstrapping
Having created the basic filesystem layout, it is now time to bootstrap the system and install some packages:
-
Mount the root partition via
mount -o subvol=@ /dev/mapper/root /mnt. -
Create an additional mount point for the home directories and mount it:
mkdir /mnt/home mount -o subvol=@home /dev/mapper/root /mnt/home -
Create a new partition for the bootloader via
mkfs.fat /dev/nvme0n1p1. Warning: This will delete your existing bootloader so if you know what you are doing and what to have a dual-boot system, do not follow this guide blindly.2 -
Create an additional mount point for the bootloader and mount it:
mkdir /mnt/boot mount /dev/nvme0n1p1 /mnt/bootThis partition is not encrypted, and I am perfectly okay with it.
-
Install the most relevant packages via
pacstrap /mnt base base-devel linux linux-firmware btrfs-progs git neovim man-db man-pages texinfo intel-ucode iwd sudo, for instance. You can add as many additional packages as you like but I personally find that these get me started reasonably well.Notice: If you are using an AMD processor, you should install
amd-ucodeinstead ofintel-ucode. Moreover, you can save a couple of MiB by picking specific packages from thelinux-firmwarecollection. I honestly do not find that it makes a big difference and I would rather save some time here. -
Generate an
fstabwithgenfstab -U /mnt >> /mnt/etc/fstab. You should check this one afterwards. Mine looks like this:# Static information about the filesystems. # See fstab(5) for details. # <file system> <dir> <type> <options> <dump> <pass> # /dev/nvme0n1p1 UUID=DCF1-BACA /boot vfat rw,relatime,fmask=0137,dmask=0137,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro 0 2 # /dev/mapper/root UUID=8a63a764-702c-4195-a9c1-b868b5b984d0 / btrfs rw,relatime,ssd,space_cache=v2,subvol=/@ 0 0 # /dev/mapper/root UUID=8a63a764-702c-4195-a9c1-b868b5b984d0 /home btrfs rw,relatime,ssd,space_cache=v2,subvol=/@home 0 0 # /dev/mapper/swap UUID=dc1db2b2-fceb-4c66-b599-88d9b3e1f5e8 none swap defaults 0 0If necessary, you can correct any errors by using
blkidand checking the UUIDs. Pay particular attention to the fact that you are not supposed to use the UUID of the device per se but the UUID of the mapper instead. Here is whatblkidis printing on my system, for example:/dev/nvme0n1p1: UUID="DCF1-BACA" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="97014ff5-fa07-4295-aff5-fb8141cea5a4" /dev/nvme0n1p2: UUID="01b2baa1-33ea-46c9-8760-24f7afd40ae5" TYPE="crypto_LUKS" PARTUUID="5e078747-0209-4363-a472-ae98797023b2" /dev/nvme0n1p3: UUID="beb62149-652f-4988-9787-f54cd2ec188d" TYPE="crypto_LUKS" PARTUUID="cadef158-5bc8-454f-91c4-4f78268ac790" /dev/mapper/root: UUID="8a63a764-702c-4195-a9c1-b868b5b984d0" UUID_SUB="ffa67280-ba7f-455e-9201-ada132616ba7" BLOCK_SIZE="4096" TYPE="btrfs" /dev/mapper/swap: UUID="dc1db2b2-fceb-4c66-b599-88d9b3e1f5e8" TYPE="swap"You can see that only the
/dev/mapperentries matter for root and swap, respectively, whereas/bootgets the regular partition UUID. -
Finally, start the
chrootwitharch-chroot /mnt.
Inside the chroot
Inside our cozy chroot, we can start making the system our own. None of this is strictly necessary for the encryption that I want to present in this post but a small digression cannot hurt.
-
Set timezone via
ln -sf /usr/share/zoneinfo/$REGION/$CITY /etc/localtime, where$REGIONand$CITYare kind of near you. For instance, I haveEuropeandZurich, respectively. Look athwclock --systohcif you want to sync the system and hardware clocks. -
Uncomment your desired locales in
/etc/locale.genand runlocale-gen. -
Set the desired locale in
/etc/locale.conf. See, this is why it’s great to havenviminstalled! -
Set a nice hostname in
/etc/hostname. -
Set a root password via
passwd.
Bootloader Setup
This is where it gets dicey because anything you do wrong here will require you to reboot from your USB device, decrypt the root partition again, mount it, and go to the chroot again—it is not like you will brick your system but it can feel annoying.
We will first make sure that our kernel is always compiled with support
for encrypted filesystems and btrfs. Hence, edit /etc/mkinitcpio.conf
and ensure the following lines are there:
BINARIES=(/usr/bin/btrfs)
HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole block sd-encrypt filesystems fsck)
Warning: The order of the hooks matters, so do not experiment too much here. With this configuration, you will be able to decrypt multiple devices (root and swap).
After editing this, generate a kernel via mkinitcpio -p linux. If
this fails, something weird is going on and you should not continue until
you know what you are doing. Because booting without a kernel just
gets you back to square one…
Assuming that everything worked, let’s install a bootloader via
bootctl --variables=yes install. In my own configuration, I had to
update fstab for the /boot partition to contain the
fmask=0137,dmask=0027 options. If this is the case for you, you need
to unmount and mount /boot, leaving the chroot once.
Bootloader Configuration
Finally, the configuration of the bootloader awaits!
-
Create a file
/boot/loader/entries/archlinux.confwith the following content:title Arch Linux linux /vmlinuz-linux initrd /intel-ucode.img initrd /initramfs-linux.img options rd.luks.name=01b2baa1-33ea-46c9-8760-24f7afd40ae5=root root=/dev/mapper/root rw rootflags=subvol=@ options rd.luks.name=beb62149-652f-4988-9787-f54cd2ec188d=swap resume=/dev/mapper/swapNotice that you can use
blkididea to get the UUIDs. This time, make sure to use the actual device UUIDs (see above), which kind of makes sense since these are the things the kernel sees before any decryption can happen. The two lines withoptionswill thus specify the respective devices and ensure that both root and swap are functional (meaning that hibernationwillshould work). -
Finally, edit
/boot/loader/loader.confto contain at least the following lines:default archlinux.conf timeout 3This instructs the bootloader to boot your Arch Linux entry after three seconds. Feel free to adjust additional settings here.
-
Exit the chroot using
CTRL + D. Then executerebootand pray.
Summary
This all seems very involved, I know. But you get a lot—in essence,
you will likely never have to touch this again. It just works (famous
last words) and, thanks to the kernel configuration, automatically
survives updates. On top of that, you raise the bar just a little bit
and make it harder for someone to extract data from your device if they
ever lay their hands on it. Whether this level of paranoia is necessary
is up to you to decide, but since the couple of extra steps do not take
long, I have been setting my system with variations of this recipe for
some time now. And at the risk of bringing the wrath of graybeards,
things have become so much easier thanks to the systemd hooks. No more
/etc/crypttab shenanigans, no more hibernation issues, and a simple
boot loader configuration. Truly wondrous!
And while I cannot guarantee that this is the perfect configuration, I can guarantee that it works for me. If you use an LLM to generate a similar configuration, it may make the following false claims:
- Claim: Multiple
optionslines are not allowed for the bootlader. This is wrong. The options will simply be concatenated. If you are worried about this, you can also just put everything into one longoptionsline. - Claim: You have to enter your passphrase twice. This is only true if you use different passphrases for the root and swap partition. Otherwise, the passphrase will be cached and unlocking works.
See? Truly wondrous!
Here’s to encrypted systems, until next time.