Skip to content

Commit 4ffe2f6

Browse files
author
Gareth Widlansky
committed
add autoenroll to sdboot
Signed-off-by: Gareth Widlansky <gareth.widlansky@proton.me>
1 parent 5f64268 commit 4ffe2f6

File tree

4 files changed

+109
-6
lines changed

4 files changed

+109
-6
lines changed

Dockerfile.sdboot

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
# Override via --build-arg=base=<image> to use a different base
22
ARG base=localhost/bootc
33
# Image to sign systemd-boot first, BEFORE, installing onto the image
4-
ARG buildroot=quay.io/centos/centos:stream10
4+
# We need to use fedora for the `efitools` package
5+
ARG buildroot=quay.io/fedora/fedora:42
56

67
FROM $base AS base-unsigned
78

89
FROM $buildroot as buildroot-base
910
RUN <<EORUN
1011
set -xeuo pipefail
11-
dnf install -y pesign openssl
12+
dnf install -y pesign openssl efitools
1213
dnf clean all
1314
EORUN
1415

@@ -41,10 +42,29 @@ EORUN
4142

4243

4344
FROM base-unsigned as final
44-
RUN --mount=type=bind,from=signer,target=/run/sdboot \
45+
RUN --mount=type=secret,id=cert \
46+
--mount=type=bind,from=signer,target=/run/sdboot \
4547
<<EORUN
4648
set -xeuo pipefail
49+
50+
# enable auto enrollment for systemd-boot
51+
auto_enroll=/usr/lib/bootc/keys
52+
mkdir -p ${auto_enroll}
53+
# in prod, it would be a good idea to also enroll the microsoft keys here as well
54+
openssl x509 -outform DER -in run/secrets/cert -out "${auto_enroll}/db.cer"
55+
4756
sdboot=/usr/lib/systemd/boot/efi/systemd-bootx64.efi
4857
# copy signed sdboot from buildroot
4958
cp "/run/sdboot/sdboot.efi" ${sdboot}
59+
cp -r "/run/sdboot/keys" "/usr/lib/bootc/keys/"
60+
61+
guid=$(cat /run/secrets/guid)
62+
cert-to-efi-sig-list -g ${guid} /run/secrets/cert /db.esl
63+
sign-efi-sig-list -k /run/secrets/key -c /run/secrets/cert db /db.esl ${keyout}/db.auth
64+
65+
cert-to-efi-sig-list -g ${guid} /run/secrets/pk_cert /pk.esl
66+
sign-efi-sig-list -k /run/secrets/pk_key -c /run/secrets/pk_cert PK /pk.esl ${keyout}/pk.auth
67+
68+
cert-to-efi-sig-list -g ${guid} /run/secrets/kek_cert /kek.esl
69+
sign-efi-sig-list -k /run/secrets/kek_key -c /run/secrets/kek_cert KEK /kek.esl ${keyout}/kek.auth
5070
EORUN

crates/lib/src/bootc_composefs/boot.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<<<<<<< HEAD
12
//! Composefs boot setup and configuration.
23
//!
34
//! This module handles setting up boot entries for composefs-based deployments,
@@ -65,8 +66,9 @@ use std::ffi::OsStr;
6566
use std::fs::create_dir_all;
6667
use std::io::Write;
6768
use std::path::Path;
69+
use std::{ffi::OsStr, os::unix::ffi::OsStrExt};
6870

