From 0a966234610ffead48e76a79af99c577add5d705 Mon Sep 17 00:00:00 2001 From: nikstur Date: Sat, 26 Nov 2022 22:23:00 +0100 Subject: [PATCH] lanzatool: bootspec from generation The bootspec is now read from each generation so that more than one entry can be generated when calling install --- nix/lanzaboote.nix | 24 ++++++++------ rust/lanzatool/bootspec.json | 3 -- rust/lanzatool/src/bootspec.rs | 3 -- rust/lanzatool/src/cli.rs | 4 +-- rust/lanzatool/src/esp.rs | 13 ++++---- rust/lanzatool/src/generation.rs | 57 +++++++++++++++++++++----------- rust/lanzatool/src/install.rs | 44 +++++++++++------------- 7 files changed, 81 insertions(+), 67 deletions(-) diff --git a/nix/lanzaboote.nix b/nix/lanzaboote.nix index 791ffa9..2a651ad 100644 --- a/nix/lanzaboote.nix +++ b/nix/lanzaboote.nix @@ -38,16 +38,20 @@ in boot.loader.supportsInitrdSecrets = mkForce true; boot.loader.external = { enable = true; - passBootspec = true; - installHook = "${pkgs.writeShellScriptBin "bootinstall" '' - ${optionalString cfg.enrollKeys '' - mkdir -p /tmp/pki - cp -r ${cfg.pkiBundle}/* /tmp/pki - ${sbctlWithPki}/bin/sbctl enroll-keys --yes-this-might-brick-my-machine - ''} - ${cfg.package}/bin/lanzatool install --pki-bundle ${cfg.pkiBundle} --public-key ${cfg.publicKeyFile} --private-key ${cfg.privateKeyFile} "$@" /nix/var/nix/profiles/system-*-link - ''}/bin/bootinstall"; - # ${cfg.package}/bin/lanzatool install ${optionalString cfg.enrollKeys "--auto-enroll"} --pki-bundle ${cfg.pkiBundle} + installHook = pkgs.writeShellScript "bootinstall" '' + ${optionalString cfg.enrollKeys '' + mkdir -p /tmp/pki + cp -r ${cfg.pkiBundle}/* /tmp/pki + ${sbctlWithPki}/bin/sbctl enroll-keys --yes-this-might-brick-my-machine + ''} + + ${cfg.package}/bin/lanzatool install \ + --pki-bundle ${cfg.pkiBundle} \ + --public-key ${cfg.publicKeyFile} \ + --private-key ${cfg.privateKeyFile} \ + ${config.boot.loader.efi.efiSysMountPoint} \ + /nix/var/nix/profiles/system-*-link + ''; }; }; } diff --git a/rust/lanzatool/bootspec.json b/rust/lanzatool/bootspec.json index e710850..5c89e13 100644 --- a/rust/lanzatool/bootspec.json +++ b/rust/lanzatool/bootspec.json @@ -15,9 +15,6 @@ "label": "LanzaOS", "toplevel": "/run/current-system", "extension": { - "esp": "esp", - "systemd": "/run/current-system/systemd", - "bootctl": "/run/current-system/sw/bin/bootctl", "osRelease": "/etc/os-release" } } \ No newline at end of file diff --git a/rust/lanzatool/src/bootspec.rs b/rust/lanzatool/src/bootspec.rs index c5463c3..4afe98d 100644 --- a/rust/lanzatool/src/bootspec.rs +++ b/rust/lanzatool/src/bootspec.rs @@ -25,8 +25,5 @@ pub struct Bootspec { #[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Extension { - pub esp: String, - pub bootctl: PathBuf, pub os_release: PathBuf, - pub systemd: PathBuf, } diff --git a/rust/lanzatool/src/cli.rs b/rust/lanzatool/src/cli.rs index 03bd5dd..f7ff2cc 100644 --- a/rust/lanzatool/src/cli.rs +++ b/rust/lanzatool/src/cli.rs @@ -35,7 +35,7 @@ struct InstallCommand { #[arg(long, default_value = "false")] auto_enroll: bool, - bootspec: PathBuf, + esp: PathBuf, generations: Vec, } @@ -67,7 +67,7 @@ fn install(args: InstallCommand) -> Result<()> { args.private_key, args.pki_bundle, args.auto_enroll, - args.bootspec, + args.esp, args.generations, ) .install() diff --git a/rust/lanzatool/src/esp.rs b/rust/lanzatool/src/esp.rs index 53796fe..9b369fc 100644 --- a/rust/lanzatool/src/esp.rs +++ b/rust/lanzatool/src/esp.rs @@ -1,7 +1,6 @@ use anyhow::{Context, Result}; use std::path::{Path, PathBuf}; -use crate::bootspec::Bootspec; use crate::generation::Generation; pub struct EspPaths { @@ -18,20 +17,22 @@ pub struct EspPaths { } impl EspPaths { - pub fn new(esp: &str, generation: Generation, bootspec: &Bootspec) -> Result { - let esp = Path::new(esp); + pub fn new(esp: impl AsRef, generation: &Generation) -> Result { + let esp = esp.as_ref(); let esp_nixos = esp.join("EFI/nixos"); let esp_linux = esp.join("EFI/Linux"); let esp_systemd = esp.join("EFI/systemd"); let esp_efi_fallback_dir = esp.join("EFI/BOOT"); + let bootspec = &generation.bootspec; + Ok(Self { - esp: esp.to_owned(), + esp: esp.to_path_buf(), nixos: esp_nixos.clone(), kernel: esp_nixos.join(nixos_path(&bootspec.kernel, "bzImage")?), initrd: esp_nixos.join(nixos_path(&bootspec.initrd, "initrd")?), linux: esp_linux.clone(), - lanzaboote_image: esp_linux.join(generation_path(generation)), + lanzaboote_image: esp_linux.join(generation_path(&generation)), efi_fallback_dir: esp_efi_fallback_dir.clone(), efi_fallback: esp_efi_fallback_dir.join("BOOTX64.EFI"), systemd: esp_systemd.clone(), @@ -60,6 +61,6 @@ fn nixos_path(path: impl AsRef, name: &str) -> Result { Ok(PathBuf::from(nixos_filename)) } -fn generation_path(generation: Generation) -> PathBuf { +fn generation_path(generation: &Generation) -> PathBuf { PathBuf::from(format!("nixos-generation-{}.efi", generation)) } diff --git a/rust/lanzatool/src/generation.rs b/rust/lanzatool/src/generation.rs index 5904374..a1f3cb6 100644 --- a/rust/lanzatool/src/generation.rs +++ b/rust/lanzatool/src/generation.rs @@ -1,36 +1,55 @@ use std::fmt; +use std::fs; use std::path::Path; use anyhow::{Context, Result}; +use crate::bootspec::Bootspec; + #[derive(Debug)] -pub struct Generation(u64); +pub struct Generation { + version: u64, + pub bootspec: Bootspec, +} impl Generation { pub fn from_toplevel(toplevel: impl AsRef) -> Result { - let file_name = toplevel.as_ref().file_name().ok_or(anyhow::anyhow!( - "Failed to extract file name from generation" - ))?; + let bootspec_path = toplevel.as_ref().join("bootspec/boot.v1.json"); + let bootspec: Bootspec = serde_json::from_slice( + &fs::read(&bootspec_path).context("Failed to read bootspec file")?, + ) + .context("Failed to parse bootspec json")?; - let file_name_str = file_name - .to_str() - .with_context(|| "Failed to convert file name of generation to string")?; - - let generation_version = file_name_str - .split("-") - .nth(1) - .ok_or(anyhow::anyhow!("Failed to extract version from generation"))?; - - let parsed_generation_version = generation_version.parse().with_context(|| { - format!("Failed to parse generation version: {}", generation_version) - })?; - - Ok(Self(parsed_generation_version)) + Ok(Self { + version: parse_version(toplevel)?, + bootspec, + }) } } impl fmt::Display for Generation { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) + write!(f, "{}", self.version) } } + +fn parse_version(toplevel: impl AsRef) -> Result { + let file_name = toplevel.as_ref().file_name().ok_or(anyhow::anyhow!( + "Failed to extract file name from generation" + ))?; + + let file_name_str = file_name + .to_str() + .with_context(|| "Failed to convert file name of generation to string")?; + + let generation_version = file_name_str + .split("-") + .nth(1) + .ok_or(anyhow::anyhow!("Failed to extract version from generation"))?; + + let parsed_generation_version = generation_version + .parse() + .with_context(|| format!("Failed to parse generation version: {}", generation_version))?; + + Ok(parsed_generation_version) +} diff --git a/rust/lanzatool/src/install.rs b/rust/lanzatool/src/install.rs index 99bc1e5..7562ea6 100644 --- a/rust/lanzatool/src/install.rs +++ b/rust/lanzatool/src/install.rs @@ -7,7 +7,6 @@ use anyhow::{Context, Result}; use nix::unistd::sync; use tempfile::tempdir; -use crate::bootspec::Bootspec; use crate::esp::EspPaths; use crate::generation::Generation; use crate::pe; @@ -20,7 +19,7 @@ pub struct Installer { private_key: PathBuf, _pki_bundle: Option, _auto_enroll: bool, - bootspec: PathBuf, + esp: PathBuf, generations: Vec, } @@ -32,7 +31,7 @@ impl Installer { private_key: PathBuf, _pki_bundle: Option, _auto_enroll: bool, - bootspec: PathBuf, + esp: PathBuf, generations: Vec, ) -> Self { Self { @@ -42,7 +41,7 @@ impl Installer { private_key, _pki_bundle, _auto_enroll, - bootspec, + esp, generations, } } @@ -50,7 +49,7 @@ impl Installer { pub fn install(&self) -> Result<()> { for toplevel in &self.generations { let generation = Generation::from_toplevel(toplevel).with_context(|| { - format!("Failed to extract generation version from: {toplevel:?}") + format!("Failed to build generation from toplevel: {toplevel:?}") })?; println!("Installing generation {generation}"); @@ -64,16 +63,14 @@ impl Installer { pub fn install_generation(&self, generation: Generation) -> Result<()> { println!("Reading bootspec..."); - let bootspec_doc: Bootspec = serde_json::from_slice( - &fs::read(&self.bootspec).context("Failed to read bootspec file")?, - ) - .context("Failed to parse bootspec json")?; + let bootspec = &generation.bootspec; - let esp_paths = EspPaths::new(&bootspec_doc.extension.esp, generation, &bootspec_doc)?; + let esp_paths = EspPaths::new(&self.esp, &generation)?; println!("Assembling lanzaboote image..."); - let kernel_cmdline = assemble_kernel_cmdline(bootspec_doc.init, bootspec_doc.kernel_params); + let kernel_cmdline = + assemble_kernel_cmdline(&bootspec.init, bootspec.kernel_params.clone()); // prepare a secure temporary directory // permission bits are not set, because when files below @@ -86,7 +83,7 @@ impl Installer { let lanzaboote_image = pe::lanzaboote_image( &secure_temp_dir, &self.lanzaboote_stub, - &bootspec_doc.extension.os_release, + &bootspec.extension.os_release, &kernel_cmdline, &esp_paths.kernel, &esp_paths.initrd, @@ -97,8 +94,8 @@ impl Installer { println!("Wrapping initrd into a PE binary..."); let initrd_location = secure_temp_dir.path().join("initrd"); - copy(&bootspec_doc.initrd, &initrd_location)?; - if let Some(initrd_secrets_script) = bootspec_doc.initrd_secrets { + copy(&bootspec.initrd, &initrd_location)?; + if let Some(initrd_secrets_script) = &bootspec.initrd_secrets { append_initrd_secrets(&initrd_secrets_script, &initrd_location)?; } let wrapped_initrd = pe::wrap_initrd(&secure_temp_dir, &self.initrd_stub, &initrd_location) @@ -108,16 +105,15 @@ impl Installer { let signer = Signer::new(&self.public_key, &self.private_key); - let systemd_boot = bootspec_doc - .extension - .systemd - .join("lib/systemd/boot/efi/systemd-bootx64.efi"); + let systemd_boot = bootspec + .toplevel + .join("systemd/lib/systemd/boot/efi/systemd-bootx64.efi"); let files_to_copy_and_sign = [ (&systemd_boot, &esp_paths.efi_fallback), (&systemd_boot, &esp_paths.systemd_boot), (&lanzaboote_image, &esp_paths.lanzaboote_image), - (&bootspec_doc.kernel, &esp_paths.kernel), + (&bootspec.kernel, &esp_paths.kernel), (&wrapped_initrd, &esp_paths.initrd), ]; @@ -160,11 +156,11 @@ pub fn append_initrd_secrets( Ok(()) } -fn assemble_kernel_cmdline(init: PathBuf, kernel_params: Vec) -> Vec { - let init_string = init - .into_os_string() - .into_string() - .expect("Failed to convert init path to string"); +fn assemble_kernel_cmdline(init: &Path, kernel_params: Vec) -> Vec { + let init_string = String::from( + init.to_str() + .expect("Failed to convert init path to string"), + ); let mut kernel_cmdline: Vec = vec![format!("init={}", init_string)]; kernel_cmdline.extend(kernel_params); kernel_cmdline