Gentoo Forums
Gentoo Forums
Gentoo Forums
Quick Search: in
Installkernel with non-UKI self-kernels for secure boot
View unanswered posts
View posts from last 24 hours

 
Reply to topic    Gentoo Forums Forum Index Kernel & Hardware
View previous topic :: View next topic  
Author Message
nagmat84
Apprentice
Apprentice


Joined: 27 Mar 2007
Posts: 248

PostPosted: Sat Jun 15, 2024 11:56 am    Post subject: Installkernel with non-UKI self-kernels for secure boot Reply with quote

I am using rEFInd as my boot manger with self-signed kernel images with EFI stub support, an initramfs and secure boot with custom keys. Currently, generating and installing a new kernel is a somewhat tedious task, because I sign my kernel manually, move the files into the right place and so on.
I am looking for a way to streamline the entire task such that I only need to call
Code:
my-desktop /usr/src/linux # make oldconfig
my-desktop /usr/src/linux # make all
my-desktop /usr/src/linux # make install
and everything else happens "auto-magically". I hoped that "sys-kernel/installkernel" in combination with "sys-kernel/dracut" would be up to the job, but I cannot figure out how.

It appears that the culprit is conflict objectives. Either, I have to choose to use a UKI (unified kernel image) which is automatically signed by darcut but unfortunately also contains a static command line. Or I can choose to not use a UKI and keep the kernel image and initramfs separated, which allows me to define a kernel command line at runtime through the boot manager, but then the kernel is image is neither signed by dracut or installkernel.

I am looking for a bzImage with EFI stub support compiled-in which installlkernel signs (via sbsign), but without having a UKI.

Currently, my rEFInd configuration (/boot/EFI/Gentoo/refind_linux.conf) looks like
Code:
"Boot normally"             "ro root=/dev/nvme0n1p5 resume=/dev/nvme0n1p4 initrd=EFI/Gentoo/amd-uc.img initrd=EFI/Gentoo/initramfs-%v-gentoo.img init=/lib/systemd/systemd udev.log_priority=3 acpi_enforce_resources=lax splash quiet"
"Boot verbosely"            "ro root=/dev/nvme0n1p5 resume=/dev/nvme0n1p4 initrd=EFI/Gentoo/amd-uc.img initrd=EFI/Gentoo/initramfs-%v-gentoo.img init=/lib/systemd/systemd udev.log_priority=3 acpi_enforce_resources=lax splash"
"Boot to single-user mode"  "ro root=/dev/nvme0n1p5 resume=/dev/nvme0n1p4 initrd=EFI/Gentoo/amd-uc.img initrd=EFI/Gentoo/initramfs-%v-gentoo.img init=/lib/systemd/systemd udev.log_priority=3 acpi_enforce_resources=lax splash single"
"Boot to rescue mode"       "ro root=/dev/nvme0n1p5 resume=/dev/nvme0n1p4 initrd=EFI/Gentoo/amd-uc.img initrd=EFI/Gentoo/initramfs-%v-gentoo.img init=/lib/systemd/systemd udev.log_priority=5 acpi_enforce_resources=lax rescue"
"Boot to emergency mode"    "ro root=/dev/nvme0n1p5 resume=/dev/nvme0n1p4 initrd=EFI/Gentoo/amd-uc.img initrd=EFI/Gentoo/initramfs-%v-gentoo.img init=/lib/systemd/systemd udev.log_priority=5 acpi_enforce_resources=lax emergency"
This allows me to boot the same, signed bzImage-%v-gentoo.efi with different kernel command lines.

I read everything (I believe) in the Gentoo Wiki about MODULES_SIGN_KEY, MODULES_SIGN_CERT, MODULES_SIGN_HASH, SECUREBOOT_SIGN_KEY, SECUREBOOT_SIGN_CERT for make.conf and uefi_secureboot_key, uefi_secureboot_cert for /etc/dracut.conf. However, it seems that these variables only have an effect, if I also enable UKI support for dracut and set "uefi=true" in dracut.conf. However, then I end up with a UKI that also statically includes a signed kernel command line and I loose the ability to provide a command line via the boot manager.

I am looking for an option to tell installkernel to simply sign the bzImage and move it into the right directory.

Bonus question: If I do not tell dracut to create a UKI, dracut puts the final initramfs into /boot/initramfs-%v.img. However, the correct place for rEFInd would be /boot/EFI/Gentoo/initramfs-%v.img. Is there any configuration option to set the output directory for dracut?
Back to top
View user's profile Send private message
AndrewAmmerlaan
Developer
Developer


Joined: 25 Jun 2014
Posts: 314
Location: Nijmegen

PostPosted: Fri Jun 21, 2024 8:27 pm    Post subject: Reply with quote

I think app-crypt/sbctl may help you automatically sign the non-UKI kernel image, it has a kernel-install hook that if setup correctly should perform the signing when installing the kernel (works only with systemd flag enabled on sys-kernel/installkernel).

Note though that there is a very good reason to disallow overriding the cmdline at boot when secure boot is enabled. If you can modify the kernel cmdline then you can effectively circumvent secure boot (via loading custom initramfs, overriding init, or whatever other cool tricks)

The better way is to simply set the kernel cmdline you want in dracut.conf with the kernel_cmdline= setting.

Quote:
I read everything (I believe) in the Gentoo Wiki about MODULES_SIGN_KEY, MODULES_SIGN_CERT, MODULES_SIGN_HASH, SECUREBOOT_SIGN_KEY, SECUREBOOT_SIGN_CERT for make.conf and uefi_secureboot_key, uefi_secureboot_cert for /etc/dracut.conf. However, it seems that these variables only have an effect, if I also enable UKI support for dracut and set "uefi=true" in dracut.conf. However, then I end up with a UKI that also statically includes a signed kernel command line and I loose the ability to provide a command line via the boot manager.


Those *_*_{KEY,CERT} variables effect the distribution kernels, from your post it looks like you are configuring your own kernel, in this case nothing you set in make.conf will have any effect.
_________________
OS: Gentoo 6.8.10-gentoo-dist, ~amd64, 23.0/desktop/plasma/systemd
MB: MSI Z370-A PRO
CPU: Intel Core i9-9900KS
GPU: Intel Arc A770 16GB & Intel UHD Graphics 630
SSD: Samsung 970 EVO Plus 2 TB
RAM: Crucial Ballistix 32GB DDR4-2400
Back to top
View user's profile Send private message
nagmat84
Apprentice
Apprentice


Joined: 27 Mar 2007
Posts: 248

PostPosted: Tue Jun 25, 2024 6:43 pm    Post subject: Reply with quote

I found the solution. This post is divided in three parts. In part 1, I only present what needs to be done without much background information. In part 2, I explain some of the points. The final part 3, comments on some aspects of the previous post.

Part #1 - The Solution

This solution is only a brief summary of the settings which are necessary to automate installation and singing of your custom kernel with "make install". It does not explain how to set up secure boot, how to generate the keys, how to install them into the UEFI firmware, etc. Fore more information on that topic, seeThis brief summary assume that you have already a working secure boot and that you are able to sign custom kernels. This how-to only shows what needs to be done such that "make all && make modules_install && make install" inside the kernel source directory works without manual intervention.

/etc/portage/make.conf
Code:
SECUREBOOT_SIGN_KEY="/path/to/your/dbk/dbk-key.pem"
SECUREBOOT_SIGN_CERT="/path/to/your/dbk/dbk-cert.pem"


/etc/portage/package.use/kernel+fw
Code:
sys-apps/systemd -boot -kernel-install
sys-kernel/installkernel -* dracut
sys-kernel/linux-firmware -initramfs savedconfig
sys-firmware/intel-microcode hostonly -initramfs split-ucode


/etc/dracut.conf.d/99-local.conf
Code:
early_microcode=yes
hostonly=yes
hostonly_mode=strict
hostonly_cmdline=no
kernel_cmdline=""
uefi=no


/etc/kernel/install.conf
Code:
layout=efistub
initrd_generator=dracut
uki_generator=none


/etc/kernel/postinst.d/99-sign.install
Code:
#!/usr/bin/env bash

KERNEL_VERSION=${1}
KERNEL_IMAGE=${2}

