lanzatool(bootspec): introduce DetSys's bootspec library

This commit is contained in:
Raito Bezarius 2022-12-17 23:55:38 +01:00
parent 30be791826
commit 92e7e4f49a
8 changed files with 89 additions and 84 deletions

View File

@ -66,6 +66,15 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bootspec"
version = "0.1.0"
source = "git+https://github.com/RaitoBezarius/bootspec-1?branch=secureboot-needs#a5d327ceb25da6ae4147fbf0b15088f44214a91a"
dependencies = [
"serde",
"serde_json",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.77" version = "1.0.77"
@ -208,6 +217,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"blake3", "blake3",
"bootspec",
"clap", "clap",
"goblin", "goblin",
"nix", "nix",
@ -349,18 +359,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.147" version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.147" version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -392,9 +402,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.103" version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -10,7 +10,9 @@ anyhow = "1.0.66"
clap = { version = "4.0.26", features = ["derive"] } clap = { version = "4.0.26", features = ["derive"] }
goblin = "0.6.0" goblin = "0.6.0"
nix = { version = "0.25.0", default-features = false, features = [ "fs" ] } nix = { version = "0.25.0", default-features = false, features = [ "fs" ] }
serde = { version = "1.0.147", features = ["derive"] } serde = { version = "1.0.151", features = ["derive"] }
serde_json = "1.0.89" serde_json = "1.0.89"
tempfile = "3.3.0" tempfile = "3.3.0"
blake3 = "1.3.3" blake3 = "1.3.3"
# TODO: wait for a upstream release and pin it.
bootspec = { git = "https://github.com/RaitoBezarius/bootspec-1", branch = "secureboot-needs" }

View File

@ -1,21 +1,23 @@
{ {
"init": "/run/current-system/init", "v1": {
"initrd": "/run/current-system/initrd", "init": "/run/current-system/init",
"kernel": "/run/current-system/kernel", "initrd": "/run/current-system/initrd",
"kernelParams": [ "kernel": "/run/current-system/kernel",
"amd_iommu=on", "kernelParams": [
"amd_iommu=pt", "amd_iommu=on",
"iommu=pt", "amd_iommu=pt",
"kvm.ignore_msrs=1", "iommu=pt",
"kvm.report_ignored_msrs=0", "kvm.ignore_msrs=1",
"udev.log_priority=3", "kvm.report_ignored_msrs=0",
"systemd.unified_cgroup_hierarchy=1", "udev.log_priority=3",
"loglevel=4" "systemd.unified_cgroup_hierarchy=1",
], "loglevel=4"
"label": "LanzaOS", ],
"toplevel": "/run/current-system", "label": "LanzaOS",
"specialisation": {}, "toplevel": "/run/current-system",
"extension": { "specialisation": {},
"osRelease": "/etc/os-release" "extensions": {
"org.lanzaboote.osRelease": "/etc/os-release"
}
} }
} }

View File

@ -1,32 +0,0 @@
use std::collections::HashMap;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Bootspec {
/// Label for the system closure
pub label: String,
/// Path to kernel (bzImage) -- $toplevel/kernel
pub kernel: PathBuf,
/// list of kernel parameters
pub kernel_params: Vec<String>,
/// Path to the init script
pub init: PathBuf,
/// Path to initrd -- $toplevel/initrd
pub initrd: PathBuf,
/// Path to "append-initrd-secrets" script -- $toplevel/append-initrd-secrets
pub initrd_secrets: Option<PathBuf>,
/// config.system.build.toplevel path
pub toplevel: PathBuf,
/// Mapping of specialisation names to their boot.json
pub specialisation: HashMap<String, Bootspec>,
pub extension: Extension,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Extension {
pub os_release: PathBuf,
}

View File

@ -1,7 +1,7 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use crate::generation::Generation; use crate::generation::OSGeneration;
pub struct EspPaths { pub struct EspPaths {
pub esp: PathBuf, pub esp: PathBuf,
@ -17,7 +17,7 @@ pub struct EspPaths {
} }
impl EspPaths { impl EspPaths {
pub fn new(esp: impl AsRef<Path>, generation: &Generation) -> Result<Self> { pub fn new(esp: impl AsRef<Path>, generation: &OSGeneration) -> Result<Self> {
let esp = esp.as_ref(); let esp = esp.as_ref();
let esp_nixos = esp.join("EFI/nixos"); let esp_nixos = esp.join("EFI/nixos");
let esp_linux = esp.join("EFI/Linux"); let esp_linux = esp.join("EFI/Linux");
@ -66,7 +66,7 @@ fn nixos_path(path: impl AsRef<Path>, name: &str) -> Result<PathBuf> {
Ok(PathBuf::from(nixos_filename)) Ok(PathBuf::from(nixos_filename))
} }
fn generation_path(generation: &Generation) -> PathBuf { fn generation_path(generation: &OSGeneration) -> PathBuf {
if let Some(specialisation_name) = generation.is_specialized() { if let Some(specialisation_name) = generation.is_specialized() {
PathBuf::from(format!( PathBuf::from(format!(
"nixos-generation-{}-specialisation-{}.efi", "nixos-generation-{}-specialisation-{}.efi",

View File

@ -1,22 +1,46 @@
use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::fs; use std::fs;
use std::path::Path; use std::path::{Path, PathBuf};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use bootspec::BootJson;
use bootspec::SpecialisationName;
use bootspec::generation::Generation;
use crate::bootspec::Bootspec; // TODO: actually, I'm not sure it's a good thing to have Default
// we should maybe have TryDefault?
#[derive(Debug)] // discuss this with upstream.
pub struct Generation { #[derive(Debug, Default, Clone, Serialize, Deserialize)]
version: u64, pub struct SecureBootExtension {
specialisation_name: Option<String>, #[serde(rename="org.lanzaboote.osRelease")]
pub bootspec: Bootspec, pub os_release: PathBuf
} }
impl Generation { pub type ExtendedBootJson = BootJson<SecureBootExtension>;
#[derive(Debug)]
pub struct OSGeneration {
/// Top-level nixpkgs version
version: u64,
/// Top-level specialisation name
specialisation_name: Option<SpecialisationName>,
/// Top-level bootspec document
pub bootspec: ExtendedBootJson,
}
fn into_boot_json(generation: Generation<SecureBootExtension>) -> Result<ExtendedBootJson> {
Ok(match generation {
Generation::V1(json) => json,
_ => panic!("Failed")
})
}
impl OSGeneration {
pub fn from_toplevel(toplevel: impl AsRef<Path>) -> Result<Self> { pub fn from_toplevel(toplevel: impl AsRef<Path>) -> Result<Self> {
let bootspec_path = toplevel.as_ref().join("bootspec/boot.v1.json"); let bootspec_path = toplevel.as_ref().join("bootspec/boot.json");
let bootspec: Bootspec = serde_json::from_slice( let generation: Generation<SecureBootExtension> = serde_json::from_slice(
&fs::read(bootspec_path).context("Failed to read bootspec file")?, &fs::read(bootspec_path).context("Failed to read bootspec file")?,
) )
.context("Failed to parse bootspec json")?; .context("Failed to parse bootspec json")?;
@ -24,24 +48,24 @@ impl Generation {
Ok(Self { Ok(Self {
version: parse_version(toplevel)?, version: parse_version(toplevel)?,
specialisation_name: None, specialisation_name: None,
bootspec, bootspec: into_boot_json(generation)?,
}) })
} }
pub fn specialise(&self, name: &str, bootspec: &Bootspec) -> Self { pub fn specialise(&self, name: &SpecialisationName, bootspec: &ExtendedBootJson) -> Self {
Self { Self {
version: self.version, version: self.version,
specialisation_name: Some(String::from(name)), specialisation_name: Some(name.clone()),
bootspec: bootspec.clone(), bootspec: bootspec.clone()
} }
} }
pub fn is_specialized(&self) -> Option<String> { pub fn is_specialized(&self) -> Option<SpecialisationName> {
self.specialisation_name.clone() self.specialisation_name.clone()
} }
} }
impl fmt::Display for Generation { impl fmt::Display for OSGeneration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.version) write!(f, "{}", self.version)
} }

View File

@ -8,7 +8,7 @@ use nix::unistd::sync;
use tempfile::tempdir; use tempfile::tempdir;
use crate::esp::EspPaths; use crate::esp::EspPaths;
use crate::generation::Generation; use crate::generation::OSGeneration;
use crate::pe; use crate::pe;
use crate::signature::KeyPair; use crate::signature::KeyPair;
@ -36,7 +36,7 @@ impl Installer {
pub fn install(&self) -> Result<()> { pub fn install(&self) -> Result<()> {
for toplevel in &self.generations { for toplevel in &self.generations {
let generation = Generation::from_toplevel(toplevel).with_context(|| { let generation = OSGeneration::from_toplevel(toplevel).with_context(|| {
format!("Failed to build generation from toplevel: {toplevel:?}") format!("Failed to build generation from toplevel: {toplevel:?}")
})?; })?;
@ -58,7 +58,7 @@ impl Installer {
Ok(()) Ok(())
} }
fn install_generation(&self, generation: &Generation) -> Result<()> { fn install_generation(&self, generation: &OSGeneration) -> Result<()> {
let bootspec = &generation.bootspec; let bootspec = &generation.bootspec;
let esp_paths = EspPaths::new(&self.esp, generation)?; let esp_paths = EspPaths::new(&self.esp, generation)?;
@ -83,7 +83,7 @@ impl Installer {
} }
let systemd_boot = bootspec let systemd_boot = bootspec
.toplevel .toplevel.0
.join("systemd/lib/systemd/boot/efi/systemd-bootx64.efi"); .join("systemd/lib/systemd/boot/efi/systemd-bootx64.efi");
[ [
@ -102,7 +102,7 @@ impl Installer {
let lanzaboote_image = pe::lanzaboote_image( let lanzaboote_image = pe::lanzaboote_image(
&secure_temp_dir, &secure_temp_dir,
&self.lanzaboote_stub, &self.lanzaboote_stub,
&bootspec.extension.os_release, &bootspec.extensions.os_release,
&kernel_cmdline, &kernel_cmdline,
&esp_paths.kernel, &esp_paths.kernel,
&esp_paths.initrd, &esp_paths.initrd,

View File

@ -1,4 +1,3 @@
mod bootspec;
mod cli; mod cli;
mod esp; mod esp;
mod generation; mod generation;