Translation(s): none
UKIs (Unified Kernel Images) are UEFI PE binaries that embed everything needed to boot a system. They can be launched from the UEFI firmware, or from a first stage or second stage bootloader.
They provide several benefits over a traditional split-artifacts approach:
- All the content, including the initrd, is covered by the PE signature and verified for trust
- They are self-describing, so boot manager do not need auxiliary configuration and can simply generate menu items with all the required details from their content
- They provide support for TPM2 PCR policy signature-based unsealing
At its very basic, a UKI is created by combining systemd-stub, kernel, cmdline, and initrd into one single EFI binary, and signing it for SecureBoot. There are various ways to build a UKI, remotely and locally, and the rest of this document will cover the available options. Users can evaluate what their preferred option is.
Contents
Build locally with systemd-ukify and initramfs-tools
systemd's ukify provides an easy way to generate unified kernel images. Ensure systemd-boot-efi is installed, and sbsigntool for signing:
1 apt install systemd-ukify-efi sbsigntool
The default initrd built by initramfs-tools will be used for the UKI. This will build the UKI:
1 dpkg-reconfigure linux-image-$(uname -r)
Out of the box, the image is not signed, which will cause problems with SecureBoot enabled machines. To build and sign the image automatically with every kernel upgrade and initrd generation, create these two scripts:
/etc/kernel/postinst.d/zz-ukify:
1 #!/bin/bash
2 set -e
3
4 /usr/lib/systemd/ukify build \
5 --linux="$2" \
6 --initrd="/boot/initrd.img-$1" \
7 --cmdline="replace with your cmdline" \
8 --splash="/path/to/splash.bmp" \ # Remove this line if you don't want one
9 --output="/boot/efi/EFI/Linux/debian.efi"
10
11 # Add this line if you want to sign the image for secure boot
12 sbsign --key /path/to/db.key --cert /path/to/db.crt --output /boot/efi/EFI/Linux/debian.efi /boot/efi/EFI/Linux/debian.efi
/etc/initramfs/post-update.d/zz-ukify:
1 #!/bin/bash
2 set -e
3
4 /usr/lib/systemd/ukify build \
5 --linux="/boot/vmlinuz-$1" \
6 --initrd="$2" \
7 --cmdline="replace with your cmdline" \
8 --splash="/path/to/splash.bmp" \ # Remove this line if you don't want one
9 --output="/boot/efi/EFI/Linux/debian.efi"
10
11 # Add this line if you want to sign the image for secure boot
12 sbsign --key /path/to/db.key --cert /path/to/db.crt --output /boot/efi/EFI/Linux/debian.efi /boot/efi/EFI/Linux/debian.efi
Note that those files *look* the same, but they are slightly different. They also need to be tweaked to use your enrolled SecureBoot keys.
Make them executable, create the destination folder, and update the initrd to create the first image:
chmod +x /etc/kernel/postinst.d/zz-ukify chmod +x /etc/initramfs/post-update.d/zz-ukify mkdir -p /boot/efi/EFI/Linux update-initramfs -u
Build locally with mkosi-initrd
A UKI can provide many more features than what can be obtained by assembling it manually from Debian's initramfs-tools initrd. Using mkosi it is possible to set up local builds of UKIs that assemble the initrd using standard Debian packages, instead of the initramfs-tools or dracut local scripts. Among other things, this also sets up the UKI with a corresponding signed TPM2 policy, so that secrets (e.g.: full disk encryption with systemd-cryptenroll) can be sealed against the public key signing the image, rather than raw values that change on every build.
Install the required packages:
apt install mkosi systemd-boot-efi
Set up the kernel-install configuration to make it use mkosi-initrd:
This is enough to get an initrd with the default configuration, that will match the content of the local host in terms of kernel modules being loaded. A kernel command line can be added via /etc/kernel/cmdline, for example to configure a rootfs on a BTRFS subvolume and plymouth:
In case additional packages are required, for example any firmware package, a mkosi-initrd configuration can be also added. This can be extended at will, including any desired extra packages, a root password for emergency shell, etc.
For example, to add plymouth, firmware for Atheros wifi cards and the kernel module for erofs:
A /etc/mkosi-initrd/mkosi.extra/ directory can also be created, and any file/directory under it will be added to the root of the generated initrd.
To sign the UKI and its PCR policies the means to access a signing key must be given, for a plaintext key (not recommended):
It is better for security to store the secret key on a hardware token, but be aware that this means interactive builds are required, so automated generation of the UKI will need to be disabled (along with unattended-upgrades). For example, if the key can be used via PKCS11:
1 mkdir -p /etc/mkosi-initrd/
2 cat <<EOF >>/etc/mkosi-initrd/mkosi.conf
3 [Validation]
4 SecureBoot=yes
5 SecureBootKeySource=provider:pkcs11
6 SecureBootCertificateSource=provider:pkcs11
7 SecureBootKey=pkcs11:<PKCS%%URI>
8 SecureBootCertificate=pkcs11:<PKCS%%URI>
9 SignExpectedPcrKeySource=provider:pkcs11
10 SignExpectedPcrCertificateSource=provider:pkcs11
11 SignExpectedPcrKey=pkcs11:<PKCS%%URI>
12 SignExpectedPcrCertificate=pkcs11:<PKCS%%URI>
13 EOF
For SecureBoot, the public key needs to be enrolled in MOK, following the same steps detailed elsewhere on this page.
Finally, when the configuration is in the desired state, build UKIs for all available kernels:
for k in /boot/vmlinu[xz]-*; do \ [ -f "$k" ] || continue; \ ver=$(basename "$k" | sed s/^vmlinu[xz]-//); \ kernel-install add "$ver" "$k"; \ done
Install a pre-built UKI
The upstream systemd project has started offering pre-built UKIs for some combinations of distributions and architectures, built and signed on the SUSE Open Build Service. These are NOT official Debian images. They are built using the latest stable branch of systemd. amd64 Trixie and testing UKIs are currently offered.
These are NOT OFFICIAL Debian images, and are experimental, use at your own risk
1 mkdir -p /etc/sysupdate.d/
2 cat <<EOF >>/etc/sysupdate.d/70-upstream-UKI.transfer
3 [Source]
4 Type=url-file
5 Path=https://download.opensuse.org/repositories/system:/systemd:/stable/debian_13_images/
6 MatchPattern=debian-trixie_@v_%a.efi
7
8 [Target]
9 Type=regular-file
10 Path=/EFI/Linux
11 PathRelativeTo=boot
12 MatchPattern=debian-trixie_@v_%a+@l-@d.efi \
13 debian-trixie_@v_%a+@l.efi \
14 debian-trixie_@v_%a.efi
15 Mode=0644
16 TriesLeft=3
17 TriesDone=0
18 InstancesMax=2
19 EOF
Due to a bug in systemd-sysupdate at the time of writing, it is necessary to add a workaround to avoid the currently running UKI from being removed to make space for updates:
The key that signs the shasum needs to be added to the importd keyring:
1 wget https://build.opensuse.org/projects/system:systemd:stable/signing_keys/download?kind=gpg -O- | gpg --dearmor > /etc/systemd/import-pubring.gpg
If SecureBoot is enabled (recommended!) the signing key needs to be imported in DB or MOK:
After these setup steps are completed, systemd-sysupdate can be ran and it will fetch and install the UKI, and keep it up to date when configured to run on a timer:
1 systemctl enable --now systemd-sysupdate.timer
GNOME 50 will also include automated support for sd-sysupdate via the GNOME Software program.
If customizations to the kernel command line and/or initrd are necessary, it is recommended to build local addons with mkosi-addon and place them under the global addon directory in the ESP: /boot/efi/loader/addons/ If SecureBoot is used, self-signing can be done, and remember to enroll the key in MOK.
The build process for these UKIs happens on OBS: https://build.opensuse.org/package/show/system:systemd/uki-debian Manifest files are provided next to the images, listing the full content, and build logs can be accessed to be checked too, the build tool is again mkosi Users familiar with OBS can branch that package and customize the build for their own needs, and adjust the systemd-sysupdate configuration, URLs, keys, etc accordingly.
See also
