From b529b9067794e81692e8e4e0643a60a5d5feb0fa Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 11 Sep 2024 22:35:25 +0200 Subject: [PATCH] qemu: enroll certificate in MOK for dm-verity and kernel mods Automatically enroll the secure boot certificate in MOK, and also set the boolean so that the kernel trusts it to verify kernel modules and dm-verity volumes. This requires secure boot to be enabled to be effective, otherwise the kernel will ignore it. --- mkosi/qemu.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 ++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/mkosi/qemu.py b/mkosi/qemu.py index 78ce4da42f..c2306abcc2 100644 --- a/mkosi/qemu.py +++ b/mkosi/qemu.py @@ -646,6 +646,46 @@ def finalize_firmware_variables( stack: contextlib.ExitStack, ) -> tuple[Path, str]: ovmf_vars = stack.enter_context(tempfile.NamedTemporaryFile(prefix="mkosi-ovmf-vars-")) + ovmf_json = stack.enter_context(tempfile.NamedTemporaryFile(prefix="mkosi-ovmf-vars-json-")) + have_mok_json = False + + if config.secure_boot_certificate: + try: + from virt.firmware.efi import guids, siglist + + # In order to make the kernel use mkosi.crt to verify dm-verity volumes, we need to + # create runtime and volatile EFI variables, MokListTrustedRT and MokListRT, as + # it's only from kernel 6.11 that db is used for this purpose. Note that, unlike + # certificates in db, the certificates in MOK are only used if secure boot is enabled. + # First create an efivar with the certificate, and then read it and convert to the + # json format that virt-fw-vars expects. + # attr=4 means EFI_VARIABLE_RUNTIME_ACCESS, and the GUID is the well-known MOK one. + + mok_sigdb = siglist.EfiSigDB() + mok_sigdb.add_cert(guids.parse_str("605dab50-e046-4300-abb6-3dd810dd8b23"), config.secure_boot_certificate) + mok_json = dict( + version=2, + variables=[ + dict( + name="MokListRT", + guid="605dab50-e046-4300-abb6-3dd810dd8b23", + attr=4, + data=bytes(mok_sigdb).hex(), + ), + dict( + name="MokListTrustedRT", + guid="605dab50-e046-4300-abb6-3dd810dd8b23", + attr=4, + data="01", + ), + ], + ) + ovmf_json.write(json.dumps(mok_json).encode()) + ovmf_json.flush() + have_mok_json = True + except ImportError: + pass + if config.qemu_firmware_variables in (None, Path("custom"), Path("microsoft")): ovmf_vars_format = ovmf.vars_format else: @@ -653,6 +693,7 @@ def finalize_firmware_variables( if config.qemu_firmware_variables == Path("custom"): assert config.secure_boot_certificate + assert have_mok_json run( [ "virt-fw-vars", @@ -660,6 +701,7 @@ def finalize_firmware_variables( "--output", ovmf_vars.name, "--enroll-cert", config.secure_boot_certificate, "--add-db", "OvmfEnrollDefaultKeys", config.secure_boot_certificate, + "--set-json", ovmf_json.name, "--no-microsoft", "--secure-boot", "--loglevel", "WARNING", @@ -669,6 +711,7 @@ def finalize_firmware_variables( options=[ "--bind", ovmf_vars.name, ovmf_vars.name, "--ro-bind", config.secure_boot_certificate, config.secure_boot_certificate, + "--ro-bind", ovmf_json.name, ovmf_json.name, ], ), ) @@ -679,7 +722,25 @@ def finalize_firmware_variables( if config.qemu_firmware_variables == Path("microsoft") or not config.qemu_firmware_variables else config.qemu_firmware_variables ) - shutil.copy2(vars, Path(ovmf_vars.name)) + if have_mok_json: + run( + [ + "virt-fw-vars", + "--input", vars, + "--output", ovmf_vars.name, + "--set-json", ovmf_json.name, + "--loglevel", "WARNING", + ], + sandbox=config.sandbox( + binary=qemu, + options=[ + "--bind", ovmf_vars.name, ovmf_vars.name, + "--ro-bind", ovmf_json.name, ovmf_json.name, + ], + ), + ) + else: + shutil.copy2(vars, Path(ovmf_vars.name)) return Path(ovmf_vars.name), ovmf_vars_format diff --git a/pyproject.toml b/pyproject.toml index 76fbeb5edb..dbff206172 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,8 @@ pythonVersion = "3.9" [tool.mypy] python_version = 3.9 +# Fails on external libraries like virt.firmware +ignore_missing_imports = true # belonging to --strict warn_unused_configs = true disallow_any_generics = true