TR-83 - Linux Boot Hardening HOWTO

Linux Boot Hardening

Introduction

Having an encrypted disk to store your root file system and other files is good to protect your data from being read or tampered with offline. The way many Linux installations are made is by leaving a boot partition /boot un-encrypted and thus modifiable. So what prevents someone to tamper with your kernel or your boot loader … nothing, if secure boot is not implemented. Loading a malicious boot loader or kernel would completely break the security of your setup, as all your encrypted data would finally become accessible.

In this blog post, we are going to address this issue by proposing a way to secure the boot sequence of your Linux based distribution.

The Different Approaches

There are different approaches to raise the security of your boot sequence.

  1. YOLO ! I don’t care if anyone can access my system … Anyway I have nothing to hide (you can stop reading this post unless you are open to change your mind)
  2. I want to implement secure boot but I accept that some of my files get accessible if someones wants to read my disk
    • You can use a signed Unified Kernel Image (UKI) stored on a non-encrypted boot partition and boot it directly from UEFI
    • You can use a traditional signed boot loader like GRUB with a non-encrypted boot partition where you store your kernel, micro-code and initramfs
  3. I want to expose the less files possible and make life harder to anyone wanting to hack into my system
    • there are probably other ways to do but the one we came up with is by using a signed boot loader and an encrypted boot partition where everything else than the boot loader will be stored.

Covering all the different ways to do is not the aim of this blog post, so we will only cover the last implementation, as it constitutes (according to us) the most secure option.

Disclaimers

To implement secure boot you will need to create your own keys and enroll them in UEFI. However, at some point you will have two possibilities:

  1. You keep Microsoft Certificates stored by default in many firmware. This means anything signed with a Microsoft key will also be allowed to boot. It might not be desirable for some users/use cases (i.e. the ones not trusting Microsoft certificates owners).
  2. You remove Microsoft Certificates from UEFI and keep only your own. This is the most secured way as only you can control what can be booted but it is also the more risky as you may soft brick your machine in some very specific conditions.

Playing with secure boot may brick your machine but don’t be afraid, this might only happen if you do things you don’t fully understand. Moreover, we are going to use sbctl to implement secure boot, which does everything to prevent you from bricking your machine. To the best of our knowledge, soft bricking can happen only if you remove the Microsoft Certificates from the UEFI certificate database and your hardware needs those certificates to authenticate some graphic drivers. The only thing to remember here is that you must not do things you don’t understand when implementing secure boot.

Road map

In order to explain how to implement secure boot, we will rely on the Archlinux installation guide as it details very well the distribution installation process. We will walk through all the steps of the installation process and will incorporate some slight changes in order to achieve our goal. Our setup can be decomposed in two independent steps.

  1. configuring encrypted boot
    • creating and formatting partitions
    • configuring cryptboot helper
    • installing grub
  2. configuring secure boot
    • creating and enrolling keys
    • signing files
    • enabling secure boot

NB: it is possible to realize those steps on a existing installation but we preferred to describe an installation from scratch. So if you understand what you read in this blog post, there is nothing preventing you from applying what you have read on your currently running setup.

Now, we can start ! Please follow the Archlinux installation guide until you reach the disk partitioning section.

Encrypted Boot

Fully encrypting the boot partition is not possible as UEFI needs to load a EFI stub to initiate the boot process. I our case this stub will be the GRUB boot loader configured in such a way that it supports both disk encryption and secure boot. To do so we will need to create and format two partitions. The first will be an non-encrypted partition we will call EFI partition holding the EFI stub needed by UEFI. The other will be the encrypted boot partition, holding the kernel, initramfs, micro-code and so on.

Disk Partitioning

The following steps substitutes a part of Disk partitioning section of Archlinux installation guide.

This section will only cover the creation of boot partitions to achieve an encrypted boot setup. The rest of the partitioning being specific to every setup will be left to the user.

# this is the device of the disk we want to format
# you must replace it with yours
DISK=/dev/nvme
# we create a gpt partition table
parted $DISK mktable gpt
# we create an un-encrypted EFI partition where our bootloader will be stored
parted $DISK mkpart EFI fat32 0% 512MB
# we create a partition where our encrypted boot will be
parted $DISK mkpart ENCBOOT 512MB 2GB
# shows current partition table
parted $DISK print

Disk formatting

Formatting EFI Partition

# EFI partition needs to be fat32 to be readable by UEFI
sudo mkfs.vfat -F32 ${DISK}p1

Formatting Boot Partition

GRUB has partial support for LUKS2 so either the partition has to be formatted with --type luks1 or using specific settings for LUKS2. If you are not using latest GRUB version, please verify that LUKS2 is supported.

# Formatting boot partation using LUKS1
sudo cryptsetup luksFormat --type luks1 ${DISK}p2

OR

# Using luks2 but with some specific settings
sudo cryptsetup luksFormat --hash sha256 --pbkdf pbkdf2 ${DISK}p2

We can now format the boot partition

# we decrypt boot partition, it is going to appear as /dev/mapper/boot
cryptsetup open ${DISK}p2 boot
# we format our boot partition
mkfs.ext4 /dev/mapper/boot
# we can close boot device
cryptsetup close boot

# format the other partitions as you need

It is now the good moment to configure the rest of your partitions (root, var …). We voluntarily do not cover this section as it out of the scope of this post.

Preparing Mountpoints

You should now having prepared the other partitions your system will use. We also assume now the root filesystem of your installation is mounted on /mnt as documented in here.

# WARNING: at this point we consider the root filesystem mounted in /mnt

# we create boot mountpoint
mkdir /mnt/boot

# we open our encrypted boot device
cryptsetup open ${DISK}p2 boot
# we mount our decrypted boot
mount /dev/mapper/boot /mnt/boot

# we create EFI partition mountpoint
mkdir /mnt/boot/efi

Please at this point continue by doing the integrality of the installation section. Before going further with system configuration, issue the following two commands. This is to prevent genfstab to take /boot and /boot/efi into account.

# our boot and efi partitions are ready so we unmount them
umount -R /mnt/boot
# we can close boot device
cryptsetup close boot

You should now continue to configure the system.

It is important to keep in mind that when generating /etc/fstab with genfstab one should be careful that /boot and /boot/efi partition DO NOT appear (or appears with noauto option) in /etc/fstab. The reason is that our boot partition is encrypted and if the kernel tries to mount it at boot it will fail.

Configure cryptboot

Once you did the chroot, with arch-chroot /mnt, take a break and do the following before going further. We are going to use cryptboot a specific tool easing the management of encrypted boot partitions. Everything done by cryptboot can be done by hand but it will be more tedious and error prone.

The first thing to do is installing cryptboot, then we can configure it.

# create cryptboot config directory
mkdir /etc/cryptboot

# generate cryptboot configuration
cryptboot configure --boot-device /dev/luks_boot_device --efi-device /dev/fat32_efi_device > /etc/cryptboot/config.toml

# control that everything is correct in configuration file

Before going further with system configuration, it is mandatory to mount /boot and /boot/efi, otherwise mkinitcpio will fail.

# use cryptboot to mount boot and efi partitions
cryptboot mount

At this point you can continue the normal Archlinux installation procedure until you arrive at the boot loader section.

Installing boot loader

If one wants to only use an encrypted boot, GRUB installation can be done the normal way with grub-install. However, as we want to later enable secure boot, we need to install it in a specific way. So that secure boot works, GRUB needs to be bundled with all the modules it needs to load the kernel. This is because recent Grub versions does not allow module side loading when secure boot is enabled. So, lets do that easily thanks to cryptboot.

Grub config

Edit /etc/default/grub

Enable grub to decrypt LUKS device

GRUB_ENABLE_CRYPTODISK=y

Do not forget to set the proper command line to boot the Linux Kernel by setting GRUB_CMDLINE_LINUX

Grub Installation

# This will run grub-mkconfig and grub-install in such a way that your grub
# installation will work with secure-boot
# --no-sign option is used because we don't want to sign GRUB yet
# as secure boot has not be configured
cryptboot grub-install --no-sign

# you can control that you see the GRUB efi stub in /boot/efi
find /boot/efi -name '*.efi'
# you can control that you have everything in /boot
# check vmlinuz
find /boot -name 'vmlinuz*'
# check initramfs
find /boot -name 'initramfs*'
# check grub.cfg
find /boot -name 'grub.cfg'

If you are done with your Archlinux installation you can now reboot the system and you should be able to boot on your fresh installation configured to use a dedicated encrypted boot partition.

At this point, we have finished configuring the boot process so that it uses an encrypted boot partition to store all the files needed to boot Linux (grub configuration, vmlinuz, initramfs …). Assuming you also use disk encryption for your other partitions, the single thing one can see while getting access to your disk is the GRUB efi stub. As mentionned earlier, this is already much more secure than having a non-encrypted boot partition, but it does not protect you from someone tampering with your boot loader (i.e. efi stub). To protect against this we need to implement secure boot, and this is exactly what we are going to do in the next section.

Secure Boot

