Secure your boot process part 2: Fedora and Unified Kernel images made easy with Dracut
As you may notice, this is almost a part 2 of my Secure your boot process: UEFI + Secureboot + EFISTUB + Luks2 + lvm + ArchLinux. Except that here i’ll not talk about all the secureboot stuff that i’ve already ran into on my last blogpost. This one is specifically focused on how to achieve the same setup using Fedora.
This blogpost compiles my personal opinions around using bootloaders on EFI environments so, the classic “opinions expressed here are solely my own and do not express the views or opinions of my employer” applies.
What is this blogpost about
- Fedora manual installation process.
- Luks2+lvm setup for encrypted partitions at boot time.
/home
disk setup with some dracut tweaks.- EFISTUB to make Linux “it’s own bootloader” avoiding the entire
/boot
to be mounted on your ESP. - Unified kernel image and Secureboot. Means, one file that has initrd, boot parameters and kernel in a “all in one” package.
Requirements
- Download the latest Fedora Workstation iso
- When this blog was published, Fedora 33 was the latest stable release
- Burn this image into a dvd media or usb stick. There is plenty of documentation here
- Some basic cli understanding is desired since we will not install this disto using Anaconda ;)
- Some understanding of disks, partitions, lvm, luks and UUIDs
- Read Secureboot Drama title of Secure your boot process: UEFI + Secureboot + EFISTUB + Luks2 + lvm + ArchLinux
Fedora Drama
Fedora heavly relies on grub2 and it has it’s own dracut scripts for auto generating initrds. While this is a nice approach to create a distribution that is reliable to those who do not want to thinker with bootloaders, i still find outdated this need of use grub while we have more lightweight counterparts like systemd-boot
, or we could just be using the EFI menu entry system on our motherboards.
Bootloaders are not needed anymore. Why insist on using them?
Installing Fedora without using Anaconda
Step 01: Boot the Live DVD Environment, go to “Region and Language” on Gnome and set up your Keyboard correctly. Connect to the internet.
Step 02: Open Gnome Terminal. Type sudo -i
to become root during all the rest of this tutorial.
Step 03: We will create luks2
containers with lvm2
volumes on top of the first disk. On my laptop i have 2 disks: sda
is my first disk while sdb
is my second disk. Use cgdisk /dev/sda
and create a 256MB partition for EFI (ESP) code ef00
and the rest of your disk space create a partition with code 8309
(Linux Luks). For the disk 2 (if any) that will be dedicated to /home
create only one partition code 8309
. Disk layout:
+-----------------------------+ +---------+
| SDA | |SDB |
+------+ +------------------+ | +---------+
||SDA1 | |SDA2 LUKS | | ||SDB1 ||
|| | +------------------+ | || ||
|| ESP | ||LV-ROOT||LV-SWAP|| | ||LUKS ||
|| | || || || | ||HOME ||
+------+ || || || | || ||
| |------------------| | +---------+
| |------------------| | | |
+-----------------------------+ +---------+
You could also use gdisk
or parted
if you are more confortable with those tools.
Note to reader: Disks are named differently depending on your system (vdX
on vms if you are just testing this material, nvmeX
for nvme disks) so change it accordingly. Also, the second disk is totally optional and you could have just one disk containing your root, home and swap partitions.
Step 04: Create your luks container and open it. Default block cipher and block encyption mode should be good enough so there is no need of changing it with -c
parameter:
cryptsetup -y -v --use-random luksFormat /dev/sda2 # type uppercase YES when asked, feed your password when asked
cryptsetup luksOpen /dev/sda2 crypt # type your password
Create your lvm infraesturucture on top of this luks container with swap and root logical volumes. If you have only one disk, create a specific volume to home if you want. Sizes defined here are totally arbitrary.
pvcreate /dev/mapper/crypt
vgcreate vg0 /dev/mapper/crypt
lvcreate --size 4G vg0 --name swap
lvcreate --size 30G vg0 --name root
Format your ESP, root and swap partitions/volumes. I’m using ext4 here but you could use xfs(remember to install xfsprogs
later).
mkfs.vfat -F32 /dev/sda1
mkfs.ext4 /dev/mapper/vg0-root
mkswap /dev/mapper/vg0-swap
Step 04.01 - OPTIONAL: If you have a dedicated disk for your /home
mountpoint and it is already partitioned accordingly, let’s create a luks container and format it.
cryptsetup -y -v --use-random luksFormat /dev/sdb1 # type uppercase YES when asked, feed your password when asked
cryptsetup luksOpen /dev/sdb1 crypthome # type your password
mkfs.ext4 /dev/mapper/crypthome
Step 05: Mount all partitions on /mnt
subdir and bootstrap Fedora:
mount /dev/mapper/vg0-root /mnt
mkdir /mnt/efi
mount /dev/sda1 /mnt/efi
mkdir /mnt/home #OPTIONAL
mount /dev/sdb1 /mnt/home #OPTIONAL
yum --releasever=33 --installroot=/mnt groupinstall core
Change releasever
with the stable version of the time you are reading this material. Wait for the bootstrap to finish.
Step 06: Copy your current resolver settings, mount sys
, proc
and efivarfs
filesystems, bind mount your /dev
and chroot
to your installation environment.
cp /etc/resolv.conf /mnt/etc
mount -t sysfs none /mnt/sys
mount -t proc none /mnt/proc
mount -t efivarfs none /mnt/sys/firmware/efi/efivars
mount -o bind /dev /mnt/dev
chroot /mnt /bin/bash
Step 07: Install some additional packages on the chroot environment:
yum install vim lvm2 openssl lz4 efibootmgr cryptsetup kernel sbsigntools gdisk efitools
This is the minimal set of packages to operate after the first reboot. If you want, install any other packages that you might deem necessary. If you want to use xfs
partitions, just install xfs_progs
here too.
During the time I was experimenting with this Fedora setup on a vm, efitools was not already available on Fedora repos and i had to install it manually. This is not needed anymore :)
Step 08: Set a password for root user and, add and set a password for yourself:
passwd
useradd -m -G wheel,users myuser
passwd myuser
Step 09: Create your fstab with the following content, replacing your ESP partition with the corresponding UUID. UUIDs are unique identifiers on disks and you can discover by issuing blkid
(will show all disks) or lsblk --output=UUID /dev/sda1
for example.
# EFI System Partition(ESP)
UUID=D39A-6EAF /efi vfat rw,relatime,fmask=0022,codepage=437,iocharset=ascii,shortnaremount-ro 0 2
#Root Partition
/dev/mapper/vg0-root / ext4 rw,relatime,defaults 0 1
# SWAP
/dev/mapper/vg0-swap none swap sw 0 0
Step 09.01 - OPTIONAL: if you’ve setup your home on the second disk, get the UUID
of /dev/mapper/crypthome
and add the following line to your fstab. Since devicemapper will rename /dev/mapper/crypthome
to a autogenerated /dev/mapper/luks-uuid-of-sdb1
during next boot, take care to use /dev/mapper/crypthome
UUID(ext4 device mapping), and not /dev/sdb1
UUID(partition with luks container). blkid
should diplay to you the device type TYPE="ext4"
# /home
UUID=xxxxxxxx-xxxxxxxxxx-x-xxxxxxxxxx /home ext4 rw,relatime 0 2
Step 09.02 - OPTIONAL: Create a luks key to auto-unlock your home partition. This key will be stored inside your root partition, that is alrady secured with luks.
dd bs=512 count=4 if=/dev/urandom of=/root/secret.bin
chmod 000 /root/secret.bin
cryptsetup luksAddKey /dev/vdb1 /root/secret.bin #type this container password. no output means success
Step 10: Create a basic directory structure for efi images and copy keytool to the ESP
mkdir -vp /efi/BOOT/Fedora
mkdir -vp /efi/EFI/BOOT
cp /usr/share/efitools/efi/KeyTool.efi /efi/BOOT/KeyTool.efi
No, i didn’t mistype /efi/BOOT/Fedora
neither /efi/EFI/BOOT
. We will use those on a near future ;)
Some motherboards come with firmwares that are able to deploy custom secureboot keys and using KeyTool may not be needed.
Step 12: Create a cmdline.txt
file for the very first boot with your Unified EFI Image:
root=/dev/mapper/vg0-root luks.crypttab=no ro rd.luks.timeout=20 rd.lvm.vg=vg0 luks.uuid=11ed1a3a-22c0-4fab-beeb-5362dcfc67a4 luks.uuid=83ddd1d9-98da-47a0-9d25-e20ea9f6d27f rd.luks.key=83ddd1d9-98da-47a0-9d25-e20ea9f6d27f=/root/secret.bin:/dev/mapper/vg0-root quiet
Detailed explanation:
root=/dev/mapper/vg0-root
- root logical volumeluks.crypttab=no
- do not use/etc/crypttab
to map devices. I’ve ran into a chicken’n egg situation where relying on crypttab would ask passwords twice even if i configure my home partition to be auto-unlocked the same way i did on Arch Linux, and if i userd.luks.crypttab=no
to ignore crypttab during the initrd phase, i would be asked my root password twice(init and kernel) and home one(during initrd since kernel would obey crypttab key auto-unlock config). Using crypttab(no parameter set) would also ask for root password twice and at this point i don’t know if it’s a bug. Usingluks.crypttab=no
and configuring it to auto-unlock my home partition using crypttab would completely ignore this config and i got asked for passwords for root. That’s why i’m not using crypttab and passing luks configuration directly during boot process.- This page contains good information about these luks related parameters, but it didn’t help much while investigating this luks+crypttab behavior.
rd.luks.timeout=20
- luks password timeout in secondsrd.lvm.vg=vg0
- mapvg0
volume group. You could map individual logical volumes here, but that is not the case.luks.uuid=11ed1a3a-22c0-4fab-beeb-5362dcfc67a4
- UUID of the first luks container(/dev/sda2
), that contains the main volume groupluks.uuid=83ddd1d9-98da-47a0-9d25-e20ea9f6d27f
- UUID of the second luks container(/dev/sdb1
)rd.luks.key=83ddd1d9-98da-47a0-9d25-e20ea9f6d27f=/root/secret.bin:/dev/mapper/vg0-root
- Key for83ddd1d9-98da-47a0-9d25-e20ea9f6d27f
(/dev/sdb1
) is/root/secret.bin
and stored at/dev/mapper/vg0-root
mountpoint- While i’m not happy to have this information exposed on my boot process, i’m also not happy with the
crypttab
situation that i’ve got. But hey, the key is inside another luks container so, even knowing the location, the file had000
permission(no read, write or execute to anyone) and our main luks partition needs to be decrypted first with my password for this to work.
- While i’m not happy to have this information exposed on my boot process, i’m also not happy with the
quiet
- less verbosity during boot
Again, if you are not using a second disk for home, the second UUID and it’s key boot entires are not relevant to you.
Step 13: Create your first unified Kernel image and place it at the fallback EFI location(/efi/EFI/BOOT/BOOX64.EFI
). Take care while pointing to your kernel and initrd versions cause they can be different than mine due to Fedora updates and/or releases.
objcopy \
--add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \
--add-section .cmdline="cmdline.txt" --change-section-vma .cmdline=0x30000 \
--add-section .linux="vmlinuz-5.10.16-200.fc33.x86_64" --change-section-vma .linux=0x40000 \
--add-section .initrd="initramfs-5.10.16-200.fc33.x86_64.img" --change-section-vma .initrd=0x3000000 \
/usr/lib/systemd/boot/efi/linuxx64.efi.stub /efi/EFI/BOOT/BOOX64.EFI`
I’m suggesting you to use the EFI fallback location to boot first time cause almost certainly you will not have permissions to write a new boot entry on your motherboard using efibootmgr
on a chroot
environment. You could double-check by running bootctl status| grep -i "sets"
(in my case i’ve got a red X).
Step 14: Exit the chroot
environment and restart your Live system. Remember to eject the live disk.
From now you have a fully functional Fedora booting into Unified Kernel images. Now, it’s time to automate tasks related to secureboot like auto signing and auto image generation after a kernel update.
Creating your Root of Trust
Quoting my other article, Secure your boot process: UEFI + Secureboot + EFISTUB + Luks2 + lvm + ArchLinux:
There is no need to reinvent the wheel so, we will use a pretty good script from mr. Roderick W. Smith aka rodsbooks.com. Check out his page for tons of informations related to EFI and other Linux related stuff. Access http://www.rodsbooks.com/efi-bootloaders/controlling-sb.html#creatingkeys download the script directly:
su - root
cd /root
mkdir keys
cd keys
wget https://www.rodsbooks.com/efi-bootloaders/mkkeys.sh
Let’s explain the contents on this script:
openssl
commands will basically create key and certificate pairs for PK, KEK and DB on X509 formatpython
line will randomly create aGUID
for our secureboot assets (think it like a name to our keys)cert-to-efi-sig-list
is the software that will convert X509 certificates(openssl ones) to a format that EFI can understandtouch
will crate an empty noPK file since we do not have a list of revoked keyssign-efi-sig-list
commands will sign PK with itself, sign noPK using PK, sign KEK using PK and finally sign DB using KEK creating all your root trust.
After taking a look at the script, just run it :)
chmod +x mkkeys.sh
./mkkeys.sh
Creating a dracut config file
Besides of what i did on Archlinux, here we will delagate all signing/unification of the kernel image to dracut. No need to use an exernal tool like sbupdate
.
However, since dracut on Fedora has a ton of custom scripts we will not be able to use it’s native capabilities like uefi_*=
and create a custom config inside /etc/dracut.d
. This will heavly break initrd creation itself, trust me, i’ve spend 2 nights thinkering with it.
Instead, we will write our own script and read it from one outside module, after all the initrd creation is done.
Let’s prepare the key infrastructure:
cp /root/keys/DB.key /etc/efi-keys/db.key
cp /root/keys/DB.crt /etc/efi-keys/db.crt
Create /etc/dracut-sb.conf
with the following content:
hostonly=yes
hostonly_cmdline=no
use_fstab=yes
compress=lz4
show_modules=yes
add_drivers+='lz4'
uefi=yes
early_microcode=yes
uefi_stub=/usr/lib/systemd/boot/efi/linuxx64.efi.stub
uefi_secureboot_cert=/etc/efi-keys/db.crt
uefi_secureboot_key=/etc/efi-keys/db.key
CMDLINE=(
root=/dev/mapper/vg0-root
luks.crypttab=no
ro
rd.luks.timeout=20
rd.lvm.vg=vg0
luks.uuid=11ed1a3a-22c0-4fab-beeb-5362dcfc67a4
luks.uuid=83ddd1d9-98da-47a0-9d25-e20ea9f6d27f
rd.luks.key=83ddd1d9-98da-47a0-9d25-e20ea9f6d27f=/root/secret.bin:/dev/mapper/vg0-root
quiet
)
kernel_cmdline="${CMDLINE[*]}"
unset CMDLINE
Pretty simple eh? some additional compression modules for initrd, uefi parameters for kernel signing(key, cert, stub location), CMDLINE
is basically the same block we’ve used to create our first unified image.
Now, the custom script that will be run after every kernel update. Create the /etc/kernel/install.d/99-make-n-sign-efiimg.install
file.
#!/usr/bin/bash
# "Life, uh... finds a way" -- Dr. Ian Malcolm, Jurassic Park
#
# Ex: bit of an overhead to run dracut twice with our dracut.d config file but...
# dracut -f -v --kver 5.10.16-200.fc33.x86_64 /efi/BOOT/Fedora/linux-signed.efi
#
# The way dracut is tailored to work with Fedora, and its dependencies with grub
# i had to do this little hack, keeping my dracut configfile outside dracut.conf.d
# config drop-in dir, or weird things could happen
# And also, because im not mounting my ESP at /boot or /boot/efi, some weird
# directory garbage is generated with my machine-id...
#
# Generate a new secureboot signed image
COMMAND="$1"
KERNEL_VERSION="$2"
DISTRO="Fedora"
SECBOOTIMG="/efi/BOOT/${DISTRO}/linux-signed.efi"
SECBOOTIMG_BKP="/efi/BOOT/${DISTRO}/linux-signed-bkp.efi"
SECBOOTCONF="/etc/dracut-sb.conf"
ret=0
case "$COMMAND" in
add)
if [[ -f ${SECBOOTIMG} ]]; then
# Backup the last efi image. crate an additional entry with efiboomgr
# to have a fallback just in case...
mv -f $SECBOOTIMG $SECBOOTIMG_BKP
fi
dracut -c $SECBOOTCONF -f -v --kver $KERNEL_VERSION $SECBOOTIMG
ret=$?
;;
remove)
ret=$?
;;
esac
exit $ret
Set execution permission for owner only(root):
chmod 700 /etc/kernel/install.d/99-make-n-sign-efiimg.install
After each kernel update /efi/BOOT/Fedora/linux-signed.efi
will be moved to /efi/BOOT/Fedora/linux-signed-bkp.efi
and a new /efi/BOOT/Fedora/linux-signed.efi
will be generated with the new kernel+initrd+microcode+cmdline glue.
Setting boot order
This part is crucial cause, you’ll need to check if your firmware will allow you to create menu entries. Most motherboards will allow but you know, we can’t test all hardware in existence so, use the following command to check if you are able to write to your motherboard efi boot menu:
bootctl status| grep -i "sets"
✓ Boot loader sets ESP partition information
If ou see a ✓
you are good to go. If not(red X is shown), you will have to change the SECBOOTIMG=
variable of the last script and change it to point to the fallback EFI boot image: SECBOOTIMG="/efi/EFI/BOOT/BOOX64.EFI"
. You will also lose the ability to quickly boot into a recovery image if something goes wrong, but using a live usb you will be able to “revert” by copying it.
If this is your case, change the script and ignore all the steps inside this chapter and jump right into Deploying secureboot keys.
Else, lets move the fallback image to our desired place. efibootmg
needs a valid image and it will check if the file exists before placing a new efi menu entry:
mkdir -p /efi/BOOT/Fedora
mv /efi/EFI/BOOT/BOOX64.EFI /efi/BOOT/Fedora/linux-signed.efi
cp /efi/BOOT/Fedora/linux-signed.efi /efi/BOOT/Fedora/linux-signed-bkp.efi
Setting menu entries is easy with efibootmgr
:
efibootmgr -c -d /dev/sda -p 1 --label "Fedora" -l "BOOT\Fedora\linux-signed.efi" --verbose
efibootmgr -c -d /dev/sda -p 1 --label "Fedora BKP" -l "BOOT\Fedora\linux-signed-bkp.efi" --verbose
In plain english: “Create an efi entry, from sda device partition 1, with the following label, this is the path and please verbose :)”. Note that /efi
is not needed here since we are already saying it’s on sda1(and it’s our ESP).
Double check boot order:
efibootmgr
BootCurrent: 0000
Timeout: 0 seconds
BootOrder: 0000,0001,2001,2002,2003
Boot0000* Fedora
Boot0001* Fedora BKP
Boot2001* EFI USB Device
Boot2002* EFI DVD/CDROM
Boot2003* EFI Network
Those values could change depending on your machine(entry numbers and order), and using efibootmg you could also reorder them or even delete boot entries to make your installation even more secure. For example, if we delete USB, DVD and network boot our menu will look like this:
efibootmgr -b 2001 -B
efibootmgr -b 2002 -B
efibootmgr -b 2003 -B
efibootmgr
BootCurrent: 0000
Timeout: 0 seconds
BootOrder: 0000,0001
Boot0000* Fedora
Boot0001* Fedora BKP
**IMPORTANT: **Test your new setup and sign your main kernel by reinstalling the kernel-core package:
yum -y reinstall kernel-core
You will notice that kernel install will take more time to finish in order to deliver a signed kernel to you.
Deploying secureboot keys
**NOTE: **If you are not able to create boot entries on your motherboard, you’ll need to rely on a live-usb efi of KeyTool, or move KeyTool to /efi/EFI/BOOT/BOOX64.EFI
and after that move your kernel back to that location.
Firstly, create a boot entry to KeyTool:
efibootmgr -c -d /dev/sda -p 1 --label "Keytool" -l "BOOT\KeyTool.efi" --verbose
efibootmgr
BootCurrent: 0000
Timeout: 0 seconds
BootOrder: 0000,0001,0002
Boot0000* Fedora
Boot0001* Fedora BKP
Boot0002* Keytool
Remember that we’ve already copied KeyTool.efi to /efi/BOOT/KeyTool.efi
in the first chapter.
Be sure Keytool will be the next thing to boot on your computer:
efibootmgr --bootorder 0002,0000,0001
You will need to be sure that Secure boot is in “setup mode” not “user mode”. I did this setup on 2 different laptop model/brands on my life: a Lenovo Ideapad 330 and an Acer Aspire VX(my current) and it was a guessing game on the Aspire.
With the Lenovo was just a matter of selecting “remove oem keys”, while with the Acer i had to not only do that but also cleanup TPM2 inside the firmware setup. The important thing here is that if you do something wrong, KeyTool
will warn you that firmware is not in setup mode and you will not be able to install certs on your firmware.
To copy your keyst to your ESP partition to keep things simple:
copy /root/keys/*.esl /efi/BOOT
copy /root/keys/*.auth /efi/BOOT
Reboot:
Now, add your keys following this order:
- Select the db entry, hit “Add new key”, and point to
DB.esl
- Select the kek entry, hit “Add new key” and point to
KEK.esl
- Finally, add the Platform Key(PK), select “Replace Keys” and point to
PK.auth
- Exit KeyTool and reboot
- Enable Secure Boot(depending on your hardware, this step will be automatically done)
You will probably receive a secureboot message warning as soon as you reboot, and that is of course because your KeyTool.efi
image isn’t signed. Enter your firmware and change your boot order.
As soon as you get a working environment, shred all those keys from your ESP:
shred /efi/BOOT/*.esl
shred /efi/BOOT/*.auth
Shred is a software that comes with coreutils
and it’s used for safe file removal.
Also, remove your Keytool entry and efi file:
efibootmgr -b 0002 -B
rm /efi/BOOT/KeyTool.efi
Done. Now install all the packages you want to have a pretty (and secure) desktop :)
Now what?
Security isn’t a “one pill” medicine. There’s plenty of room for other tools to create a more secure environment:
- Deploy
tpm2
boot policies to create a Security Violation if boot options are changed - Remove all undeeded boot entries
- Use
usbguard
to avoid tricky devices like those usb drivers that are actually HIDs under-the-hood - Setup a vpn client to use on public wifi hostpots you don’t trust much
- Create a fake windows installation partition before your root luks partition
Be safe!