success

Unified Kernel Image

Read Arch Wiki for details.

You can generate a UKI via mkinitcpio, first edit its linux.preset config file:

# mkinitcpio preset file for the 'linux' package

#ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-linux"
ALL_microcode=(/boot/*-ucode.img)

PRESETS=('default' 'fallback')

#default_config="/etc/mkinitcpio.conf"
default_image="/boot/initramfs-linux.img"
default_uki="/boot/EFI/archuki/arch.efi"
default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"

#fallback_config="/etc/mkinitcpio.conf"
fallback_image="/boot/initramfs-linux-fallback.img"
#fallback_uki="/efi/EFI/Linux/arch-linux-fallback.efi"
fallback_options="-S autodetect"

You will need to change default_uki path to a location under your EFI directory, where GRUB's EFI binary resides. Make sure the directory exists when running mkinitcpio.

Write kernel parameters in /etc/kernel/cmdline:

loglevel=3 quiet splash reboot=acpi cryptdevice=UUID=<your cryptdisk UUID>:arch_root root=UUID=<your root UUID> rw

Note I have encrypted my entire system root, if you haven't done that I recommend doing so immediately.

# create dir for UKI
mkdir -p /boot/EFI/archuki

# regenerate initramfs
mkinitcpio -p linux

# or you can reinstall `linux`
# `mkinitcpio` will run automatically
pacman -Sy linux

Check if /boot/EFI/archuki/arch.efi exists. If not, something has gone wrong.

Now we need to add this EFI binary as a boot entry

# assuming EFI partition is /dev/nvme1n1p1
uki_path='\EFI\archuki\arch.efi' # match the one above
esp_disk=/dev/nvme1n1
part=1

# create entry
efibootmgr | grep 'Arch Linux' || efibootmgr --create --disk "$esp_disk" --part "$part" --label "Arch Linux" --loader "$uki_path" --unicode

Setup Secure Boot

First you need to enter BIOS settings, and set secure boot to Setup Mode so you can enroll your own keys.

Then use sbctl to do the job.

I wrote a script to automate the process:

#!/bin/bash

# run as root
[[ "$EUID" -eq 0 ]] || exec sudo bash "$0" "$@"

# EFI partition
esp_disk=/dev/nvme1n1
part=1
uki_path='\EFI\archuki\arch.efi'

# install packages
command -v efi-readvar || sudo pacman -Sy efitools
command -v pesign || sudo pacman -Sy pesign
command -v sbsign || sudo pacman -Sy sbsigntools
command -v sbctl || sudo pacman -Sy sbctl

# check status
sbctl status

# create keys
[[ -f /usr/share/secureboot/keys/PK/PK.key ]] || sbctl create-keys

# enroll keys
sbctl status | grep 'Setup Mode' | grep 'Disabled' || {
    echo "[+] Enrolling keys"
    sbctl enroll-keys -m
}

# check status again to make sure keys are enrolled
echo "[+] Checking status"
sbctl status

# sign
for f in $(sbctl verify | grep 'not signed' | awk '{print $2}'); do
    echo "[+] Signing $f"
    sbctl sign -s "$f"
done

# sign modules
key="/usr/share/secureboot/keys/db/db.key"
cert="/usr/share/secureboot/keys/db/db.pem"

for kernel_dir in /usr/lib/modules/*; do
    kernel="$(basename "$kernel_dir")"
    echo "[+] Signing modules for $kernel"
    signer="/usr/lib/modules/$kernel/build/scripts/sign-file"
    mod_dir="/lib/modules/$kernel/extramodules"
    [[ -f "$signer" ]] && {
        for mod in "$mod_dir/"*; do
            echo "[+] Signing module $mod"
            modinfo "$mod" | grep 'Database Key' || {
                xz -d "$mod"
                mod_decompressed="${mod%.*}"
                "$signer" sha256 "$key" "$cert" "$mod_decompressed"
                xz "$mod_decompressed"
            }
        done
    }
done

# check status again to make sure all files are signed
sbctl verify

# add EFI to boot entry
efibootmgr | grep 'Arch Linux' || efibootmgr --create --disk "$esp_disk" --part "$part" --label "Arch Linux" --loader "$uki_path" --unicode

Reboot and enable secureboot in BIOS, set Arch Linux as your first boot entry.


Comments

comments powered by Disqus