In order to implement secure boot the easiest way is by using sbctl so this is the one we will document. One can find other ways in Archlinux’s secure boot documentation.

The first thing one has to do is to find how to enter in secure boot Setup Mode. This is very specific to your hardware and this is something you will have to find in your UEFI BIOS interface (before booting). The Setup Mode puts the firmware in a specific state during which you can enroll your cryptographic keys used during secure boot. It is worth noting that Setup Mode is a temporary state, at the next boot it will be disabled and keys cannot be changed anymore (unless you decide to do enter Setup Mode again). You should now realize that setting up secure boot without having a password on your UEFI BIOS is useless as anyone getting access to it can enroll its own keys or even disable completely secure boot.

Creating Signing Keys

Make sure you are in Setup Mode

# running this command you should see if you are in Setup Mode
sbctl status

The next step is to create your cryptographic keys

sbctl create-keys

Enroll Keys

This is the critical part and where you should be careful not bricking your machine. The following command will enroll your keys along with the Microsoft ones. This is the safest option, but it is not the ideal one especially if you have a trust issue regarding Microsoft certificates.

sbctl enroll-keys -m

The other option is to run the following command. If there is a risk to brick your device sbctl should warn you. Anyway, it is better for you to read more about Option ROM so that you have the whole picture.

# do this with care
sbctl enroll-keys

Sign Files

It is important to understand that this step does not require Setup Mode to be enabled.

# we make sure boot partition and efi partition are mounted
cryptboot mount
# this command will display signed and unsigned files
sbctl verify

# sign boot loader (path may differ according to your setup)
# -s allows sbctl to save grubx64.efi in the database of files
# to sign. Then sbctl sign-all can be used to sign all files in db
sbctl sign -s /boot/efi/EFI/grubx64.efi
# sign your kernel image
sbctl sign -s /boot/vmlinuz*
reboot

As you can see, setting up secure boot is not the more complicated part. At this point you may need to enable secure boot in your UEFI BIOS. If you followed everything in this post the boot should work without issue. Once you get back to a terminal run the following commands (as root).

# you should see that secure boot is enabled
cryptboot run sbctl status

Voila ! You’ve made your own secure boot setup with Linux.

Pushing further

System Updates

If you have made the choice of using an encrypted boot partition, you may have anticipated that kernel upgrades would become more complex, as it needs to update files /boot. This is one of the reason why cryptboot exists. It can mount your encrypted boot, run a command and then unmount it in a single command line. For instance running cryptboot run -- pacman -Syu would run a package update on Archlinux with your encrypted boot mounted in /boot.

Protecting UEFI BIOS with a password

Without securing access to your UEFI BIOS a secure boot setup is useless, since it can simply be disabled. Therefore it is crucial to protect your UEFI BIOS with a password.

Protecting your secure boot signing keys

If you dug a bit more around all the steps you have done so far, you may have observed that all secure boot signing materials are stored in /usr/share/secureboot by sbctl. In spite of this directory is protected by ACLs, we believe it is not ideal because those are exposed to privileged user. If the system gets compromised, those might be easily stolen. Even though the following solution is not perfect, storing the cryptographic materials on the encrypted boot partition would definitely harden the setup. So that the hardening is really effective one does not have to let /boot mounted permanently and thus rely on cryptboot to mount boot partition only when needed. Such hardening can be easily done withcryptboot harden-sbctl command.

Speaking about the ideal setup to store your secure boot cryptographic materials. Those would have to be stored on a completely separated machine, which would be used exclusively to sign every update you make to the kernel and EFI stub. This solution complexifies a lot system updates and is cumbersome, especially for a single workstation. However, that might be an affordable solution while managing a fleet of Linux machines.

Discussions

Why not using a single encrypted root partition ?

This is possible, however it means all the files in /boot are accessible all the time when the system root partition is mounted. This is by essence less secure than mounting /boot only at boot time and for kernel updates, actually the two moments where it needs to be mounted. Moreover, the encrypted boot partition can be used to store secure boot signing keys which adds an additional level of protection to them as those are not always accessible.

Conclusion

In this post we have proposed a way to harden the boot process, which might be the weak point of your Linux installation. To do so, we recommend both to implement secure boot and use an dedicated encrypted boot partition to store all the files needed for boot such as the Linux kernel, the initramfs and so on. We hope you enjoyed reading this content and that it helped you hardening your Linux setup.

Classification of this document

TLP:CLEAR information may be distributed without restriction, subject to copyright controls.

Revision

  • Version 1.0 - TLP:CLEAR - First version - 3rd April 2024