]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Change UnifiedKernelImages to enum and accept signed/unsigned
authorLuca Boccassi <luca.boccassi@gmail.com>
Thu, 17 Jul 2025 01:16:18 +0000 (02:16 +0100)
committerJörg Behrmann <behrmann@physik.fu-berlin.de>
Tue, 22 Jul 2025 15:49:21 +0000 (17:49 +0200)
With custom firmware we enroll our keys in db, so local UKIs can be
built and there's no need to fail the build. Many distributions
ship signed bootloaders, but they still don't ship UKIs.
Add an enum and a parser (to keep backward compat), and if set to
unsigned build locally instead of failing when the bootloader is
signed.

mkosi/__init__.py
mkosi/config.py
mkosi/resources/man/mkosi.1.md
tests/test_json.py

index 415e2b6a040c9c084fc59d87f0af272b239dd221..1ebb627c7cb77416a67b3087885eed1fabc5296f 100644 (file)
@@ -66,6 +66,7 @@ from mkosi.config import (
     SecureBootSignTool,
     ShimBootloader,
     Ssh,
+    UnifiedKernelImage,
     Verb,
     Verity,
     Vmm,
@@ -1894,9 +1895,9 @@ def systemd_stub_version(context: Context, stub: Path) -> Optional[GenericVersio
 def want_uki(context: Context) -> bool:
     return want_efi(context.config) and (
         context.config.bootloader.is_uki()
-        or context.config.unified_kernel_images == ConfigFeature.enabled
+        or context.config.unified_kernel_images.enabled()
         or (
-            context.config.unified_kernel_images == ConfigFeature.auto
+            context.config.unified_kernel_images == UnifiedKernelImage.auto
             and systemd_stub_binary(context).exists()
             and context.config.find_binary("ukify", "/usr/lib/systemd/ukify") is not None
         )
@@ -2133,7 +2134,10 @@ def install_uki(
     with umask(~0o700):
         boot_binary.parent.mkdir(parents=True, exist_ok=True)
 
-    if context.config.bootloader.is_signed():
+    if (
+        context.config.bootloader.is_signed()
+        and context.config.unified_kernel_images == UnifiedKernelImage.auto
+    ) or context.config.unified_kernel_images == UnifiedKernelImage.signed:
         for p in (context.root / "usr/lib/modules" / kver).glob("*.efi"):
             log_step(f"Installing prebuilt UKI at {p} to {boot_binary}")
             shutil.copy2(p, boot_binary)
@@ -2403,7 +2407,7 @@ def copy_nspawn_settings(context: Context) -> None:
 
 
 def get_uki_path(context: Context) -> Optional[Path]:
-    if not want_efi(context.config) or context.config.unified_kernel_images == ConfigFeature.disabled:
+    if not want_efi(context.config) or context.config.unified_kernel_images == UnifiedKernelImage.none:
         return None
 
     ukis = sorted(
@@ -2807,7 +2811,7 @@ def check_tools(config: Config, verb: Verb) -> None:
                 reason="build unified kernel image profiles",
                 hint=("Use ToolsTree=default to download most required tools including ukify automatically"),
             )
-        elif want_efi(config) and config.unified_kernel_images == ConfigFeature.enabled:
+        elif want_efi(config) and config.unified_kernel_images.enabled():
             check_ukify(
                 config,
                 version="254",
index 16d613a40fbe467ee31f8b113a0755698aa80819..e877fa7d2bedf63bf60268df01e288b0224d78e6 100644 (file)
@@ -329,6 +329,16 @@ class ShimBootloader(StrEnum):
     unsigned = enum.auto()
 
 
+class UnifiedKernelImage(StrEnum):
+    none = enum.auto()
+    auto = enum.auto()
+    signed = enum.auto()
+    unsigned = enum.auto()
+
+    def enabled(self) -> bool:
+        return self in (UnifiedKernelImage.signed, UnifiedKernelImage.unsigned)
+
+
 class Cacheonly(StrEnum):
     always = enum.auto()
     auto = enum.auto()
@@ -1995,7 +2005,7 @@ class Config:
     bootloader: Bootloader
     bios_bootloader: BiosBootloader
     shim_bootloader: ShimBootloader
-    unified_kernel_images: ConfigFeature
+    unified_kernel_images: UnifiedKernelImage
     unified_kernel_image_format: str
     unified_kernel_image_profiles: list[UKIProfile]
     initrds: list[Path]
@@ -3077,7 +3087,10 @@ SETTINGS: list[ConfigSetting[Any]] = [
         dest="unified_kernel_images",
         metavar="FEATURE",
         section="Content",
-        parse=config_parse_feature,
+        parse=config_make_enum_parser_with_boolean(
+            UnifiedKernelImage, yes=UnifiedKernelImage.signed, no=UnifiedKernelImage.none
+        ),
+        default=UnifiedKernelImage.auto,
         help="Specify whether to use UKIs with grub/systemd-boot in UEFI mode",
     ),
     ConfigSetting(
@@ -5775,6 +5788,7 @@ def json_type_transformer(refcls: Union[type[Args], type[Config]]) -> Callable[[
         KeySource: key_source_transformer,
         Vmm: enum_transformer,
         list[UKIProfile]: uki_profile_transformer,
+        UnifiedKernelImage: enum_transformer,
         list[ArtifactOutput]: enum_list_transformer,
         CertificateSource: certificate_source_transformer,
         ConsoleMode: enum_transformer,
index 9fc38ad84726fb79403c5dc66d60a4880bdd1890..207db86243db6d87f58c0859bd0a1a0d05650c54 100644 (file)
@@ -1001,13 +1001,18 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
 
 `UnifiedKernelImages=`, `--unified-kernel-images=`
 :   Specifies whether to use unified kernel images or not when
-    `Bootloader=` is set to `systemd-boot` or `grub`. Takes a boolean
-    value or `auto`. Defaults to `auto`. If enabled, unified kernel images
-    are always used and the build will fail if any components required to
-    build unified kernel images are missing. If set to `auto`, unified
+    `Bootloader=` is set to `systemd-boot` or `grub`. Takes one of `none`,
+    `unsigned`, `signed` or `auto`. Defaults to `auto`. If `unsigned` or `signed`,
+    unified kernel images are always used and the build will fail if any components
+    required to build unified kernel images are missing. If set to `auto`, unified
     kernel images will be used if all necessary components are available.
     Otherwise Type 1 entries as defined by the Boot Loader Specification
     will be used instead. If disabled, Type 1 entries will always be used.
+    If `Bootloader=` is set to one of the signed variant, a pre-built UKI
+    will be searched and the build will fail if it cannot be found, unless
+    `UnifiedKernelImages=` is set to `unsigned`, in which case the UKI will
+    be built locally. This is useful when combined with the runtime `Firmware=`
+    option set to `custom` so that the local signing key is enrolled in UEFI db.
 
 `UnifiedKernelImageFormat=`, `--unified-kernel-image-format=`
 :   Takes a filename without any path components to specify the format that
index 2fc1925f6a0e17c0a8dcb8e3cd93c4cfd72b79a6..61fb176529d6e1f0c40f98921be04ac989d2f7a0 100644 (file)
@@ -38,6 +38,7 @@ from mkosi.config import (
     ShimBootloader,
     Ssh,
     UKIProfile,
+    UnifiedKernelImage,
     Verb,
     Verity,
     Vmm,
@@ -597,7 +598,7 @@ def test_config() -> None:
                 sign_expected_pcr=True,
             )
         ],
-        unified_kernel_images=ConfigFeature.auto,
+        unified_kernel_images=UnifiedKernelImage.auto,
         unit_properties=["PROPERTY=VALUE"],
         use_subvolumes=ConfigFeature.auto,
         verity_certificate_source=CertificateSource(type=CertificateSourceType.file),