# import variables from make.conf
source "/etc/portage/make.conf"

# familiar helpers, we intentionally don't use Gentoo functions.sh
die() {
        echo -e " ${NOCOLOR-\e[1;31m*\e[0m }${*}" >&2
        exit 1
}

einfo() {
        echo -e " ${NOCOLOR-\e[1;32m*\e[0m }${*}" >&2
}

ewarn() {
        echo -e " ${NOCOLOR-\e[1;33m*\e[0m }${*}" >&2
}

main() {
        # re-define for subst to work
        [[ -n ${NOCOLOR+yes} ]] && NOCOLOR=

        [[ -f ${KERNEL_IMAGE} ]] || die "Kernel image does not exist: \"${KERNEL_IMAGE}\""
        [[ -f ${SECUREBOOT_SIGN_KEY} ]] || die "Signature key does not exist: \"${SECUREBOOT_SIGN_KEY}\""
        [[ -f ${SECUREBOOT_SIGN_CERT} ]] || die "Signature certificate does not exist: \"${SECUREBOOT_SIGN_CERT}\""

        einfo "Signing ${KERNEL_IMAGE} ..."
        sbsign --key "${SECUREBOOT_SIGN_KEY}" --cert "${SECUREBOOT_SIGN_CERT}" "${KERNEL_IMAGE}" || die "Could not sign \"${KERNEL_IMAGE}\""
        [[ -f ${KERNEL_IMAGE}.signed ]] || die "Signed kernel image does not exist: \"${KERNEL_IMAGE}.signed\""
        mv -f "${KERNEL_IMAGE}.signed" "${KERNEL_IMAGE}"
        sbverify --cert "${SECUREBOOT_SIGN_CERT}" "${KERNEL_IMAGE}" || die "Could not verify signature"
}

main


/usr/src/linux/.config
Code:
CONFIG_HAVE_KERNEL_LZ4=y
CONFIG_EFI_STUB=y
CONFIG_MODULE_SIG_FORMAT=y
CONFIG_MODULE_SIG=y
CONFIG_MODULE_SIG_FORCE=y
CONFIG_MODULE_SIG_ALL=y
CONFIG_MODULE_SIG_SHA256=y
CONFIG_MODULE_SIG_HASH="sha256"
CONFIG_MODULE_SIG_KEY="/path/to/your/dbk/dbk-chain.pem"
CONFIG_MODULE_SIG_KEY_TYPE_RSA=y


Ensure that the directory "/boot/EFI/Gentoo" exists, i.e. run "mkdir /boot/EFI/Gentoo". Note, this assume that you have only a single ESP partition which is mounted at "/boot". REFInd is installed at "/boot/EFI/refind" and Gentoo kernels go into "/boot/EFI/Gentoo".

/boot/EFI/Gentoo/refind_linux.conf
Code:
"Boot normally"             "udev.log_priority=2 splash quiet loglevel=2 systemd.show_status=false"
"Boot verbosely"            "udev.log_priority=3 splash"
"Boot to single-user mode"  "udev.log_priority=3 splash single"
"Boot to rescue mode"       "udev.log_priority=5 rescue"
"Boot to emergency mode"    "udev.log_priority=5 emergency"


