Real full disk encryption using GRUB on Artix Linux for BIOS and UEFI
Published on 2021-02-10. Modified on 2022-07-18.
In this tutorial we're going to take a look at setting up full disk encryption on the Artix Linux base system using GRUB for both a BIOS/MBR based setup and a UEFI based setup. While the choice to install in UEFI mode is encouraging, vendor UEFI implementations still carry more bugs than their BIOS/MBR counterparts. You are advised to do a search relating to your particular motherboard model before proceeding. Contrary to "modern" advice I still haven't found any compelling reason to use UEFI yet.
When we use GRUB as the boot loader we can setup a full disk LUKS encryption system without any use of a separated unencrypted boot partition. Normally a separate boot partition needs to remain unencrypted as the bootloader needs to be able to boot the kernel before invoking LUKS, but because GRUB can load encryption modules such as crypto.mod
, luks.mod
, and cryptodisk.mod
we can use GRUB in various settings and still gain a real full disk encryption model without the need for an unencrypted boot partition. This setup is not possible using other boot loaders such as systemd-boot or syslinux, because neither of those support loading encryption modules (as of writing).
The benefits of running with a real full disk encryption rather than an unencrypted boot partition is that we can mitigate numerous attacks that can occur before and during the boot process, such as an attacker installing a modified kernel that is able to harvest your password phrase. This doesn't mean that the system isn't vulnerable to tampering with the BIOS or the UEFI boot loader itself, however it does provide yet another level of security that makes it a bit more difficult to gain access to the encrypted information.
For BIOS our setup will simply consist of one disk partition, it will look like this:
sdX1 (LUKS with EXT4)
For UEFI we'll add another partition to hold the GRUB UEFI file:
sdX1 (GRUB UEFI) sdX2 (LUKS with EXT4)
Everything will be fully encrypted with LUKS.
Instead of using a partition for swap we will use a swap file. Not only is this a much more simple setup, but it also eliminates the need to encrypt the swap partition independently. By using a swap file we automatically get the encryption we need.
If you need to be able to put your machine into hibernation, you need a proper swap partition. I never use hibernation so I am not going to go through that in this tutorial.
I'll use EXT4 as the filesystem, but you can change that into something else.
One minor downside to this setup with GRUB is that you have to enter your encryption password twice. Once for GRUB and another time during the system boot-up when the Linux initrd image is loaded. However, this can be avoided by adding a keyfile to mkinitcpio
- which we'll do. When the system is booted the keyfile resides in the ramfs, unencrypted, but at this point, so does the LUKS master key, so if an attacker can get a hold of your keyfile in this situation, he might as well get your master key. In such a situation you will need to do a lot more to secure your system, something which is well beyond the scope of this tutorial.
As of writing Artix Linux supports three different init systems, mainly OpenRC, runit and s6. I am most familiar with runit, so that is what I have decided to use, but you can use any of the other two if you want.
Let's get started.
-
Boot the relevant Artix Linux base install medium, either "artix-base-openrc", "artix-base-runit" or "artix-base-s6".
-
Once you have booted up and logged in, become root:
$ su
Then verify that your network is working:
# ip a
And try to ping out:
# ping artixlinux.org
-
Setup your keyboard:
# loadkeys KEYMAP
Where "KEYMAP" corresponds to your keyboard layout. For a list of all the available keymaps, use the command:
# ls /usr/share/kbd/keymaps/i386/qwerty/
In my case I am using a Danish keyboard layout so I am using the following command:
# loadkeys dk
If you find that your keymap is represented with and without a "latin" version, like in
dk
anddk-latin
, then the latin version is the one that enables dead keys while the one without the latin part doesn't.Dead keys are those keyboard keys that do not type anything until you hit the key twice or a combination of two keys. Tildes and umlauts are like this by default under plain Linux. This is the default behavior for these keys under Microsoft Windows as well.
Remember that the above keyboard setup might change if you use a graphical window system (such as Xfce4 or Gnome for example) because such systems often has their own keyboard layout setup. The above setup is for usage in the terminal without the X Window System running.
Also if you use another keyboard type than QWERTY, then take a look in
/usr/share/kbd/keymaps/i386/
for the supported types. -
Locate your harddrive:
# fdisk -l
-
Before you begin partitioning your disk you may want to write random data to the drive first with something like the following:
# dd if=/dev/urandom of=/dev/sdX
Please note that this can be a very time-consuming process, depending on the speed of your CPU and disk, as well as the size of the disk. If you don't write random data to the whole device, it may be possible for an adversary to deduce how much space is actually being used.
Personally I don't write random data to the disk as I just want to protect the disk from being accessed in case the computer is ever stolen.Next, partition the disk. If you're not comfortable using
fdisk
thencfdisk
is a nice partitioning tool. We're going to create one partition for BIOS or two partitions for UEFI.# cfdisk /dev/sdX
Create the relevant partitions.
If you're going to use UEFI then the general recommendation is 500 MB or more for your UEFI partition. Please also note that the GUID Partition Table is mandatory for UEFI.
Remember to make the root filesystem bootable and choose "dos" as the label type if you're using a BIOS/MBR setup.
If you're using a disk that already has a GPT label, you can change that withfdisk /dev/sda
, then chooseo
, choosedos
, and thenw
. -
Format the root partition using LUKS.
I am using the BIOS setup, so in my case it's the first and only partition on the device. In the rest of the tutorial you need to change that if you're using UEFI. If
sdX1
is your boot partition, then you'll properly need to change my examples fromsdX1
tosdX2
as that will be your root partition.# cryptsetup luksFormat --type luks1 /dev/sdX1
cryptsetup
currently defaults to v2 of the LUKS header. There has been great work at getting GRUB version 2.06 to support LUKS2, but there still is a bug that prevents this from working. Make sure you specify--type luks1
when creating the encrypted partition. See bug 55093 and the Encrypted boot section at the Arch Linux wiki for details. -
Open the newly formatted LUKS partition:
# cryptsetup open /dev/sdX1 cryptroot
In this case I have chosen the name "cryptroot" for the encrypted root partition, but you can name it whatever you want, just remember to change it everywhere where I have used "cryptroot" in this tutorial.
-
Install the filesystem of your choice on the encrypted root partition (EXT4, XFS, Btrfs, etc.) In this case I'm using EXT4.
# mkfs.ext4 /dev/mapper/cryptroot
Use the command
lsblk -fp
to list all the UUIDs of your system. Write down the one for/dev/mapper/cryptroot
as you're going to use it more than once.If you're using UEFI you need to format the UEFI boot partition with the Fat filesystem.
# mkfs.vfat -F32 /dev/sdXY
Where
sdXY
is your UEFI boot partition. -
Mount the root filesystem:
# mount /dev/mapper/cryptroot /mnt
-
If you want you can select the Pacman mirrors by placing the ones you prefer in the top:
# vi /etc/pacman.d/mirrorlist
-
Now we're going to install the base system. In this step you have to decide which init system you want.
If you choose OpenRC:
# basestrap /mnt base openrc elogind-openrc
If you choose runit:
# basestrap /mnt base runit elogind-runit
If you choose s6:
# basestrap /mnt base s6 elogind-s6
-
Then we need to install a kernel and some microcode and firmware. In this setup I am using an Intel CPU, so I use the
intel-ucode
. If you run with an AMD CPU, you needamd-ucode
. We also need to install the GRUP bootloader.Artix Linux provides three kernels
linux
, which is the vanilla Linux kernel and modules, with a few patches applied.linut-lts
, which is long-term support (LTS) Linux kernel and modules. Andlinux-zen
, which is a result of a collaborative effort of kernel hackers to provide what they consider the best Linux kernel possible for everyday systems.If you use an UEFI setup you also need to add the
efibootmgr
package to the following command:# basestrap /mnt grub linux linux-firmware intel-ucode
-
Generate and verify
fstab
(use-U
for UUID and-L
for partition labels):# fstabgen -U /mnt >> /mnt/etc/fstab # cat /mnt/etc/fstab
If you're using an SSD disk you need to consider whether you want to add the "discard" option in order to enable continues TRIM support.
On traditional magnetic drives, deleted files are not completely removed from the disk at the time of deletion (this is why you can recover deleted files), instead the filesystem references the location of a file on the disk, and when a file is deleted, that reference is erased, allowing you to write new data over old data in these spaces.
With SSDs this is different. New data can only be written on completely new or erased cells of the drive. Because the space must be cleared prior to a write, if enough free space is not already available at the time a file is being written, it must be erased first. This can negatively affect performance.
TRIM allows the SSD to erase unused cells in the background so that the SSD does not have to erase the cell later when it has to write, thus speeding up the write process.
Most recent SSDs have their own internal garbage collection process that does this very effectively, so TRIM isn't necessary to maintain write performance anymore.
Without enabling TRIM (either periodic TRIM or continues TRIM) garbage collection can become write-amplified in the edge case where your hard drive is almost full. This problem can be mostly mitigated by over-provisioning the SSD's unused space (leave about 20% of the drive free).
Please see the Relevant reading section for further information regarding TRIM, especially the links regarding OpenBSD.From a security point of view, enabling TRIM allows an attacker to get an idea of how full the volume is.
If you want to enable continues TRIM in
fstab
, you need to add thediscard
option:# /dev/mapper/cryptroot UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx / ext4 rw,relatime,discard 0 1
-
Now, chroot into the newly created system and set the basic stuff up (change the zoneinfo to your location):
# artix-chroot /mnt # echo KEYMAP=dk > /etc/vconsole.conf # ln -sf /usr/share/zoneinfo/Europe/Copenhagen /etc/localtime # hwclock --systohc
Before we continue we need to install a text editor and cryptsetup. I prefer to use Vim or Neovim, so I will install that.
# pacman -S vim cryptsetup
Then we can proceed with the setup. First uncomment your locales:
# vim /etc/locale.gen
Then generate the locales and setup your hostname:
# locale-gen # echo LANG=en_US.UTF-8 > /etc/locale.conf # echo my-hostname > /etc/hostname
-
Create the keyfile (in order to avoid having to type the encryption password twice):
# dd bs=512 count=4 if=/dev/urandom of=/crypto_keyfile.bin # cryptsetup luksAddKey /dev/disk/by-uuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /crypto_keyfile.bin # chmod 000 /crypto_keyfile.bin
-
Add
encrypt
to themkinitcpio
HOOKS and the keyfile to the files section:# vim /etc/mkinitcpio.conf FILES=(/crypto_keyfile.bin) HOOKS=(base udev autodetect modconf block encrypt filesystems keyboard fsck)
-
Create the initial ramdisk environment:
# mkinitcpio -p linux
-
Set the root password:
# passwd
-
Enable cryptdisk support in GRUB:
# vim /etc/default/grub
Add the following line:
GRUB_CMDLINE_LINUX="cryptdevice=UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:cryptroot"
Where the "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" matches the UUID of the partition you unencrypt when you use the
cryptsetup
command. Use the commandls -l /dev/disk/by-uuid
to verify that you are using the correct UUID forsdXY
and not thedm-0
device.In my specific case it is the UUID that macthes sda2:
# ls -l /dev/disk/by-uuid
9bbca4ad-9942-452f-9c8d-e042818398eb -> ../../dm-0 a3269d46-cf1b-46da-89bb-ec4ee3007432 -> ../../sda2Then uncomment:
GRUB_ENABLE_CRYPTODISK=y
If you're using an SSD disk and you have enabled continues TRIM you need to add "allow-discards" to that:
GRUB_CMDLINE_LINUX="cryptdevice=UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:cryptroot:allow-discards"
-
Install GRUB and reboot:
For a BIOS setup
# grub-install /dev/sdX --recheck
For an UEFI setup
# mkdir /boot/efi # mount /dev/sdXY /boot/efi (mount the UEFI boot partition you created) # grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=grub /dev/sdX --recheck
As mentioned, motherboard vendors implement UEFI differently and for some the way GRUB stores its EFI file isn't working. In one previous setup of mine I was running with a MSI motherboard and I had to rename the file and the path once GRUB was done. The one I had looked like this:
# cd /boot/efi # tree . └── EFI └── boot └── bootx64.efi
The setup varies from vendor to vendor, but you can go ahead and test with your board, if the default isn't working you can always just boot up on the Artix Linux install medium and then mount the UEFI partition and then rename the directory and GRUB UEFI file.
-
Generate
grub.cfg
:# grub-mkconfig -o /boot/grub/grub.cfg
-
Now you can setup the network and install additional users and packages:
# useradd --create-home foo # passwd foo
I prefer to run with dhcpcd so I'll set that up.
With Artix Linux each service file is installed separately with the package name and the init system name at the end. E.g.
dhcpcd-runit
ordhcpcd-openrc
.# pacman -S dhcpcd dhcpcd-runit # vim /etc/dhcpcd.conf (what ever changes you may need to make)
Don't try to enable any services until you have rebooted (unless you're using OpenRC). It's easier as runit and s6 rely on a
/run
(a tmpfs) directory to be created.So we'll just reboot the system now:
# exit # reboot
-
You should now be presented with the GRUB prompt for the password. Once the system is finished booting, log in.
Continue the setup by enabling dhcpcd as a service (I am using runit, but look up how to do this with the other init systems on the Artix Linux wiki):
# ln -s /etc/runit/sv/dhcpcd/ /run/runit/service/
Then check the status with:
# sv status dhcpcd
-
Now it's time to add the relevant entries to the
/etc/hosts
file:# vim /etc/hosts 127.0.0.1 localhost ::1 localhost 127.0.1.1 myhostname.localdomain myhostname
-
Last we need to create a swap file (if you want that). Use
dd
to create a file the size of your choosing. E.g. creating a 4GB swap file:# dd if=/dev/zero of=/swapfile bs=1M count=4096 status=progress
Set the right permissions:
# chmod 600 /swapfile
Format the file to swap:
# mkswap /swapfile
Activate the swap file:
# swapon /swapfile
Finally, edit the
/etc/fstab
configuration to add an entry for the swap file:# vim /etc/fstab /swapfile none swap defaults 0 0
You can check that the swapfile is up and running with:
# free
That's it. You can now proceed and install all the rest of your favorite programs :)