From: Chris Coulson Date: Tue, 30 Jun 2026 22:15:57 +0000 (+0100) Subject: Add support for signing PCR policies for the initrd X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;p=thirdparty%2Fmkosi.git Add support for signing PCR policies for the initrd The way that NvPCRs are initialized and anchored in systemd is changing, and to support this, the UKI needs to include signed PCR policies that can only be satisfied during the initrd, as these are used to authorize NvPCR initialization. This is enabled with a new --sign-initrd-pcrs option for ukify. This adds a new "SignInitrdPCRs=" option to control this. The default is "auto" which will turn on this option if PCR signing is enabled and ukify is new enough. The corresponding systemd PR is https://github.com/systemd/systemd/pull/42796 --- diff --git a/mkosi/__init__.py b/mkosi/__init__.py index 9e35c53db..c681b57b3 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -1775,18 +1775,18 @@ def build_uki( "--pcr-banks", "sha256", ] # fmt: skip - if ( - systemd_tool_version( - python_binary(context.config), - ukify, - sandbox=context.sandbox, - ) - >= "258" - ): + ukify_version = systemd_tool_version(python_binary(context.config), ukify, sandbox=context.sandbox) + + if ukify_version >= "258": cert_parameter = "--pcr-certificate" else: cert_parameter = "--pcr-public-key" + if context.config.sign_initrd_pcrs == ConfigFeature.enabled or ( + context.config.sign_initrd_pcrs == ConfigFeature.auto and ukify_version >= "262~devel" + ): + arguments += ["--sign-initrd-pcrs"] + # If we're providing the private key via an engine or provider, we have to pass in a X.509 # certificate via --pcr-certificate as well. if context.config.sign_expected_pcr_key_source.type != KeySourceType.file: @@ -2756,6 +2756,9 @@ def check_inputs(config: Config) -> None: hint="Run mkosi genkey to generate a key/certificate pair", ) + if config.sign_initrd_pcrs == ConfigFeature.enabled and not want_signed_pcrs(config): + die("SignInitrdPCRs= is enabled but PCR signing is not enabled") + if config.secure_boot_key_source != config.sign_expected_pcr_key_source: die("Secure boot key source and expected PCR signatures key source have to be the same") @@ -2905,6 +2908,13 @@ def check_tools(config: Config, verb: Verb) -> None: reason="sign PCR hashes with OpenSSL engine", ) + if config.sign_initrd_pcrs == ConfigFeature.enabled and want_signed_pcrs(config): + check_ukify( + config, + version="262~devel", + reason="sign a PCR policy for the initrd", + ) + if config.verity_key_source.type != KeySourceType.file: check_systemd_tool( config, diff --git a/mkosi/config.py b/mkosi/config.py index cfaa73970..15fec66ed 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -2210,6 +2210,7 @@ class Config: sign_expected_pcr_key_source: KeySource sign_expected_pcr_certificate: Optional[Path] sign_expected_pcr_certificate_source: CertificateSource + sign_initrd_pcrs: ConfigFeature passphrase: Optional[Path] checksum: bool sign: bool @@ -3769,6 +3770,14 @@ SETTINGS: list[ConfigSetting[Any]] = [ help="The source to use to retrieve the expected PCR signing certificate", scope=SettingScope.inherit, ), + ConfigSetting( + dest="sign_initrd_pcrs", + metavar="FEATURE", + section="Validation", + name="SignInitrdPCRs", + parse=config_parse_feature, + help="Generate a signed PCR policy that can only be satisfied from the initrd and embed this into the UKI", # noqa: E501 + ), ConfigSetting( dest="passphrase", metavar="PATH", @@ -5962,6 +5971,7 @@ def summary(config: Config) -> str: Expected PCRs Key Source: {config.sign_expected_pcr_key_source} Expected PCRs Certificate: {none_to_none(config.sign_expected_pcr_certificate)} Expected PCRs Certificate Source: {config.sign_expected_pcr_certificate_source} + Sign initrd PCRs: {config.sign_initrd_pcrs} Passphrase: {none_to_none(config.passphrase)} Checksum: {yes_no(config.checksum)} Sign: {yes_no(config.sign)} diff --git a/mkosi/resources/man/mkosi.1.md b/mkosi/resources/man/mkosi.1.md index 05c04386d..fc2c92c9b 100644 --- a/mkosi/resources/man/mkosi.1.md +++ b/mkosi/resources/man/mkosi.1.md @@ -1454,6 +1454,13 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, `SignExpectedPcrCertificate=`, `--sign-expected-pcr-certificate=` : Path to the X.509 file containing the certificate for signing the expected PCR signatures. +`SignInitrdPCRs=`, `--sign-initrd-pcrs=` +: Whether to generate signed PCR policies that can only be satisfied from the + initrd. This is required for initialization of NvPCRs. This takes a boolean value + or the special value `auto`, which is the default and is equivalent to a true value + if PCR signing is enabled (see `SignExpectedPcr=`) and the version of **ukify** is + at least v262. Signing is performed with the key that is supplied to `SignExpectedPcrKey=`. + `SecureBootKeySource=`, `--secure-boot-key-source=`, `VerityKeySource=`, `--verity-key-source=`, `SignExpectedPcrKeySource=`, `--sign-expected-key-source=` : The source of the corresponding private key, to support OpenSSL engines and providers, e.g. `--secure-boot-key-source=engine:pkcs11` or `--secure-boot-key-source=provider:pkcs11`. diff --git a/tests/test_json.py b/tests/test_json.py index 26f68b14a..41457e70b 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -385,6 +385,7 @@ def test_config() -> None: "Source": "", "Type": "file" }, + "SignInitrdPCRs": "disabled", "SkeletonTrees": [ { "Source": "/foo/bar", @@ -611,6 +612,7 @@ def test_config() -> None: sign_expected_pcr_key_source=KeySource(type=KeySourceType.file), sign_expected_pcr_key=Path("/my/key"), sign_expected_pcr=ConfigFeature.disabled, + sign_initrd_pcrs=ConfigFeature.disabled, sign=False, skeleton_trees=[ConfigTree(Path("/foo/bar"), Path("/")), ConfigTree(Path("/bar/baz"), Path("/qux"))], snapshot="snapshot",