Part #2 - Some Explanations

  • /etc/portage/make.conf: This sets the variables with the path to the key and certificate. These variables are used by some Gentoo scripts but also by our custom script /etc/kernel/postinst.d/99-sign.install.
  • /etc/portage/package.use/kernel+fw:
    • Do not install systemd-boot (formerly gummiboot) and do not install the installkernel script from systemd
    • Gentoo installkernel script shall only use dracut to generate an initramfs; in particular do not enable efistub (because that requires systemd-boot, we use rEFInd as the boot manager and the EFI stub support by the kernel itself); do not enable uki or ukify, because we want a separate initramfs and kernel image
    • Disable initramfs to avoid loitering /boot with small, independent initramfs for microcode, microcode support is built in into the "full" initramfs by dracut
  • /etc/dracut.conf.d/99-local.conf:
    • Include early microcode into the only initramfs (we don't use separate initramfs for that, see above)
    • Ensure that no command line is built into the initramfs; both parameters are required to make this really work
    • Don't build a unified kernel image (uefi=no)
  • /etc/kernel/install.conf:
    • Layout "efistub" installs the initramfs and kernel image into "/boot/EFI/Gentoo". The directory name is taken from "NAME" inside "/etc/os-release" and the directory must exist. (Otherwise the files will end up in "/boot").
    • Use dracut to build the initramsf
    • Don't create a unified kernel image
    /etc/kernel/postinst.d/99-sign.install: This script is invoked by "installkernel" after the initramfs and the kernel image has already been moved to "/boot/EFI/Gentoo". The script receives the to kernel image as the second command line parameter. The script sources "/etc/portage/make.conf" and signs the kernel image.
  • /usr/src/linux/.config: No surprises here, see the linked tutorials above for an in-depth explanation. This kernel options enforce that modules must be properly signed for loading and that in-tree modules are signed as part of the build procedure.
  • /boot/EFI/Gentoo/refind_linux.conf: Provide four different command line options for our kernel. This shortened command line rely on two aspects: a) The initramfs must have the same basename as the kernel; then rEFInd will autoatically add the correct initrd= command line argument; b) auto-discoverable partition layout and partition UUIDs, that is why no root=, resume=, etc. are necessary.


Part #3 - Responses to previous post

Quote:
Note though that there is a very good reason to disallow overriding the cmdline at boot when secure boot is enabled. If you can modify the kernel cmdline then you can effectively circumvent secure boot (via loading custom initramfs, overriding init, or whatever other cool tricks)
I don't deny that. But it also very inconvenient to have several large kernel images if the only thing you want to change is the verbosity of the boot process. There is on open issue on that at https://github.com/systemd/systemd/issues/24539. The general idea is to include several pre-determined ".cmdline" entries into a single UKI such that one can select on of those at boot time. For the time being, I keep it that way.

Quote:
Those *_*_{KEY,CERT} variables effect the distribution kernels, from your post it looks like you are configuring your own kernel, in this case nothing you set in make.conf will have any effect.
Not entirely, correct. The *_*_{KEY,CERT} variables in /etc/portage/make.conf also affect custom build kernels, but only if dracut is configured to generate a UKI (i.e. dracut.conf: uefi=true). If dracut builds a UKI, then dracut uses those variables to sign the UKI, even if the kernel is a self-configured kernel.
Back to top
View user's profile Send private message
AndrewAmmerlaan
Developer
Developer


Joined: 25 Jun 2014
Posts: 314
Location: Nijmegen

PostPosted: Tue Jun 25, 2024 7:13 pm    Post subject: Reply with quote

Quote:
efistub (because that requires systemd-boot, we use rEFInd as the boot manager and the EFI stub support by the kernel itself)


That's not quite corrrect, efistub does not require any bootloader, it is the bootloader. Enabling the "efistub" flag on 'sys-kernel/installkernel' as well as the dracut flag will result in the same install.conf that you also set manually.

The logic in installkernel is that "efistub" means use the kernel's own vanilla efi stub loader, and "uki" means use systemd's sytemd-stub loader. The latter does indeed require the "systemd" flag, but not the "systemd-boot" flag.

Quote:
Not entirely, correct. The *_*_{KEY,CERT} variables in /etc/portage/make.conf also affect custom build kernels, but only if dracut is configured to generate a UKI (i.e. dracut.conf: uefi=true). If dracut builds a UKI, then dracut uses those variables to sign the UKI, even if the kernel is a self-configured kernel.


No, there is code in the dracut installkernel hook that references those variables, but when the plugin is actually run those variables are only populated if '/sbin/installkernel' is called via emerge.
'source "/etc/portage/make.conf"' is a nice workaround for that though.
_________________
OS: Gentoo 6.8.10-gentoo-dist, ~amd64, 23.0/desktop/plasma/systemd
MB: MSI Z370-A PRO
CPU: Intel Core i9-9900KS
GPU: Intel Arc A770 16GB & Intel UHD Graphics 630
SSD: Samsung 970 EVO Plus 2 TB
RAM: Crucial Ballistix 32GB DDR4-2400
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    Gentoo Forums Forum Index Kernel & Hardware All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum