NMBL: UKI + full disk encryption + Secure Boot

Benefits:
No bootloader (best security and speed) with a normal fallback bootloader and snapshots, etc.

Purpose:
Educational + security (paranoid level :wink:).

Caution:
Backup your data! Keep a live USB nearby.

Based on:

Inspired by:

Tested on:
Single-boot setups and:

  • default GRUB + Btrfs + encryption

  • systemd-boot + F2FS + encryption

(since GRUB fails with F2FS when disabling the shim lock)

Since the GRUB setup keeps /boot inside /, there is no problem with limited space on/efi even with many snapshots.

In the default CachyOS setup with GRUB and full disk encryption, only the GRUB bootloader is exposed to attack, as the config, kernel, initramfs, and the rest are encrypted on the LUKS1 partition. GRUB has a large and old codebase with some open CVEs, so it is potentially exploitable even when signed by Secure Boot.

In the default systemd-boot + LUKS2 (+ Secure Boot) setup, the initramfs and config are not protected (they reside on /EFI, are not signed by Secure Boot, and are not encrypted). The bootloader code itself is safer than GRUB due to its simplicity.

LUKS1 is less secure than LUKS2, but with a long password (10+ characters), the energy cost required to brute-force it is astronomical.

To increase security, we can create a Unified Kernel Image (kernel + initramfs + command line), sign it with Secure Boot, and load it directly from UEFI.
No bootloader = smaller attack surface and slightly faster boot. All the code is protected by Secure Boot.

If we keep a bootloader present, we can still use its benefits (e.g. booting into snapshots, changing the command line, multiple kernels—though we can also create multiple UKIs) in case something fails, while being aware of the potential insecurity.

The BIOS needs to have a user password set to prevent disabling Secure Boot or booting from USB.

First, configure Secure Boot according to the CachyOS wiki:

systemd-boot requires an additional step: signing the bootloader itself.

Then…

1. mkinitcpio configuration

Edit /etc/mkinitcpio.conf and change:

FILES=(/crypto_keyfile.bin)

to:

FILES=()

We don’t want to include the keyfile for our LUKS partition in the UKI, because it would be stored on a plaintext partition and anyone could access the data.

Consider changing HOOKS (mkinitcpio - ArchWiki). This is not strictly necessary—be careful.
I wanted visible boot messages and a smaller UKI, so I removed:

  • base (not necessary for systemd; needed for udev)

  • kms

  • plymouth

  • fsck (not needed for Btrfs)

My current hooks:

HOOKS=(systemd autodetect microcode modconf block keyboard sd-vconsole sd-encrypt filesystems)

My current UKI size is ~134 MB, so it’s possible to store two UKIs plus GRUB on a default 300 MB /EFI partition.

There was a change in the default install setup some time ago (udev → systemd, encrypt → sd-encrypt, etc.) along with corresponding kernel command-line changes!

Test this on one profile first. If something goes wrong, use the LTS kernel to repair:

sudo mkinitcpio -p linux-cachyos

and reboot.

For GRUB, the password will be requested twice at this stage (no keyfile).

2. UKI preset

Edit /etc/mkinitcpio.d/linux-cachyos.preset and add/edit/uncomment:

For GRUB:

default_uki=“/boot/efi/EFI/BOOT/BOOTX64.EFI”

For systemd-boot:

default_uki=“/boot/EFI/BOOT/BOOTX64.EFI”

This is a fallback path. When booting from a drive and no other bootloader is present, UEFI uses it. In our case, a bootloader with enabled shim lock already exists from installation time, so the bootloader itself is mostly useless with Secure Boot enabled, and the path can still be utilized.

Consider using another path, e.g.:

default_uki=“/boot/efi/EFI/cachyosuki/cachyos.efi”

Adjust the path and create the directory beforehand:

sudo mkdir -p /boot/efi/EFI/cachyosuki/

3. Kernel command line

Check the current kernel command line:

cat /proc/cmdline

Edit / check (or create, for GRUB) /etc/kernel/cmdline and fill it with the contents of /proc/cmdline.

My: root=UUID=xxxx rw rootflags=subvol=@ nowatchdog nvme_load=YES zswap.enabled=0 rd.luks.uuid=yyy loglevel=3
(and lsm=landlock,lockdown,yama,integrity,apparmor,bpf for apparmor)

Removed: BOOT_IMAGE=/@/boot/vmlinuz-linux-cachyos rd.luks.key=/crypto_keyfile.bin as they are not needed, but they can be kept.

4. Regenerate initramfs / UKI

sudo mkinitcpio -P

5. UEFI entries

Check UEFI entries:

efibootmgr

Add entries as required and adjust the boot order.

Example for /dev/nvme0n1, EFI on the first partition (strip /boot/efi for GRUB and /boot for systemd-boot):

Fallback path:

sudo efibootmgr -c -d /dev/nvme0n1 -p 1 -L "CachyOsUKI" -l 'EFI/boot/BOOTX64.EFI' --unicode

Custom path:

sudo efibootmgr -c -d /dev/nvme0n1 -p 1 -L "CachyOsUKI" -l '/EFI/cachyosuki/cachyos.efi' --unicode

This should add the entry and set it as first.

  • To reorder: sudo efibootmgr -o xx,yy,zz ...
  • To delete: sudo efibootmgr -b xx -B

On one of my laptops, entries are sometimes removed after a Secure Boot failure. Then I manually select the UKI or GRUB file from the firmware menu, boot, and reinstall them:

sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=cachyos --modules="tpm" --disable-shim-lock

This adds the entry after install.

sudo efibootmgr -c -d /dev/nvme0n1 -p 1 -L "CachyOsUKI" -l 'EFI/boot/BOOTX64.EFI' --unicode

Set it as first.

sudo sbctl-batch-sign

(to sign GRUB; the rest should already be signed by hooks)

6. Optional: LTS UKI

Consider adding an LTS UKI by configuring /etc/mkinitcpio.d/linux-cachyos-lts.preset in the same way.

And enjoy 0.5–6 s shorter boot time :slightly_smiling_face: