lanzatool(bootspec): introduce DetSys's bootspec library
This commit is contained in:
parent
30be791826
commit
92e7e4f49a
|
@ -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",
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"v1": {
|
||||||
"init": "/run/current-system/init",
|
"init": "/run/current-system/init",
|
||||||
"initrd": "/run/current-system/initrd",
|
"initrd": "/run/current-system/initrd",
|
||||||
"kernel": "/run/current-system/kernel",
|
"kernel": "/run/current-system/kernel",
|
||||||
|
@ -15,7 +16,8 @@
|
||||||
"label": "LanzaOS",
|
"label": "LanzaOS",
|
||||||
"toplevel": "/run/current-system",
|
"toplevel": "/run/current-system",
|
||||||
"specialisation": {},
|
"specialisation": {},
|
||||||
"extension": {
|
"extensions": {
|
||||||
"osRelease": "/etc/os-release"
|
"org.lanzaboote.osRelease": "/etc/os-release"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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,
|
|
||||||
}
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
mod bootspec;
|
|
||||||
mod cli;
|
mod cli;
|
||||||
mod esp;
|
mod esp;
|
||||||
mod generation;
|
mod generation;
|
||||||
|
|
Loading…
Reference in New Issue