]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Add Verity= feature 3115/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 9 Oct 2024 11:46:21 +0000 (13:46 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 9 Oct 2024 17:26:42 +0000 (19:26 +0200)
This allows explicitly enabling/disabling use of verity for disk and
extension images as requested in #3113..

mkosi/__init__.py
mkosi/config.py
mkosi/resources/man/mkosi.1.md
mkosi/resources/repart/definitions/confext-unsigned.repart.d/10-root.conf [new file with mode: 0644]
mkosi/resources/repart/definitions/portable-unsigned.repart.d/10-root.conf [new file with mode: 0644]
mkosi/resources/repart/definitions/sysext-unsigned.repart.d/10-root.conf [new file with mode: 0644]
tests/test_json.py

index 5aafbb7979d4eba5cf827c2de920c9f81972f033..c7c61becfa9d3890a034ce1ff42f586415e3c181 100644 (file)
@@ -2407,6 +2407,18 @@ def check_inputs(config: Config) -> None:
     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.verity == ConfigFeature.enabled and not config.verity_key:
+        die(
+            "Verity= is enabled but no verity key is configured",
+            hint="Run mkosi genkey to generate a key/certificate pair",
+        )
+
+    if config.verity == ConfigFeature.enabled and not config.verity_certificate:
+        die(
+            "Verity= is enabled but no verity certificate is configured",
+            hint="Run mkosi genkey to generate a key/certificate pair",
+        )
+
     for addon in config.pe_addons:
         if not addon.output:
             die(
@@ -2968,6 +2980,7 @@ def make_image(
     skip: Sequence[str] = [],
     split: bool = False,
     tabs: bool = False,
+    verity: bool = False,
     root: Optional[Path] = None,
     definitions: Sequence[Path] = [],
 ) -> list[Partition]:
@@ -2999,7 +3012,10 @@ def make_image(
     if context.config.passphrase:
         cmdline += ["--key-file", workdir(context.config.passphrase)]
         options += ["--ro-bind", context.config.passphrase, workdir(context.config.passphrase)]
-    if context.config.verity_key:
+    if verity:
+        assert context.config.verity_key
+        assert context.config.verity_certificate
+
         if context.config.verity_key_source.type != KeySourceType.file:
             cmdline += ["--private-key-source", str(context.config.verity_key_source)]
             options += ["--bind-try", "/run/pcscd", "/run/pcscd"]
@@ -3008,7 +3024,7 @@ def make_image(
             options += ["--ro-bind", context.config.verity_key, workdir(context.config.verity_key)]
         else:
             cmdline += ["--private-key", context.config.verity_key]
-    if context.config.verity_certificate:
+
         cmdline += ["--certificate", workdir(context.config.verity_certificate)]
         options += [
             "--ro-bind", context.config.verity_certificate, workdir(context.config.verity_certificate),
@@ -3050,6 +3066,14 @@ def make_image(
 
     partitions = [Partition.from_dict(d) for d in output]
 
+    if context.config.verity == ConfigFeature.enabled and not any(
+        p.type.startswith("usr-verity-sig") or p.type.startswith("root-verity-sig") for p in partitions
+    ):
+        die(
+            "Verity is explicitly enabled but didn't find any verity signature partition",
+            hint="Make sure to add verity signature partitions in mkosi.repart if building a disk image",
+        )
+
     if split:
         for p in partitions:
             if p.split_path:
@@ -3058,6 +3082,12 @@ def make_image(
     return partitions
 
 
+def want_verity(config: Config) -> bool:
+    return config.verity == ConfigFeature.enabled or bool(
+        config.verity == ConfigFeature.auto and config.verity_key and config.verity_certificate
+    )
+
+
 def make_disk(
     context: Context,
     msg: str,
@@ -3131,7 +3161,14 @@ def make_disk(
         definitions = [defaults]
 
     return make_image(
-        context, msg=msg, skip=skip, split=split, tabs=tabs, root=context.root, definitions=definitions
+        context,
+        msg=msg,
+        skip=skip,
+        split=split,
+        tabs=tabs,
+        verity=want_verity(context.config),
+        root=context.root,
+        definitions=definitions,
     )
 
 
@@ -3275,7 +3312,8 @@ def make_esp(context: Context, uki: Path) -> list[Partition]:
 
 
 def make_extension_image(context: Context, output: Path) -> None:
-    r = context.resources / f"repart/definitions/{context.config.output_format}.repart.d"
+    unsigned = "-unsigned" if not want_verity(context.config) else ""
+    r = context.resources / f"repart/definitions/{context.config.output_format}{unsigned}.repart.d"
 
     cmdline: list[PathString] = [
         "systemd-repart",
@@ -3304,7 +3342,10 @@ def make_extension_image(context: Context, output: Path) -> None:
     if context.config.passphrase:
         cmdline += ["--key-file", context.config.passphrase]
         options += ["--ro-bind", context.config.passphrase, workdir(context.config.passphrase)]
-    if context.config.verity_key:
+    if want_verity(context.config):
+        assert context.config.verity_key
+        assert context.config.verity_certificate
+
         if context.config.verity_key_source.type != KeySourceType.file:
             cmdline += ["--private-key-source", str(context.config.verity_key_source)]
         if context.config.verity_key.exists():
@@ -3312,7 +3353,7 @@ def make_extension_image(context: Context, output: Path) -> None:
             options += ["--ro-bind", context.config.verity_key, workdir(context.config.verity_key)]
         else:
             cmdline += ["--private-key", context.config.verity_key]
-    if context.config.verity_certificate:
+
         cmdline += ["--certificate", workdir(context.config.verity_certificate)]
         options += [
             "--ro-bind", context.config.verity_certificate, workdir(context.config.verity_certificate)
index 5180ae6874c21207960865fb695e4e40939f296a..d8f491dc27100805b558b1842265af50ed1c6e08 100644 (file)
@@ -1666,6 +1666,7 @@ class Config:
     secure_boot_key_source: KeySource
     secure_boot_certificate: Optional[Path]
     secure_boot_sign_tool: SecureBootSignTool
+    verity: ConfigFeature
     verity_key: Optional[Path]
     verity_key_source: KeySource
     verity_certificate: Optional[Path]
@@ -2829,6 +2830,13 @@ SETTINGS = (
         choices=SecureBootSignTool.choices(),
         help="Tool to use for signing PE binaries for secure boot",
     ),
+    ConfigSetting(
+        dest="verity",
+        section="Validation",
+        metavar="FEATURE",
+        parse=config_parse_feature,
+        help="Configure whether to enforce or disable verity partitions for disk images",
+    ),
     ConfigSetting(
         dest="verity_key",
         metavar="KEY",
@@ -4552,6 +4560,7 @@ def summary(config: Config) -> str:
       SecureBoot Signing Key Source: {config.secure_boot_key_source}
              SecureBoot Certificate: {none_to_none(config.secure_boot_certificate)}
                SecureBoot Sign Tool: {config.secure_boot_sign_tool}
+                             Verity: {config.verity}
                  Verity Signing Key: {none_to_none(config.verity_key)}
           Verity Signing Key Source: {config.verity_key_source}
                  Verity Certificate: {none_to_none(config.verity_certificate)}
index 028127c2226b69589acb0c63507e13b892c73e87..1d32e1b972dab072423f4c9c59e3318074ccd3a7 100644 (file)
@@ -1130,6 +1130,22 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
     If set to `auto`, either sbsign or pesign are used if available, with sbsign being preferred if both are
     installed.
 
+`Verity=`, `--verity=`
+:   Whether to enforce or disable signed verity for extension images.
+    Takes a boolean value or `auto`. If enabled, a verity key and
+    certificate must be present and the build will fail if we don't
+    detect any verity partitions in the disk image produced by
+    systemd-repart. If disabled, verity partitions will be excluded from
+    disk images produced by systemd-repart even if the partition
+    definitions contain verity partitions. If set to `auto`, the verity
+    key and certificate will be passed to systemd-repart if available,
+    but the build won't fail if no verity partitions are found in the
+    disk image produced by systemd-repart.
+
+    Note that explicitly disabling signed verity is not yet implemented
+    for the `disk` output and only works for extension images at the
+    moment.
+
 `VerityKey=`, `--verity-key=`
 :   Path to the PEM file containing the secret key for signing the verity signature, if a verity signature
     partition is added with systemd-repart. When `VerityKeySource=` is specified, the input type depends on
diff --git a/mkosi/resources/repart/definitions/confext-unsigned.repart.d/10-root.conf b/mkosi/resources/repart/definitions/confext-unsigned.repart.d/10-root.conf
new file mode 100644 (file)
index 0000000..3ccd909
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Partition]
+Type=root
+Format=erofs
+CopyFiles=/etc/
+Minimize=best
diff --git a/mkosi/resources/repart/definitions/portable-unsigned.repart.d/10-root.conf b/mkosi/resources/repart/definitions/portable-unsigned.repart.d/10-root.conf
new file mode 100644 (file)
index 0000000..9048198
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Partition]
+Type=root
+Format=erofs
+CopyFiles=/
+Minimize=best
diff --git a/mkosi/resources/repart/definitions/sysext-unsigned.repart.d/10-root.conf b/mkosi/resources/repart/definitions/sysext-unsigned.repart.d/10-root.conf
new file mode 100644 (file)
index 0000000..40743a1
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Partition]
+Type=root
+Format=erofs
+CopyFiles=/opt/
+CopyFiles=/usr/
+Minimize=best
index 317152e338916a4963b7231949dd85bf11511b31..3b3375576a2c939e25b22e665e0f5276949b3bbd 100644 (file)
@@ -372,6 +372,7 @@ def test_config() -> None:
                 "PROPERTY=VALUE"
             ],
             "UseSubvolumes": "auto",
+            "Verity": "enabled",
             "VerityCertificate": "/path/to/cert",
             "VerityKey": null,
             "VerityKeySource": {
@@ -550,6 +551,7 @@ def test_config() -> None:
         unified_kernel_images=ConfigFeature.auto,
         unit_properties=["PROPERTY=VALUE"],
         use_subvolumes=ConfigFeature.auto,
+        verity=ConfigFeature.enabled,
         verity_certificate=Path("/path/to/cert"),
         verity_key=None,
         verity_key_source=KeySource(type=KeySourceType.file),