69-
use anyhow::{anyhow, Context, Result};
71+
use anyhow::{anyhow, bail, Context, Result};
7072
use bootc_blockdev::find_parent_devices;
7173
use bootc_kernel_cmdline::utf8::{Cmdline, Parameter};
7274
use bootc_mount::inspect_filesystem_of_dir;
@@ -78,6 +80,7 @@ use cap_std_ext::{
7880
};
7981
use clap::ValueEnum;
8082
use composefs::fs::read_file;
83+
use composefs::generic_tree::{Directory, Inode, LeafContent};
8184
use composefs::tree::RegularFile;
8285
use composefs_boot::BootOps;
8386
use composefs_boot::{
@@ -1142,6 +1145,42 @@ pub(crate) fn setup_composefs_uki_boot(
11421145
Ok(())
11431146
}
11441147

1148+
pub struct AuthFile {
1149+
pub filename: Utf8PathBuf,
1150+
pub file: RegularFile<Sha512HashValue>,
1151+
}
1152+
1153+
fn get_autoenroll_keys(root: &Directory<RegularFile<Sha512HashValue>>) -> Result<Vec<AuthFile>> {
1154+
let mut entries = vec![];
1155+
match root.get_directory("/usr/lib/bootc/keys".as_ref()) {
1156+
Ok(keys_dir) => {
1157+
for (filename, inode) in keys_dir.entries() {
1158+
if !filename.as_bytes().ends_with(b".auth") {
1159+
continue;
1160+
}
1161+
1162+
let Inode::Leaf(leaf) = inode else {
1163+
bail!("/usr/lib/bootc/keys/{filename:?} is a directory");
1164+
};
1165+
1166+
let LeafContent::Regular(file) = &leaf.content else {
1167+
bail!("/usr/lib/bootc/keys/{filename:?} is not a regular file");
1168+
};
1169+
let path = match Utf8PathBuf::from_os_string(filename.into()) {
1170+
Ok(p) => p,
1171+
Err(_) => bail!("couldn't get pathbuf: /usr/lib/bootc/keys/{filename:?}"),
1172+
};
1173+
entries.push(AuthFile {
1174+
filename: path,
1175+
file: file.clone(),
1176+
});
1177+
}
1178+
}
1179+
Err(other) => Err(other)?,
1180+
};
1181+
Ok(entries)
1182+
}
1183+
11451184
#[context("Setting up composefs boot")]
11461185
pub(crate) fn setup_composefs_boot(
11471186
root_setup: &RootSetup,
@@ -1160,6 +1199,8 @@ pub(crate) fn setup_composefs_boot(
11601199

11611200
let postfetch = PostFetchState::new(state, &mounted_fs)?;
11621201

1202+
let keys = get_autoenroll_keys(&fs.root)?;
1203+
11631204
let boot_uuid = root_setup
11641205
.get_boot_uuid()?
11651206
.or(root_setup.rootfs_uuid.as_deref())
@@ -1181,6 +1222,8 @@ pub(crate) fn setup_composefs_boot(
11811222
&root_setup.physical_root_path,
11821223
&state.config_opts,
11831224
None,
1225+
keys,
1226+
&repo,
11841227
)?;
11851228
}
11861229

crates/lib/src/bootloader.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1+
use std::fs::create_dir_all;
12
use std::process::Command;
23

34
use anyhow::{anyhow, bail, Context, Result};
45
use bootc_utils::CommandRunExt;
56
use camino::Utf8Path;
7+
use cap_std_ext::cap_std::ambient_authority;
68
use cap_std_ext::cap_std::fs::Dir;
9+
use cap_std_ext::dirext::CapStdExtDirExt;
10+
use composefs::fs::read_file;
711
use fn_error_context::context;
812

913
use bootc_blockdev::{Partition, PartitionTable};
1014
use bootc_mount as mount;
1115

12-
use crate::bootc_composefs::boot::mount_esp;
16+
use crate::bootc_composefs::boot::{mount_esp, AuthFile};
1317
use crate::{discoverable_partition_specification, utils};
1418

1519
/// The name of the mountpoint for efi (as a subdirectory of /boot, or at the toplevel)
@@ -74,6 +78,8 @@ pub(crate) fn install_systemd_boot(
7478
_rootfs: &Utf8Path,
7579
_configopts: &crate::install::InstallConfigOpts,
7680
_deployment_path: Option<&str>,
81+
autoenroll: Vec<AuthFile>,
82+
repo: &crate::store::ComposefsRepository,
7783
) -> Result<()> {
7884
let esp_part = device
7985
.find_partition_of_type(discoverable_partition_specification::ESP)
@@ -87,7 +93,29 @@ pub(crate) fn install_systemd_boot(
8793
Command::new("bootctl")
8894
.args(["install", "--esp-path", esp_path.as_str()])
8995
.log_debug()
90-
.run_inherited_with_cmd_context()
96+
.run_inherited_with_cmd_context()?;
97+
98+
if autoenroll.len() < 1 {
99+
return Ok(());
100+
}
101+
102+
println!("Autoenrolling keys");
103+
let path = esp_path.join("loader/keys/auto");
104+
create_dir_all(&path)?;
105+
106+
let keys_dir = Dir::open_ambient_dir(&path, ambient_authority())
107+
.with_context(|| format!("Opening {path}"))?;
108+
for a in autoenroll.iter() {
109+
let p = path.join(a.filename.clone());
110+
keys_dir
111+
.atomic_write(
112+
&a.filename,
113+
read_file(&a.file, &repo).context("reading file")?,
114+
)
115+
.with_context(|| format!("Writing secure boot key: {p}"))?;
116+
println!("Wrote secure boot key: {p}");
117+
}
118+
Ok(())
91119
}
92120

93121
#[context("Installing bootloader using zipl")]

tests/build-sealed

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,25 @@ runv podman build -t $sdboot_signed \
6161
--build-arg=buildroot=${BOOTC_buildroot_base} \
6262
--secret=id=key,src=${secureboot}/db.key \
6363
--secret=id=cert,src=${secureboot}/db.crt \
64+
<<<<<<< HEAD
6465
--secret=id=cert,src=${secureboot}/db.crt \
6566
--secret=id=pk_key,src=${secureboot}/PK.key \
6667
--secret=id=pk_cert,src=${secureboot}/PK.crt \
6768
--secret=id=kek_key,src=${secureboot}/KEK.key \
6869
--secret=id=kek_cert,src=${secureboot}/KEK.crt \
6970
--secret=id=guid,src=${secureboot}/GUID.txt \
7071
-f Dockerfile.sdboot .
72+
||||||| parent of 6bd32cdc (add autoenroll to sdboot)
73+
--secret=id=cert,src=${secureboot}/db.crt -f Dockerfile.sdboot .
74+
=======
75+
--secret=id=pk_key,src=${secureboot}/PK.key \
76+
--secret=id=pk_cert,src=${secureboot}/PK.crt \
77+
--secret=id=kek_key,src=${secureboot}/KEK.key \
78+
--secret=id=kek_cert,src=${secureboot}/KEK.crt \
79+
--secret=id=guid,src=${secureboot}/GUID.txt \
80+
-f Dockerfile.sdboot .
81+
82+
>>>>>>> 6bd32cdc (add autoenroll to sdboot)
7183

7284
graphroot=$(podman system info -f '{{.Store.GraphRoot}}')
7385
echo "Computing composefs digest..."

0 commit comments

Comments
 (0)