Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions crates/lib/src/bootc_composefs/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@
//! 1. **Primary**: New/upgraded deployment (default boot target)
//! 2. **Secondary**: Currently booted deployment (rollback option)

use std::ffi::OsStr;
use std::fs::create_dir_all;
use std::io::Write;
use std::path::Path;
use std::{ffi::OsStr, os::unix::ffi::OsStrExt};

use anyhow::{anyhow, Context, Result};
use anyhow::{anyhow, bail, Context, Result};
use bootc_blockdev::find_parent_devices;
use bootc_kernel_cmdline::utf8::{Cmdline, Parameter};
use bootc_mount::inspect_filesystem_of_dir;
Expand Down Expand Up @@ -132,6 +132,8 @@ const SYSTEMD_LOADER_CONF_PATH: &str = "loader/loader.conf";
const INITRD: &str = "initrd";
const VMLINUZ: &str = "vmlinuz";

const BOOTC_AUTOENROLL_PATH: &str = "usr/lib/bootc/keys";

/// We want to be able to control the ordering of UKIs so we put them in a directory that's not the
/// directory specified by the BLS spec. We do this because we want systemd-boot to only look at
/// our config files and not show the actual UKIs in the bootloader menu
Expand Down Expand Up @@ -1142,6 +1144,46 @@ pub(crate) fn setup_composefs_uki_boot(
Ok(())
}

pub struct AutoEnroll {
pub dir: Dir,
pub keys: Vec<Utf8PathBuf>,
}

fn get_systemd_boot_autoenroll(fs: &Dir, p: &str) -> Result<Option<AutoEnroll>> {
let mut entries = vec![];

let keys_dir = match fs.open_dir(p) {
Ok(d) => d,
Err(_) => return Ok(None), // if the directory doesn't exist it just means we don't have any keys to enroll
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should throw an error if this error is anything other than ErrorKind::NotFound. We could use fs.open_dir_optional

};

for entry in keys_dir.entries()? {
let file = entry?;

let name = file.file_name();
if !file.file_type()?.is_file() {
bail!("/{p}/{name:?} is a not a regular file");
}

if !name.as_bytes().ends_with(b".auth") {
continue;
}

let path = match Utf8PathBuf::from_os_string(name.clone()) {
Ok(p) => p,
Err(_) => bail!("couldn't get pathbuf: /{p}/{name:?}"),
};
entries.push(path);
}
if entries.len() > 0 {
return Ok(Some(AutoEnroll {
dir: keys_dir,
keys: entries,
}));
}
return Ok(None);
}

#[context("Setting up composefs boot")]
pub(crate) fn setup_composefs_boot(
root_setup: &RootSetup,
Expand Down Expand Up @@ -1181,6 +1223,7 @@ pub(crate) fn setup_composefs_boot(
&root_setup.physical_root_path,
&state.config_opts,
None,
get_systemd_boot_autoenroll(&mounted_fs, BOOTC_AUTOENROLL_PATH)?,
)?;
}

Expand Down
31 changes: 29 additions & 2 deletions crates/lib/src/bootloader.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use std::fs::create_dir_all;
use std::process::Command;

use anyhow::{anyhow, bail, Context, Result};
use bootc_utils::CommandRunExt;
use camino::Utf8Path;
use cap_std_ext::cap_std::fs::Dir;
use cap_std_ext::dirext::CapStdExtDirExt;
use fn_error_context::context;

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

use crate::bootc_composefs::boot::mount_esp;
use crate::bootc_composefs::boot::{mount_esp, AutoEnroll};
use crate::{discoverable_partition_specification, utils};

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

const SYSTEMD_AUTOENROLL: &str = "loader/keys/auto";

#[allow(dead_code)]
pub(crate) fn esp_in(device: &PartitionTable) -> Result<&Partition> {
device
Expand Down Expand Up @@ -74,6 +78,7 @@ pub(crate) fn install_systemd_boot(
_rootfs: &Utf8Path,
_configopts: &crate::install::InstallConfigOpts,
_deployment_path: Option<&str>,
autoenroll: Option<AutoEnroll>,
) -> Result<()> {
let esp_part = device
.find_partition_of_type(discoverable_partition_specification::ESP)
Expand All @@ -87,7 +92,29 @@ pub(crate) fn install_systemd_boot(
Command::new("bootctl")
.args(["install", "--esp-path", esp_path.as_str()])
.log_debug()
.run_inherited_with_cmd_context()
.run_inherited_with_cmd_context()?;

match autoenroll {
None => return Ok(()),
Some(AutoEnroll { dir, keys }) => {
println!("Autoenrolling keys");
let path = esp_path.join(SYSTEMD_AUTOENROLL);
create_dir_all(&path)?;

let keys_dir = esp_mount
.fd
.open_dir(SYSTEMD_AUTOENROLL)
.with_context(|| format!("Opening {path}"))?;
for filename in keys.iter() {
let p = path.join(filename.clone());
keys_dir
.atomic_write(&filename, dir.read(&filename)?)
.with_context(|| format!("Writing secure boot key: {p}"))?;
println!("Wrote secure boot key: {p}");
}
}
}
Ok(())
}

#[context("Installing bootloader using zipl")]
Expand Down
Loading