diff options
| -rw-r--r-- | src/hasher.rs | 54 | ||||
| -rw-r--r-- | src/initramfs.rs | 27 | ||||
| -rw-r--r-- | tests/initramfs.rs | 29 | ||||
| -rw-r--r-- | tests/package_installer.rs | 28 |
4 files changed, 131 insertions, 7 deletions
diff --git a/src/hasher.rs b/src/hasher.rs index da00778..f2bf378 100644 --- a/src/hasher.rs +++ b/src/hasher.rs @@ -1,4 +1,5 @@ use std::ffi::OsString; +use std::path::Path; use std::process::Command; use anyhow::{Context, Result, bail}; @@ -54,6 +55,59 @@ impl CommandRunner for ProcessRunner { } } +/// Runs Hasher commands under the configured non-root Hasher account. +/// Root retains ownership of native rootfs finalization and packaging stages. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SudoUserRunner { + user: OsString, +} + +impl SudoUserRunner { + pub fn new(user: impl Into<OsString>) -> Self { + Self { user: user.into() } + } + + pub fn wrap(&self, invocation: Invocation) -> Invocation { + let mut arguments = vec![ + OsString::from("-n"), + OsString::from("-u"), + self.user.clone(), + ]; + arguments.push(invocation.program); + arguments.extend(invocation.arguments); + Invocation::new("sudo", arguments) + } + + pub fn prepare_workdir(&self, workdir: &Path) -> Invocation { + Invocation::new( + "sudo", + [ + "-n".into(), + "install".into(), + "-d".into(), + "-o".into(), + self.user.clone(), + "-g".into(), + self.user.clone(), + workdir.as_os_str().to_owned(), + ], + ) + } +} + +impl CommandRunner for SudoUserRunner { + fn run(&self, invocation: Invocation) -> Result<()> { + if let Some(workdir) = invocation + .arguments() + .windows(2) + .find_map(|pair| (pair[0] == "--workdir").then(|| Path::new(&pair[1]))) + { + ProcessRunner.run(self.prepare_workdir(workdir))?; + } + ProcessRunner.run(self.wrap(invocation)) + } +} + #[derive(Debug)] pub struct HasherInstaller<R> { runner: R, diff --git a/src/initramfs.rs b/src/initramfs.rs index 4ece24c..c934aaf 100644 --- a/src/initramfs.rs +++ b/src/initramfs.rs @@ -93,6 +93,7 @@ fn normalize( #[derive(Debug, Clone, PartialEq, Eq)] pub struct InitramfsRequest { rootfs: PathBuf, + hasher_workdir: Option<PathBuf>, kernel: KernelVersion, } @@ -102,12 +103,18 @@ impl InitramfsRequest { if !rootfs.is_dir() { bail!("rootfs is not a directory: {}", rootfs.display()); } + let hasher_workdir = rootfs.parent().map(Path::to_path_buf); Ok(Self { rootfs: rootfs.to_path_buf(), + hasher_workdir, kernel: KernelVersion::discover_rt(rootfs)?, }) } + pub fn hasher_workdir(&self) -> Option<&Path> { + self.hasher_workdir.as_deref() + } + pub fn rootfs(&self) -> &Path { &self.rootfs } @@ -179,13 +186,29 @@ impl<R> MakeInitrdBuilder<R> { impl<R: CommandRunner> InitramfsBuilder for MakeInitrdBuilder<R> { fn build(&mut self, request: &InitramfsRequest) -> Result<InitramfsResult> { + let workdir = request + .hasher_workdir() + .ok_or_else(|| anyhow::anyhow!("rootfs has no Hasher workdir parent"))?; self.runner.run(Invocation::new( - "chroot", + "sudo", [ - request.rootfs().as_os_str().to_owned(), + "-n".into(), + "-u".into(), + "hermes".into(), + "hsh-run".into(), + "--rooter".into(), + "--mountpoints=/proc".into(), + "--workdir".into(), + workdir.as_os_str().to_owned(), + "--".into(), "make-initrd".into(), + "-N".into(), + "-v".into(), "-k".into(), request.kernel().as_str().into(), + "AUTODETECT=".into(), + "-c".into(), + "/etc/initrd.mk.oem".into(), ], ))?; let result = InitramfsResult::from_rootfs(request.rootfs(), request.kernel())?; diff --git a/tests/initramfs.rs b/tests/initramfs.rs index 5ad0b99..85908b2 100644 --- a/tests/initramfs.rs +++ b/tests/initramfs.rs @@ -67,15 +67,34 @@ fn make_initrd_adapter_uses_typed_chroot_arguments_and_records_output_digest() { let result = builder.build(&request).expect("build initramfs"); + let workdir = rootfs + .path() + .parent() + .expect("rootfs fixture has a parent") + .display() + .to_string(); assert_eq!( builder.runner().invocations, vec![Invocation::new( - "chroot", + "sudo", [ - rootfs.path().as_os_str().to_owned(), - "make-initrd".into(), - "-k".into(), - "6.12-rt1".into(), + "-n", + "-u", + "hermes", + "hsh-run", + "--rooter", + "--mountpoints=/proc", + "--workdir", + &workdir, + "--", + "make-initrd", + "-N", + "-v", + "-k", + "6.12-rt1", + "AUTODETECT=", + "-c", + "/etc/initrd.mk.oem", ], )] ); diff --git a/tests/package_installer.rs b/tests/package_installer.rs index 76d8683..74bf9aa 100644 --- a/tests/package_installer.rs +++ b/tests/package_installer.rs @@ -102,3 +102,31 @@ fn build_plan_compiles_its_typed_package_request() { ); assert_eq!(request.selectors(), ["^kernel-(image|modules-())-(rt)$"]); } + +#[test] +fn sudo_user_runner_executes_hasher_commands_as_the_configured_user() { + let runner = alt_controller_image::hasher::SudoUserRunner::new("hermes"); + let invocation = runner.wrap(Invocation::new("hsh", ["--version"])); + + assert_eq!( + invocation, + Invocation::new("sudo", ["-n", "-u", "hermes", "hsh", "--version"]) + ); + let preparation = runner.prepare_workdir(std::path::Path::new("/build/work")); + assert_eq!( + preparation, + Invocation::new( + "sudo", + [ + "-n", + "install", + "-d", + "-o", + "hermes", + "-g", + "hermes", + "/build/work" + ] + ) + ); +} |