Skip to content

Commit

Permalink
Merge pull request #3102 from DaanDeMeyer/pcr
Browse files Browse the repository at this point in the history
Allow signing expected PCRs independently of using secure boot
  • Loading branch information
DaanDeMeyer authored Oct 4, 2024
2 parents b5f4a9b + 0b14c93 commit f9dc109
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 19 deletions.
59 changes: 40 additions & 19 deletions mkosi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1477,6 +1477,8 @@ def want_signed_pcrs(config: Config) -> bool:
return config.sign_expected_pcr == ConfigFeature.enabled or (
config.sign_expected_pcr == ConfigFeature.auto
and config.find_binary("systemd-measure", "/usr/lib/systemd/systemd-measure") is not None
and bool(config.sign_expected_pcr_key)
and bool(config.sign_expected_pcr_certificate)
)


Expand Down Expand Up @@ -1593,24 +1595,28 @@ def build_uki(

arguments += ["--sign-kernel"]

if want_signed_pcrs(context.config):
if want_signed_pcrs(context.config):
assert context.config.sign_expected_pcr_key
assert context.config.sign_expected_pcr_certificate
arguments += [
"--pcr-private-key", context.config.sign_expected_pcr_key,
# SHA1 might be disabled in OpenSSL depending on the distro so we opt to not sign
# for SHA1 to avoid having to manage a bunch of configuration to re-enable SHA1.
"--pcr-banks", "sha256",
] # fmt: skip
if context.config.sign_expected_pcr_key.exists():
options += ["--bind", context.config.sign_expected_pcr_key, context.config.sign_expected_pcr_key]
if context.config.sign_expected_pcr_key_source.type == KeySourceType.engine:
arguments += [
"--pcr-private-key", context.config.secure_boot_key,
# SHA1 might be disabled in OpenSSL depending on the distro so we opt to not sign
# for SHA1 to avoid having to manage a bunch of configuration to re-enable SHA1.
"--pcr-banks", "sha256",
"--signing-engine", context.config.sign_expected_pcr_key_source.source,
"--pcr-public-key", context.config.sign_expected_pcr_certificate,
] # fmt: skip
options += [
"--ro-bind",
context.config.sign_expected_pcr_certificate,
context.config.sign_expected_pcr_certificate,
"--bind-try", "/run/pcscd", "/run/pcscd",
] # fmt: skip
if context.config.secure_boot_key.exists():
options += ["--bind", context.config.secure_boot_key, context.config.secure_boot_key]
if context.config.secure_boot_key_source.type == KeySourceType.engine:
arguments += [
"--signing-engine", context.config.secure_boot_key_source.source,
"--pcr-public-key", context.config.secure_boot_certificate,
] # fmt: skip
options += [
"--ro-bind", context.config.secure_boot_certificate, context.config.secure_boot_certificate, # noqa
"--bind-try", "/run/pcscd", "/run/pcscd",
] # fmt: skip

if microcodes:
# new .ucode section support?
Expand Down Expand Up @@ -2376,15 +2382,30 @@ def check_inputs(config: Config) -> None:
if config.secure_boot and not config.secure_boot_key:
die(
"SecureBoot= is enabled but no secure boot key is configured",
hint="Run mkosi genkey to generate a secure boot key/certificate pair",
hint="Run mkosi genkey to generate a key/certificate pair",
)

if config.secure_boot and not config.secure_boot_certificate:
die(
"SecureBoot= is enabled but no secure boot key is configured",
hint="Run mkosi genkey to generate a secure boot key/certificate pair",
"SecureBoot= is enabled but no secure boot certificate is configured",
hint="Run mkosi genkey to generate a key/certificate pair",
)

if config.sign_expected_pcr == ConfigFeature.enabled and not config.sign_expected_pcr_key:
die(
"SignExpectedPcr= is enabled but no private key is configured",
hint="Run mkosi genkey to generate a key/certificate pair",
)

if config.sign_expected_pcr == ConfigFeature.enabled and not config.sign_expected_pcr_certificate:
die(
"SignExpectedPcr= is enabled but no certificate is configured",
hint="Run mkosi genkey to generate a key/certificate pair",
)

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")


def check_tool(config: Config, *tools: PathString, reason: str, hint: Optional[str] = None) -> Path:
tool = config.find_binary(*tools)
Expand Down
33 changes: 33 additions & 0 deletions mkosi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,9 @@ class Config:
verity_key_source: KeySource
verity_certificate: Optional[Path]
sign_expected_pcr: ConfigFeature
sign_expected_pcr_key: Optional[Path]
sign_expected_pcr_key_source: KeySource
sign_expected_pcr_certificate: Optional[Path]
passphrase: Optional[Path]
checksum: bool
sign: bool
Expand Down Expand Up @@ -2780,6 +2783,33 @@ def parse_ini(path: Path, only_sections: Collection[str] = ()) -> Iterator[tuple
help="Measure the components of the unified kernel image (UKI) and "
"embed the PCR signature into the UKI",
),
ConfigSetting(
dest="sign_expected_pcr_key",
metavar="KEY",
section="Validation",
parse=config_parse_key,
paths=("mkosi.key",),
help="Private key for signing expected PCR signature",
scope=SettingScope.universal,
),
ConfigSetting(
dest="sign_expected_pcr_key_source",
section="Validation",
metavar="SOURCE[:ENGINE]",
parse=config_parse_key_source,
default=KeySource(type=KeySourceType.file),
help="The source to use to retrieve the expected PCR signing key",
scope=SettingScope.universal,
),
ConfigSetting(
dest="sign_expected_pcr_certificate",
metavar="PATH",
section="Validation",
parse=config_make_path_parser(),
paths=("mkosi.crt",),
help="Certificate for signing expected PCR signature in X509 format",
scope=SettingScope.universal,
),
ConfigSetting(
dest="passphrase",
metavar="PATH",
Expand Down Expand Up @@ -4447,6 +4477,9 @@ def summary(config: Config) -> str:
Verity Signing Key Source: {config.verity_key_source}
Verity Certificate: {none_to_none(config.verity_certificate)}
Sign Expected PCRs: {config.sign_expected_pcr}
Expected PCRs Signing Key: {none_to_none(config.sign_expected_pcr_key)}
Expected PCRs Key Source: {config.sign_expected_pcr_key_source}
Expected PCRs Certificate: {none_to_none(config.sign_expected_pcr_certificate)}
Passphrase: {none_to_none(config.passphrase)}
Checksum: {yes_no(config.checksum)}
Sign: {yes_no(config.sign)}
Expand Down
15 changes: 15 additions & 0 deletions mkosi/resources/man/mkosi.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,18 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
`systemd-measure` binary is in `PATH`. Depends on `SecureBoot=`
being enabled and key from `SecureBootKey=`.

`SignExpectedPcrKey=`, `--sign-expected-pcr-key=`
: Path to the PEM file containing the secret key for signing the expected PCR signatures.
When `SignExpectedPcrKeySource=` is specified, the input type depends on
the source.

`SignExpectedPcrKeySource=`, `--sign-expected-key-source=`
: Source of `VerityKey=`, to support OpenSSL engines. E.g.:
`--verity-key-source=engine:pkcs11`

`SignExpectedPcrCertificate=`, `--sign-expected-pcr-certificate=`
: Path to the X.509 file containing the certificate for signing the expected PCR signatures.

`Passphrase=`, `--passphrase`
: Specify the path to a file containing the passphrase to use for LUKS
encryption. It should contain the passphrase literally, and not end in
Expand Down Expand Up @@ -2608,6 +2620,9 @@ and cannot be configured in subimages:
- `VerityCertificate=`
- `VerityKey=`
- `VerityKeySource=`
- `SignExpectedPcrCertificate=`
- `SignExpectedPcrKey=`
- `SignExpectedPcrSource=`
- `VolatilePackageDirectories=`
- `WithNetwork=`
- `WithTests`
Expand Down
2 changes: 2 additions & 0 deletions mkosi/resources/mkosi-tools/mkosi.conf.d/10-opensuse.conf
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Packages=
patterns-base-minimal_base
pesign
policycoreutils
python3-cryptography
python3-pefile
qemu-headless
qemu-ipxe
Expand All @@ -35,6 +36,7 @@ Packages=
systemd-coredump
systemd-experimental
systemd-journal-remote
tpm2.0-tools
virtiofsd
xz
zypper
9 changes: 9 additions & 0 deletions tests/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@ def test_config() -> None:
"ShimBootloader": "none",
"Sign": false,
"SignExpectedPcr": "disabled",
"SignExpectedPcrCertificate": "/my/cert",
"SignExpectedPcrKey": "/my/key",
"SignExpectedPcrKeySource": {
"Source": "",
"Type": "file"
},
"SkeletonTrees": [
{
"Source": "/foo/bar",
Expand Down Expand Up @@ -505,6 +511,9 @@ def test_config() -> None:
shim_bootloader=ShimBootloader.none,
sign=False,
sign_expected_pcr=ConfigFeature.disabled,
sign_expected_pcr_key=Path("/my/key"),
sign_expected_pcr_key_source=KeySource(type=KeySourceType.file),
sign_expected_pcr_certificate=Path("/my/cert"),
skeleton_trees=[ConfigTree(Path("/foo/bar"), Path("/")), ConfigTree(Path("/bar/baz"), Path("/qux"))],
source_date_epoch=12345,
split_artifacts=True,
Expand Down

0 comments on commit f9dc109

Please sign in to comment.