Skip to content

Commit cfa6579

Browse files
Gareth Widlanskycgwalters
authored andcommitted
sdboot: add support for key enrollment in bootc install
systemd-boot has support for automatically enrolling keys for Secure Boot, this adds support for copying these keys as embedded in the input container image into the location where systemd-boot can perform automatic enrollment on them. Commit-message-written-by: Colin Walters <walters@verbum.org> Signed-off-by: Gareth Widlansky <gareth.widlansky@proton.me>
1 parent 3efcbdd commit cfa6579

File tree

3 files changed

+91
-3
lines changed

3 files changed

+91
-3
lines changed

crates/lib/src/bootc_composefs/boot.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ use std::fs::create_dir_all;
6666
use std::io::Write;
6767
use std::path::Path;
6868

69-
use anyhow::{anyhow, Context, Result};
69+
use anyhow::{anyhow, bail, Context, Result};
7070
use bootc_blockdev::find_parent_devices;
7171
use bootc_kernel_cmdline::utf8::{Cmdline, Parameter};
7272
use bootc_mount::inspect_filesystem_of_dir;
@@ -137,6 +137,10 @@ const SYSTEMD_LOADER_CONF_PATH: &str = "loader/loader.conf";
137137
const INITRD: &str = "initrd";
138138
const VMLINUZ: &str = "vmlinuz";
139139

140+
const BOOTC_AUTOENROLL_PATH: &str = "usr/lib/bootc/install/secureboot-keys";
141+
142+
const AUTH_EXT: &str = "auth";
143+
140144
/// We want to be able to control the ordering of UKIs so we put them in a directory that's not the
141145
/// directory specified by the BLS spec. We do this because we want systemd-boot to only look at
142146
/// our config files and not show the actual UKIs in the bootloader menu
@@ -1146,6 +1150,52 @@ pub(crate) fn setup_composefs_uki_boot(
11461150
Ok(())
11471151
}
11481152

1153+
pub struct SecurebootKeys {
1154+
pub dir: Dir,
1155+
pub keys: Vec<Utf8PathBuf>,
1156+
}
1157+
1158+
fn get_secureboot_keys(fs: &Dir, p: &str) -> Result<Option<SecurebootKeys>> {
1159+
let mut entries = vec![];
1160+
1161+
// if the dir doesn't exist, return None
1162+
let keys_dir = match fs.open_dir_optional(p)? {
1163+
Some(d) => d,
1164+
_ => return Ok(None),
1165+
};
1166+
1167+
// https://github.com/systemd/systemd/blob/26b2085d54ebbfca8637362eafcb4a8e3faf832f/man/systemd-boot.xml#L392
1168+
1169+
for entry in keys_dir.entries()? {
1170+
let dir_e = entry?;
1171+
let dirname = dir_e.file_name();
1172+
if !dir_e.file_type()?.is_dir() {
1173+
bail!("/{p}/{dirname:?} is not a directory");
1174+
}
1175+
1176+
let dir_path: Utf8PathBuf = dirname.try_into()?;
1177+
let dir = dir_e.open_dir()?;
1178+
for entry in dir.entries()? {
1179+
let e = entry?;
1180+
let local: Utf8PathBuf = e.file_name().try_into()?;
1181+
let path = dir_path.join(local);
1182+
1183+
if path.extension() != Some(AUTH_EXT) {
1184+
continue;
1185+
}
1186+
1187+
if !e.file_type()?.is_file() {
1188+
bail!("/{p}/{path:?} is not a file");
1189+
}
1190+
entries.push(path);
1191+
}
1192+
}
1193+
return Ok(Some(SecurebootKeys {
1194+
dir: keys_dir,
1195+
keys: entries,
1196+
}));
1197+
}
1198+
11491199
#[context("Setting up composefs boot")]
11501200
pub(crate) async fn setup_composefs_boot(
11511201
root_setup: &RootSetup,
@@ -1185,6 +1235,7 @@ pub(crate) async fn setup_composefs_boot(
11851235
&root_setup.physical_root_path,
11861236
&state.config_opts,
11871237
None,
1238+
get_secureboot_keys(&mounted_fs, BOOTC_AUTOENROLL_PATH)?,
11881239
)?;
11891240
}
11901241

