]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Add support for signing PCR policies for the initrd main
authorChris Coulson <chris.coulson@amutable.com>
Tue, 30 Jun 2026 22:15:57 +0000 (23:15 +0100)
committerJörg Behrmann <behrmann@physik.fu-berlin.de>
Wed, 5 Aug 2026 19:58:46 +0000 (21:58 +0200)
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

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

index 9e35c53db3da7065d20bc4b208a69a2d8bd4e29c..c681b57b30e06883241448f8485b08a03c2c68b7 100644 (file)
@@ -1775,18 +1775,18 @@ def build_uki(
             "--pcr-banks", "sha256",
         ]  # fmt: skip
 
             "--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"
 
             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:
         # 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",
         )
 
             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")
 
     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",
                 )
 
                     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,
         if config.verity_key_source.type != KeySourceType.file:
             check_systemd_tool(
                 config,
index cfaa73970d8cabc372aef4002f71edcb4c34b983..15fec66eda7193b5dea544ed1b5c4cc09069c7c1 100644 (file)
@@ -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_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
     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,
     ),
         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",
     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}
            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)}
                          Passphrase: {none_to_none(config.passphrase)}
                            Checksum: {yes_no(config.checksum)}
                                Sign: {yes_no(config.sign)}
index 05c04386d94ad6cbebecfeb1e43c1c0f0497307a..fc2c92c9bd79af1c1a0e36ec88b78a72250be954 100644 (file)
@@ -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.
 
 `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`.
 `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`.
index 26f68b14a115757baf4a72fbf61df05cbd1f5df9..41457e70bea53627b8d0827b8c31278b620b5dea 100644 (file)
@@ -385,6 +385,7 @@ def test_config() -> None:
                 "Source": "",
                 "Type": "file"
             },
                 "Source": "",
                 "Type": "file"
             },
+            "SignInitrdPCRs": "disabled",
             "SkeletonTrees": [
                 {
                     "Source": "/foo/bar",
             "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_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",
         sign=False,
         skeleton_trees=[ConfigTree(Path("/foo/bar"), Path("/")), ConfigTree(Path("/bar/baz"), Path("/qux"))],
         snapshot="snapshot",