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

Attachment: signature.asc
Description: PGP signature

Reply via email to