Skip to content
Draft
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
7 changes: 7 additions & 0 deletions nix/modules/lanzaboote.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ let
loaderConfigFile = loaderSettingsFormat.generate "loader.conf" cfg.settings;

configurationLimit = if cfg.configurationLimit == null then 0 else cfg.configurationLimit;

bootMountPoint =
if config.boot.loader.systemd-boot.xbootldrMountPoint != null then
config.boot.loader.systemd-boot.xbootldrMountPoint
else
config.boot.loader.efi.efiSysMountPoint;
in
{
options.boot.lanzaboote = {
Expand Down Expand Up @@ -139,6 +145,7 @@ in
--private-key ${cfg.privateKeyFile} \
--configuration-limit ${toString configurationLimit} \
${config.boot.loader.efi.efiSysMountPoint} \
${bootMountPoint} \
/nix/var/nix/profiles/system-*-link
'';
};
Expand Down
2 changes: 1 addition & 1 deletion rust/tool/shared/src/esp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::architecture::Architecture;
/// Generic ESP paths which can be specific to a bootloader
pub trait EspPaths<const N: usize> {
/// Build an ESP path structure out of the ESP root directory
fn new(esp: impl AsRef<Path>, arch: Architecture) -> Self;
fn new(esp: impl AsRef<Path>, boot: impl AsRef<Path>, arch: Architecture) -> Self;

/// Return the used file paths to store as garbage collection roots.
fn iter(&self) -> std::array::IntoIter<&PathBuf, N>;
Expand Down
6 changes: 3 additions & 3 deletions rust/tool/shared/src/pe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl StubParameters {
initrd_path: &Path,
kernel_target: &Path,
initrd_target: &Path,
esp: &Path,
boot: &Path,
) -> Result<Self> {
// Resolve maximally those paths
// We won't verify they are store paths, otherwise the mocking strategy will fail for our
Expand All @@ -44,8 +44,8 @@ impl StubParameters {
lanzaboote_store_path: lanzaboote_stub.to_path_buf(),
kernel_store_path: kernel_path.to_path_buf(),
initrd_store_path: initrd_path.to_path_buf(),
kernel_path_at_esp: esp_relative_uefi_path(esp, kernel_target)?,
initrd_path_at_esp: esp_relative_uefi_path(esp, initrd_target)?,
kernel_path_at_esp: esp_relative_uefi_path(boot, kernel_target)?,
initrd_path_at_esp: esp_relative_uefi_path(boot, initrd_target)?,
kernel_cmdline: Vec::new(),
os_release_contents: Vec::new(),
})
Expand Down
5 changes: 5 additions & 0 deletions rust/tool/systemd/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ struct InstallCommand {
/// EFI system partition mountpoint (e.g. efiSysMountPoint)
esp: PathBuf,

/// $BOOT as per [Boot Loader Specification](https://uapi-group.org/specifications/specs/boot_loader_specification/#the-boot-partition-placeholder)
/// (i.e. xbootldrMountPoint if exists, otherwise efiSysMountPoint)
boot: PathBuf,

/// List of generation links (e.g. /nix/var/nix/profiles/system-*-link)
generations: Vec<PathBuf>,
}
Expand Down Expand Up @@ -103,6 +107,7 @@ fn install(args: InstallCommand) -> Result<()> {
local_signer,
args.configuration_limit,
args.esp,
args.boot,
args.generations,
)
.install()
Expand Down
38 changes: 23 additions & 15 deletions rust/tool/systemd/src/esp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use lanzaboote_tool::esp::EspPaths;
pub struct SystemdEspPaths {
pub esp: PathBuf,
pub efi: PathBuf,
pub boot: PathBuf,
pub boot_efi: PathBuf,
pub nixos: PathBuf,
pub linux: PathBuf,
pub efi_fallback_dir: PathBuf,
Expand All @@ -19,26 +21,30 @@ pub struct SystemdEspPaths {
pub systemd_boot_loader_config: PathBuf,
}

impl EspPaths<10> for SystemdEspPaths {
fn new(esp: impl AsRef<Path>, architecture: Architecture) -> Self {
impl EspPaths<12> for SystemdEspPaths {
fn new(esp: impl AsRef<Path>, boot: impl AsRef<Path>, architecture: Architecture) -> Self {
let esp = esp.as_ref();
let efi = esp.join("EFI");
let efi_nixos = efi.join("nixos");
let efi_linux = efi.join("Linux");
let efi_systemd = efi.join("systemd");
let efi_efi_fallback_dir = efi.join("BOOT");
let boot = boot.as_ref();
let esp_efi = esp.join("EFI");
let boot_efi = boot.join("EFI");
let boot_efi_nixos = boot_efi.join("nixos");
let boot_efi_linux = boot_efi.join("Linux");
let esp_efi_systemd = esp_efi.join("systemd");
let esp_efi_efi_fallback_dir = esp_efi.join("BOOT");
let loader = esp.join("loader");
let systemd_boot_loader_config = loader.join("loader.conf");

Self {
esp: esp.to_path_buf(),
efi,
nixos: efi_nixos,
linux: efi_linux,
efi_fallback_dir: efi_efi_fallback_dir.clone(),
efi_fallback: efi_efi_fallback_dir.join(architecture.efi_fallback_filename()),
systemd: efi_systemd.clone(),
systemd_boot: efi_systemd.join(architecture.systemd_filename()),
efi: esp_efi,
boot: boot.to_path_buf(),
boot_efi,
nixos: boot_efi_nixos,
linux: boot_efi_linux,
efi_fallback_dir: esp_efi_efi_fallback_dir.clone(),
efi_fallback: esp_efi_efi_fallback_dir.join(architecture.efi_fallback_filename()),
systemd: esp_efi_systemd.clone(),
systemd_boot: esp_efi_systemd.join(architecture.systemd_filename()),
loader,
systemd_boot_loader_config,
}
Expand All @@ -52,10 +58,12 @@ impl EspPaths<10> for SystemdEspPaths {
&self.linux
}

fn iter(&self) -> std::array::IntoIter<&PathBuf, 10> {
fn iter(&self) -> std::array::IntoIter<&PathBuf, 12> {
[
&self.esp,
&self.efi,
&self.boot,
&self.boot_efi,
&self.nixos,
&self.linux,
&self.efi_fallback_dir,
Expand Down
9 changes: 5 additions & 4 deletions rust/tool/systemd/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ impl<S: Signer> Installer<S> {
signer: S,
configuration_limit: usize,
esp: PathBuf,
boot: PathBuf,
generation_links: Vec<PathBuf>,
) -> Self {
let mut gc_roots = Roots::new();
let esp_paths = SystemdEspPaths::new(esp, arch);
let esp_paths = SystemdEspPaths::new(esp, boot, arch);
gc_roots.extend(esp_paths.iter());

Self {
Expand Down Expand Up @@ -253,7 +254,7 @@ impl<S: Signer> Installer<S> {
&initrd_location,
&kernel_target,
&initrd_target,
&self.esp_paths.esp,
&self.esp_paths.boot,
)?
.with_cmdline(&kernel_cmdline)
.with_os_release_contents(os_release_contents.as_bytes());
Expand Down Expand Up @@ -283,11 +284,11 @@ impl<S: Signer> Installer<S> {
let stub = fs::read(&stub_target)
.with_context(|| format!("Failed to read the stub: {}", stub_target.display()))?;
let kernel_path = resolve_efi_path(
&self.esp_paths.esp,
&self.esp_paths.boot,
pe::read_section_data(&stub, ".linux").context("Missing kernel path.")?,
)?;
let initrd_path = resolve_efi_path(
&self.esp_paths.esp,
&self.esp_paths.boot,
pe::read_section_data(&stub, ".initrd").context("Missing initrd path.")?,
)?;

Expand Down
2 changes: 2 additions & 0 deletions rust/tool/systemd/tests/integration/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ fn random_string(length: usize) -> String {
pub fn lanzaboote_install(
config_limit: u64,
esp_mountpoint: &Path,
boot_mountpoint: &Path,
generation_links: impl IntoIterator<Item = impl AsRef<OsStr>>,
) -> Result<Output> {
// To simplify the test setup, we use the systemd stub here instead of the lanzaboote stub. See
Expand Down Expand Up @@ -172,6 +173,7 @@ pub fn lanzaboote_install(
.arg("--configuration-limit")
.arg(config_limit.to_string())
.arg(esp_mountpoint)
.arg(boot_mountpoint)
.args(generation_links)
.output()?;

Expand Down
59 changes: 47 additions & 12 deletions rust/tool/systemd/tests/integration/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::common::{self, count_files};
#[test]
fn keep_only_configured_number_of_generations() -> Result<()> {
let esp_mountpoint = tempdir()?;
let boot_mountpoint = tempdir()?;
let tmpdir = tempdir()?;
let profiles = tempdir()?;
let generation_links: Vec<PathBuf> = [1, 2, 3]
Expand All @@ -18,11 +19,17 @@ fn keep_only_configured_number_of_generations() -> Result<()> {
.expect("Failed to setup generation link")
})
.collect();
let stub_count = || count_files(&esp_mountpoint.path().join("EFI/Linux")).unwrap();
let kernel_and_initrd_count = || count_files(&esp_mountpoint.path().join("EFI/nixos")).unwrap();
let stub_count = || count_files(&boot_mountpoint.path().join("EFI/Linux")).unwrap();
let kernel_and_initrd_count =
|| count_files(&boot_mountpoint.path().join("EFI/nixos")).unwrap();

// Install all 3 generations.
let output0 = common::lanzaboote_install(0, esp_mountpoint.path(), generation_links.clone())?;
let output0 = common::lanzaboote_install(
0,
esp_mountpoint.path(),
boot_mountpoint.path(),
generation_links.clone(),
)?;
assert!(output0.status.success());
assert_eq!(stub_count(), 3, "Wrong number of stubs after installation");
assert_eq!(
Expand All @@ -33,7 +40,12 @@ fn keep_only_configured_number_of_generations() -> Result<()> {

// Call `lanzatool install` again with a config limit of 2 and assert that one is deleted.
// In addition, the garbage kernel should be deleted as well.
let output1 = common::lanzaboote_install(2, esp_mountpoint.path(), generation_links)?;
let output1 = common::lanzaboote_install(
2,
esp_mountpoint.path(),
boot_mountpoint.path(),
generation_links,
)?;
assert!(output1.status.success());
assert_eq!(stub_count(), 2, "Wrong number of stubs after gc.");
assert_eq!(
Expand All @@ -48,6 +60,7 @@ fn keep_only_configured_number_of_generations() -> Result<()> {
#[test]
fn delete_garbage_kernel() -> Result<()> {
let esp_mountpoint = tempdir()?;
let boot_mountpoint = tempdir()?;
let tmpdir = tempdir()?;
let profiles = tempdir()?;
let generation_links: Vec<PathBuf> = [1, 2, 3]
Expand All @@ -57,22 +70,33 @@ fn delete_garbage_kernel() -> Result<()> {
.expect("Failed to setup generation link")
})
.collect();
let stub_count = || count_files(&esp_mountpoint.path().join("EFI/Linux")).unwrap();
let kernel_and_initrd_count = || count_files(&esp_mountpoint.path().join("EFI/nixos")).unwrap();
let stub_count = || count_files(&boot_mountpoint.path().join("EFI/Linux")).unwrap();
let kernel_and_initrd_count =
|| count_files(&boot_mountpoint.path().join("EFI/nixos")).unwrap();

// Install all 3 generations.
let output0 = common::lanzaboote_install(0, esp_mountpoint.path(), generation_links.clone())?;
let output0 = common::lanzaboote_install(
0,
esp_mountpoint.path(),
boot_mountpoint.path(),
generation_links.clone(),
)?;
assert!(output0.status.success());

// Create a garbage kernel, which should be deleted.
fs::write(
esp_mountpoint.path().join("EFI/nixos/kernel-garbage.efi"),
boot_mountpoint.path().join("EFI/nixos/kernel-garbage.efi"),
"garbage",
)?;

// Call `lanzatool install` again with a config limit of 2.
// In addition, the garbage kernel should be deleted as well.
let output1 = common::lanzaboote_install(2, esp_mountpoint.path(), generation_links)?;
let output1 = common::lanzaboote_install(
2,
esp_mountpoint.path(),
boot_mountpoint.path(),
generation_links,
)?;
assert!(output1.status.success());

assert_eq!(stub_count(), 2, "Wrong number of stubs after gc.");
Expand All @@ -88,6 +112,7 @@ fn delete_garbage_kernel() -> Result<()> {
#[test]
fn keep_unrelated_files_on_esp() -> Result<()> {
let esp_mountpoint = tempdir()?;
let boot_mountpoint = tempdir()?;
let tmpdir = tempdir()?;
let profiles = tempdir()?;
let generation_links: Vec<PathBuf> = [1, 2, 3]
Expand All @@ -99,11 +124,16 @@ fn keep_unrelated_files_on_esp() -> Result<()> {
.collect();

// Install all 3 generations.
let output0 = common::lanzaboote_install(0, esp_mountpoint.path(), generation_links.clone())?;
let output0 = common::lanzaboote_install(
0,
esp_mountpoint.path(),
boot_mountpoint.path(),
generation_links.clone(),
)?;
assert!(output0.status.success());

let unrelated_loader_config = esp_mountpoint.path().join("loader/loader.conf");
let unrelated_uki = esp_mountpoint.path().join("EFI/Linux/ubuntu.efi");
let unrelated_uki = boot_mountpoint.path().join("EFI/Linux/ubuntu.efi");
let unrelated_os = esp_mountpoint.path().join("EFI/windows");
let unrelated_firmware = esp_mountpoint.path().join("dell");
fs::File::create(&unrelated_loader_config)?;
Expand All @@ -112,7 +142,12 @@ fn keep_unrelated_files_on_esp() -> Result<()> {
fs::create_dir(&unrelated_firmware)?;

// Call `lanzatool install` again with a config limit of 2.
let output1 = common::lanzaboote_install(2, esp_mountpoint.path(), generation_links)?;
let output1 = common::lanzaboote_install(
2,
esp_mountpoint.path(),
boot_mountpoint.path(),
generation_links,
)?;
assert!(output1.status.success());

assert!(unrelated_loader_config.exists());
Expand Down
Loading