On Mon, Aug 04, 2025 at 12:35:29AM +0000, Ada Avery wrote: > Hey folks! > > I have implemented support for booting Guix System using systemd-boot. I'd > like to get it upstreamed, but I want to see if there is interest and whether > my general approach is agreeable before I put in the work required to make a > clean patch ready for review. If this is the sort of thing you think belongs > in Guix, please let me know. I'd like to offer some thanks to Lilah > Tascheter for code and advice that she provided to help get me started > hacking on Guix. My systemd-boot implementation builds on top of her work. > > Below I have included some background context and a justification for why I > want to use systemd-boot with Guix. After that you'll find a high-level > description of my approach. You can find my implementation in my channel at > https://git.squircle.space/gooy-guix.git/ in (gnu bootloader systemd). > However, please remember that I do not feel that the implementation in my > channel is ready to be submitted in a PR. I have a number of cleanup tasks I > need to complete first. > > * Why systemd-boot? > > I have been working on extending Guix's family of bootloaders to accomplish > two objectives. > > First, I want to improve Guix's support for full disk encryption. The > limiting factor here is GRUB. grub-bootloader keeps the kernel and initrd in > /gnu/store and thus needs to mount the filesystem containing the store before > it can boot. GRUB only supports a subset of the features of the full LUKS > implementation in Linux, and even for the configurations that GRUB does > support I have found its implementations are painfully slow. In my opinion, > the best solution is to put the kernel and initrd on an unencrypted > filesystem. That ensures that when it comes time to decrypt the root > filesystem we will have access to a full-featured implementation for LUKS. > > Second, I want Guix to support secure boot using MOK (machine owner keys). > On systems where the device owner is able to register new keys with the UEFI, > they are able to sign their bootloader and related files and have the UEFI > validate the signature before booting. This protects the user against > attacks that attempt to manipulate the booatloader or kernel. It isn't > complete protection against an evil-main-style attack, but it definitely > increases the sophistication required to pull off that attack. > > I believe that the best way to accomplish both of these is to add > systemd-boot as a bootloader option in Guix. > > Lilah implemented a bootloader that accomplished both of my objectives in > https://issues.guix.gnu.org/68524. Her initial implementation worked by > generating "UKI"s (Unified Kernel Images) and registering them as boot > options in the UEFI. A UKI is a binary file that contains a kernel, an > initrd, and a small UEFI bootloader "stub". The stub makes the binary into a > bootloader of sorts that the UEFI can directly boot into. Reviewers pointed > out that Lilah's implementation did not support `guix system rollback`, and > so Lilah created a followup patchset that refactored the Guix bootloader > machinery to allow her UEFI UKI bootloader to correctly handle rollbacks. > Herman Rimm contributed to this, but it seems like that effort stalled out. > See these threads for more info. > - https://issues.guix.gnu.org/73202 > - https://lists.gnu.org/r/guix-devel/2024-02/msg00228.html > - https://mail.gnu.org/archive/html/guix-patches/2024-08/msg00309.html > > The reason Lilah's first implementation didn't support rollback is fairly > simple. Guix's bootloader handling has two phases. First, Guix generates a > bootloader-specific "configuration file" and copies it onto the device that > will contain the bootloader. The logic for generating the bootloader > configuration file is defined by the configuration-file-generator slot on the > bootloader record, but the generator function must return a derivation that > outputs a single file. Guix's bootloader infrastructure copies the > configuration file into its appropriate destination. This happens during > `guix system reconfigure` and `guix system rollback`. In the second phase, > Guix calls the function in the installer slot of the bootloader record. This > function is expected to, well, install the bootloader onto the target device. > Unlike the configuration file generator, the installer is allowed to write > to the installation target freely. However, the installer is *not* called > during `guix system rollback`. Lilah needed to generate UKIs and configure > the UEFI during the installer phase since each UKI needs to be individually > copied onto the target disk. Since the installer isn't run during rollbacks, > her bootloader couldn't reconfigure the UEFI and change the boot order. > > To make a UKI-based boot work with Guix's current bootloader system, we need > to make it so that changing the configuration file is sufficient to change > the boot order. This means we cannot rely on directly configuring the UEFI > to alter boot order. We need a bootloader that is responsible for picking > the right UKI to boot after consulting a configuration file. > > Traditionally, GRUB fills this role. We could imagine generating UKIs and > then using GRUB's chainloader feature to make boot entries for them. I > believe this was proposed by one of the reviewers on Lilah's original patch. > In theory, this would work! However, I chose to use systemd-boot as my > bootloader. Since my objective is to enable secure-boot, I wanted a > dead-simple bootloader that has minimal surface area for attack. It should > be impossible to trick the bootloader into booting something that isn't > properly signed. GRUB is an incredibly impressive piece of software, but I > don't think anyone would describe it as simple. It is extremely > feature-rich, and unfortunately features mean complexity and attack surface > area. By contrast, systemd-boot is specifically designed to have minimal > functionality, and supporting secure boot is a first class feature of the > bootloader. For my purposes, systemd-boot is the right bootloader to use. > > If you have concerns about systemd and dependencies, worry not. systemd-boot > is a standalone part of systemd. Using systemd-boot does not require any > other systemd components to be installed into the OS. We only need the EFI > stub binary, bootctl (a tool for installing systemd-boot), and a Python > script named ukify. > > * How systemd-bootloader works > > The basic strategy I used is as follows. I defined a new bootloader instance > > (define-public systemd-bootloader > (bootloader > (name 'systemd-bootloader) > ... > (installer install-systemd-boot) > (configuration-file "/boot/efi/loader/loader.conf") > (configuration-file-generator systemd-bootloader-conf-generator))) > > systemd-bootloader-conf-generator creates a loader.conf for systemd-boot > which names the UKI that should be booted by default. I also embed some > metadata in loader.conf that describes how to generate the UKIs described by > the system's bootloader-configuration-menu-entries. install-systemd-boot > installs systemd-boot and then reads the metadata embedded in loader.conf and > produces the needed UKIs in /boot/efi/EFI/Linux. If you're familiar with > Lilah's install-uki.scm approach, this should seem familiar. Unlike Lilah's > approach, the configuration file contains data about how to generate the UKIs > rather than executable code which will produce them. > > Here is an example of what the loader.conf contains. First, it sets options > that systemd-boot checks for. Namely, default and timeout. The default > option names a UKI file that will be generated during bootloader > installation. The UKI file names include a hash of the components they are > made from (label, kernel, initrd, kernel arguments). For brevity I have > truncated hashes. > > timeout 5 > default guix-abcdef...efi > > After those fields, there are a series of lines that contain the metadata > required to generate the UKIs from the system's > bootloader-configuration-menu-entries. systemd-boot treats lines starting > with # as comments, so we can easily embed our metadata without conflicting > with systemd-boot's interpretation of the file. Here's an example of what > one of those lines might look like > > # GUIX-ENTRY: (ukify "menu-entry-label goes here" > "/gnu/store/abcdef...-kernel/bzImage" "/gnu/store/abcdef...-initrd" > "kernel.arguments=go_here") > > When we run install-systemd-boot, it scans through loader.conf looking for > lines that start with "# GUIX-ENTRY: ". We then read in the sexp that > follows on the line. The output filename is computed via hashing the 4 > components, and if the UKI doesn't exist, it is generated using the data > contained in the sexp. One of the (ukify ...) sexps will correspond to the > .efi binary named in the `default` option earlier in the file. Any > guix-*.efi files in /EFI/Linux that don't have corresponding entries in > loader.conf are deleted. > > When a system rollback occurs, guix only regenerates loader.conf. This is > fine since the UKI for the old system version will still be in /EFI/Linux. > All we need to do is change which file is named in the `default` setting. > > * Conclusion > > Again, if you think this is the sort of thing that should be upstreamed into > Guix, please let me know. I'm happy to put in the effort to adapt my work > into a form suitable for merging into Guix, but I don't want to spend my time > on that unless y'all are actually interested in merging something like this. > > Thanks! > -Ada
I see that there is support for more than just x86_64, so that answered my question regarding aarch64/riscv64. I don't personally see a reason why it couldn't be in Guix. I think it would be a nice addition, to support secure-boot. -- Efraim Flashner <efr...@flashner.co.il> אפרים פלשנר GPG key = A28B F40C 3E55 1372 662D 14F7 41AA E7DC CA3D 8351 Confidentiality cannot be guaranteed on emails sent or received unencrypted
signature.asc
Description: PGP signature