Merge pull request #6 from blitz/lanzatool-install
lanzatool.install: init
This commit is contained in:
commit
788a112050
|
@ -69,30 +69,14 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "ct-codecs"
|
||||
version = "1.1.1"
|
||||
name = "goblin"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df"
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-compact"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f2d21333b679bbbac680b3eb45c86937e42f69277028f4e97b599b80b86c253"
|
||||
checksum = "572564d6cba7d09775202c8e7eebc4d534d5ae36578ab402fb21e182a0ac9505"
|
||||
dependencies = [
|
||||
"ct-codecs",
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"log",
|
||||
"plain",
|
||||
"scroll",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -110,13 +94,21 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "lanztool"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"ed25519-compact",
|
||||
"goblin",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -125,6 +117,15 @@ version = "0.2.137"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "once_cell"
|
||||
version = "1.16.0"
|
||||
|
@ -137,6 +138,12 @@ version = "6.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "plain"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
|
@ -179,6 +186,63 @@ dependencies = [
|
|||
"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]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
@ -217,12 +281,6 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
|
@ -8,4 +8,6 @@ edition = "2021"
|
|||
[dependencies]
|
||||
anyhow = "1.0.66"
|
||||
clap = { version = "4.0.26", features = ["derive"] }
|
||||
ed25519-compact = "2.0.2"
|
||||
goblin = "0.6.0"
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = "1.0.89"
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
use crate::crypto;
|
||||
use crate::install;
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct Cli {
|
||||
|
@ -14,12 +13,10 @@ pub struct Cli {
|
|||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Commands {
|
||||
/// Generate key pair
|
||||
Generate,
|
||||
/// Sign
|
||||
Sign { file: PathBuf, private_key: PathBuf },
|
||||
/// Sign
|
||||
Verify { file: PathBuf, public_key: PathBuf },
|
||||
Install {
|
||||
public_key: PathBuf,
|
||||
bootspec: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
|
@ -31,49 +28,15 @@ impl Cli {
|
|||
impl Commands {
|
||||
pub fn call(self) -> Result<()> {
|
||||
match self {
|
||||
Commands::Generate => generate(),
|
||||
Commands::Sign { file, private_key } => sign(&file, &private_key),
|
||||
Commands::Verify { file, public_key } => verify(&file, &public_key),
|
||||
Commands::Install {
|
||||
public_key,
|
||||
bootspec,
|
||||
} => install(&public_key, &bootspec),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate() -> Result<()> {
|
||||
let key_pair = crypto::generate_key();
|
||||
|
||||
fs::write("public_key.pem", key_pair.pk.to_pem())?;
|
||||
fs::write("private_key.pem", key_pair.sk.to_pem())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sign(file: &Path, private_key: &Path) -> Result<()> {
|
||||
let message = fs::read(file)?;
|
||||
let private_key = fs::read_to_string(private_key)?;
|
||||
|
||||
let signature = crypto::sign(&message, &private_key)?;
|
||||
|
||||
let file_path = with_extension(file, ".sig");
|
||||
fs::write(file_path, signature.as_slice())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify(file: &Path, public_key: &Path) -> Result<()> {
|
||||
let message = fs::read(file)?;
|
||||
|
||||
let signature_path = with_extension(file, ".sig");
|
||||
let signature = fs::read(signature_path)?;
|
||||
|
||||
let public_key = fs::read_to_string(public_key)?;
|
||||
|
||||
crypto::verify(&message, &signature, &public_key)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn with_extension(path: &Path, extension: &str) -> PathBuf {
|
||||
let mut file_path = path.to_path_buf().into_os_string();
|
||||
file_path.push(extension);
|
||||
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))
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use ed25519_compact::{KeyPair, Noise, PublicKey, SecretKey, Seed, Signature};
|
||||
|
||||
pub fn generate_key() -> KeyPair {
|
||||
KeyPair::from_seed(Seed::default())
|
||||
}
|
||||
|
||||
pub fn sign(message: &[u8], private_key: &str) -> Result<Signature> {
|
||||
let private_key = SecretKey::from_pem(private_key)?;
|
||||
let signature = private_key.sign(message, Some(Noise::generate()));
|
||||
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
pub fn verify(message: &[u8], signature: &[u8], public_key: &str) -> Result<()> {
|
||||
let signature = Signature::from_slice(signature)?;
|
||||
let public_key = PublicKey::from_pem(public_key)?;
|
||||
|
||||
public_key.verify(message, &signature)?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -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("kernel"),
|
||||
initrd: esp_nixos.join("initrd"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
mod bootspec;
|
||||
mod cli;
|
||||
mod crypto;
|
||||
mod esp;
|
||||
mod install;
|
||||
mod stub;
|
||||
|
||||
use std::process;
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
Loading…
Reference in New Issue