crates/lib/src/bootloader.rs

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

34
use anyhow::{anyhow, bail, Context, Result};
@@ -9,7 +10,7 @@ use fn_error_context::context;
910
use bootc_blockdev::{Partition, PartitionTable};
1011
use bootc_mount as mount;
1112

12-
use crate::bootc_composefs::boot::mount_esp;
13+
use crate::bootc_composefs::boot::{mount_esp, SecurebootKeys};
1314
use crate::{discoverable_partition_specification, utils};
1415

1516
/// The name of the mountpoint for efi (as a subdirectory of /boot, or at the toplevel)
@@ -19,6 +20,9 @@ pub(crate) const EFI_DIR: &str = "efi";
1920
#[allow(dead_code)]
2021
const BOOTUPD_UPDATES: &str = "usr/lib/bootupd/updates";
2122

23+
// from: https://github.com/systemd/systemd/blob/26b2085d54ebbfca8637362eafcb4a8e3faf832f/man/systemd-boot.xml#L392
24+
const SYSTEMD_KEY_DIR: &str = "loader/keys";
25+
2226
#[allow(dead_code)]
2327
pub(crate) fn esp_in(device: &PartitionTable) -> Result<&Partition> {
2428
device
@@ -74,6 +78,7 @@ pub(crate) fn install_systemd_boot(
7478
_rootfs: &Utf8Path,
7579
_configopts: &crate::install::InstallConfigOpts,
7680
_deployment_path: Option<&str>,
81+
autoenroll: Option<SecurebootKeys>,
7782
) -> Result<()> {
7883
let esp_part = device
7984
.find_partition_of_type(discoverable_partition_specification::ESP)
@@ -87,7 +92,35 @@ pub(crate) fn install_systemd_boot(
8792
Command::new("bootctl")
8893
.args(["install", "--esp-path", esp_path.as_str()])
8994
.log_debug()
90-
.run_inherited_with_cmd_context()
95+
.run_inherited_with_cmd_context()?;
96+
97+
if let Some(SecurebootKeys { dir, keys }) = autoenroll {
98+
let path = esp_path.join(SYSTEMD_KEY_DIR);
99+
create_dir_all(&path)?;
100+
101+
let keys_dir = esp_mount
102+
.fd
103+
.open_dir(SYSTEMD_KEY_DIR)
104+
.with_context(|| format!("Opening {path}"))?;
105+
106+
for filename in keys.iter() {
107+
let p = path.join(&filename);
108+
109+
// create directory if it doesn't already exist
110+
if let Some(parent) = p.parent() {
111+
create_dir_all(parent)?;
112+
}
113+
114+
dir.copy(&filename, &keys_dir, &filename)
115+
.with_context(|| format!("Copying secure boot key: {p}"))?;
116+
println!("Wrote Secure Boot key: {p}");
117+
}
118+
if keys.is_empty() {
119+
tracing::debug!("No Secure Boot keys provided for systemd-boot enrollment");
120+
}
121+
}
122+
123+
Ok(())
91124
}
92125

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

docs/src/man/bootc-install.8.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ updates.
2828
An installation is not simply a copy of the container filesystem, but
2929
includes other setup and metadata.
3030

31+
## Secure Boot Keys
32+
33+
When installing with `systemd-boot`, bootc can let `systemd-boot` can handle enrollment of Secure Boot keys by putting signed EFI signature lists in `/usr/lib/bootc/install/secureboot-keys` which will copy over into `ESP/loader/keys` after bootloader installation. The keys will be copied to `loader/keys` subdirectory of the ESP. after installing `systemd-boot` to the system. More information on how key enrollment works with `systemd-boot` is available in the [systemd-boot](https://github.com/systemd/systemd/blob/26b2085d54ebbfca8637362eafcb4a8e3faf832f/man/systemd-boot.xml#L392) man page.
34+
3135
<!-- BEGIN GENERATED OPTIONS -->
3236
<!-- END GENERATED OPTIONS -->
3337

0 commit comments

Comments
 (0)