lanzatool.install: init

This commit is contained in:
nikstur 2022-11-23 15:26:26 +01:00
parent cfff037390
commit 4356d342a2
9 changed files with 323 additions and 1 deletions

View File

@ -95,6 +95,17 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "goblin"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "572564d6cba7d09775202c8e7eebc4d534d5ae36578ab402fb21e182a0ac9505"
dependencies = [
"log",
"plain",
"scroll",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.0" version = "0.4.0"
@ -110,6 +121,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "itoa"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]] [[package]]
name = "lanztool" name = "lanztool"
version = "0.1.0" version = "0.1.0"
@ -117,6 +134,9 @@ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
"ed25519-compact", "ed25519-compact",
"goblin",
"serde",
"serde_json",
] ]
[[package]] [[package]]
@ -125,6 +145,15 @@ version = "0.2.137"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.16.0" version = "1.16.0"
@ -137,6 +166,12 @@ version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]] [[package]]
name = "proc-macro-error" name = "proc-macro-error"
version = "1.0.4" version = "1.0.4"
@ -179,6 +214,63 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "scroll"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
dependencies = [
"scroll_derive",
]
[[package]]
name = "scroll_derive"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.10.0"

View File

@ -9,3 +9,6 @@ edition = "2021"
anyhow = "1.0.66" anyhow = "1.0.66"
clap = { version = "4.0.26", features = ["derive"] } clap = { version = "4.0.26", features = ["derive"] }
ed25519-compact = "2.0.2" ed25519-compact = "2.0.2"
goblin = "0.6.0"
serde = { version = "1.0.147", features = ["derive"] }
serde_json = "1.0.89"

View File

@ -0,0 +1,24 @@
{
"v1": {
"init": "/nix/store/7zrsjhxi0c93m2l89rj8jdp9khm8fc6s-nixos-system-tuxedo-22.11.20221115.85d6b39/init",
"initrd": "/nix/store/7a4plccwni1sldhyra75f7m44xgsgiqw-initrd-linux-6.0.8/initrd",
"kernel": "/nix/store/nsw0422iwp4linayqx727pi4fdyja0wv-linux-6.0.8/bzImage",
"kernelParams": [
"amd_iommu=on",
"amd_iommu=pt",
"iommu=pt",
"kvm.ignore_msrs=1",
"kvm.report_ignored_msrs=0",
"udev.log_priority=3",
"systemd.unified_cgroup_hierarchy=1",
"loglevel=4"
],
"label": "NixOS 21.11.20210810.dirty (Linux 5.15.30)",
"toplevel": "/nix/store/7zrsjhxi0c93m2l89rj8jdp9khm8fc6s-nixos-system-tuxedo-22.11.20221115.85d6b39",
"extension": {
"esp": "test",
"bootctl": "/nix/store/89366aivz6v8a34yyni2m04ca9hwrl92-systemd-250.4/bin/bootctl",
"osRelease": "/nix/store/734vglb01ssz73wlihad7xa9yzvwlvx6-etc-os-release"
}
}
}

View File

@ -0,0 +1,36 @@
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct Bootspec {
pub v1: GenerationV1,
}
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct GenerationV1 {
/// 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,
pub extension: Extension,
}
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Extension {
pub esp: String,
pub bootctl: PathBuf,
pub os_release: PathBuf,
}

View File

@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
use anyhow::Result; use anyhow::Result;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use crate::crypto; use crate::{crypto, install};
#[derive(Parser)] #[derive(Parser)]
pub struct Cli { pub struct Cli {
@ -20,6 +20,10 @@ pub enum Commands {
Sign { file: PathBuf, private_key: PathBuf }, Sign { file: PathBuf, private_key: PathBuf },
/// Sign /// Sign
Verify { file: PathBuf, public_key: PathBuf }, Verify { file: PathBuf, public_key: PathBuf },
Install {
public_key: PathBuf,
bootspec: PathBuf,
},
} }
impl Cli { impl Cli {
@ -34,6 +38,10 @@ impl Commands {
Commands::Generate => generate(), Commands::Generate => generate(),
Commands::Sign { file, private_key } => sign(&file, &private_key), Commands::Sign { file, private_key } => sign(&file, &private_key),
Commands::Verify { file, public_key } => verify(&file, &public_key), Commands::Verify { file, public_key } => verify(&file, &public_key),
Commands::Install {
public_key,
bootspec,
} => install(&public_key, &bootspec),
} }
} }
} }
@ -77,3 +85,8 @@ fn with_extension(path: &Path, extension: &str) -> PathBuf {
file_path.push(extension); file_path.push(extension);
PathBuf::from(file_path) PathBuf::from(file_path)
} }
fn install(public_key: &Path, bootspec: &Path) -> Result<()> {
let lanzaboote_bin = std::env::var("LANZABOOTE")?;
install::install(public_key, bootspec, Path::new(&lanzaboote_bin))
}

22
rust/lanzatool/src/esp.rs Normal file
View File

@ -0,0 +1,22 @@
use std::path::{Path, PathBuf};
pub struct EspPaths {
pub esp: PathBuf,
pub nixos: PathBuf,
pub kernel: PathBuf,
pub initrd: PathBuf,
}
impl EspPaths {
pub fn new(esp: &str) -> Self {
let esp = Path::new(esp);
let esp_nixos = esp.join("EFI/nixos");
Self {
esp: esp.to_owned(),
nixos: esp_nixos.clone(),
kernel: esp_nixos.join("EFI/nixos"),
initrd: esp_nixos.join("initrd"),
}
}
}

View File

@ -0,0 +1,52 @@
use std::fs;
use std::path::Path;
use std::process::Command;
use anyhow::Result;
use crate::bootspec::Bootspec;
use crate::esp::EspPaths;
use crate::stub;
pub fn install(_: &Path, bootspec: &Path, lanzaboote_bin: &Path) -> Result<()> {
let bootspec_doc: Bootspec = serde_json::from_slice(&fs::read(bootspec)?)?;
let esp_paths = EspPaths::new(&bootspec_doc.v1.extension.esp);
stub::assemble(
lanzaboote_bin,
&bootspec_doc.v1.extension.os_release,
&bootspec_doc.v1.kernel_params,
&esp_paths.kernel,
&esp_paths.initrd,
)
.unwrap();
// Copy the files to the ESP
fs::create_dir_all(&esp_paths.nixos)?;
fs::copy(bootspec_doc.v1.kernel, esp_paths.kernel)?;
fs::copy(bootspec_doc.v1.initrd, esp_paths.initrd)?;
// install_systemd_boot(bootctl, &esp)?;
Ok(())
}
fn install_systemd_boot(bootctl: &Path, esp: &Path) -> Result<()> {
let args = vec![
String::from("install"),
String::from("--path"),
esp.display().to_string(),
];
let status = Command::new(&bootctl).args(&args).status()?;
if !status.success() {
return Err(anyhow::anyhow!(
"Failed success run `{}` with args `{:?}`",
&bootctl.display(),
&args
)
.into());
}
Ok(())
}

View File

@ -1,5 +1,9 @@
mod bootspec;
mod cli; mod cli;
mod crypto; mod crypto;
mod esp;
mod install;
mod stub;
use std::process; use std::process;

View File

@ -0,0 +1,76 @@
use std::fs;
use std::os::unix::fs::MetadataExt;
use std::path::Path;
use std::process::Command;
use anyhow::Result;
use goblin;
pub fn assemble(
lanzaboote_bin: &Path,
os_release: &Path,
kernel_cmdline: &[String],
kernel_path: &Path,
initrd_path: &Path,
) -> Result<()> {
// objcopy copies files into the PE binary. That's why we have to write the contents
// of some bootspec properties to disk
let kernel_cmdline_file = Path::new("/tmp/kernel_cmdline");
fs::write(kernel_cmdline_file, kernel_cmdline.join(" "))?;
let kernel_path_file = Path::new("/tmp/kernel_path");
fs::write(kernel_path_file, kernel_path.to_str().unwrap())?;
let initrd_path_file = Path::new("/tmp/initrd_path");
fs::write(initrd_path_file, initrd_path.to_str().unwrap())?;
let pe_binary = fs::read(lanzaboote_bin)?;
let pe = goblin::pe::PE::parse(&pe_binary)?;
let os_release_offs = u64::from(
pe.sections
.iter()
.find(|s| s.name().unwrap() == ".sdmagic")
.and_then(|s| Some(s.size_of_raw_data + s.virtual_address))
.unwrap(),
);
let kernel_cmdline_offs = os_release_offs + file_size(os_release)?;
let initrd_path_offs = kernel_cmdline_offs + file_size(kernel_cmdline_file)?;
let kernel_path_offs = initrd_path_offs + file_size(initrd_path_file)?;
let args = vec![
String::from("--add-section"),
format!(".osrel={}", path_to_string(os_release)),
String::from("--change-section-vma"),
format!(".osrel={:#x}", os_release_offs),
String::from("--add-section"),
format!(".cmdline={}", path_to_string(kernel_cmdline_file)),
String::from("--change-section-vma"),
format!(".cmdline={:#x}", kernel_cmdline_offs),
String::from("--add-section"),
format!(".initrdp={}", path_to_string(initrd_path_file)),
String::from("--change-section-vma"),
format!(".initrdp={:#x}", initrd_path_offs),
String::from("--add-section"),
format!(".kernelp={}", path_to_string(kernel_path_file)),
String::from("--change-section-vma"),
format!(".kernelp={:#x}", kernel_path_offs),
lanzaboote_bin.to_str().unwrap().to_owned(),
String::from("stub.efi"),
];
let status = Command::new("objcopy").args(&args).status()?;
if !status.success() {
return Err(anyhow::anyhow!("Failed to build stub with args `{:?}`", &args).into());
}
Ok(())
}
// All Linux file paths should be convertable to strings
fn path_to_string(path: &Path) -> String {
path.to_owned().into_os_string().into_string().unwrap()
}
fn file_size(path: &Path) -> Result<u64> {
Ok(fs::File::open(path)?.metadata()?.size())
}