Add ChrootedTask to run all tasks in a different (curently hardcoded) root

main
trivernis 2 years ago committed by Michal Stopyra
parent 766cdc108a
commit 35b2ed5782

1
.gitignore vendored

@ -14,3 +14,4 @@ Cargo.lock
# Misc
test-config.json
chroot

1
Cargo.lock generated

@ -3517,6 +3517,7 @@ dependencies = [
"dotenv",
"embed-nu",
"lazy_static",
"libc",
"paste",
"rusty-value",
"serde",

@ -10,7 +10,7 @@ codename = "Walter White"
name = "tourmaline"
[[bin]]
name = "trl"
name = "trm"
path = "src/main.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -21,6 +21,7 @@ color-eyre = "0.6.2"
dotenv = "0.15.0"
embed-nu = "0.3.3"
lazy_static = "1.4.0"
libc = "0.2.135"
paste = "1.0.9"
rusty-value = { version = "0.6.0", features = ["derive", "json"] }
serde = { version = "1.0.145", features = ["derive"] }

@ -26,6 +26,9 @@ pub enum AppError {
#[error(transparent)]
OSConfig(#[from] OSConfigError),
#[error(transparent)]
Chroot(#[from] ChrootError),
}
#[derive(Error, Debug)]
@ -78,3 +81,18 @@ pub enum OSConfigError {
#[error("Missing config key {0}")]
MissingConfigKey(String),
}
#[derive(Error, Debug)]
pub enum ChrootError {
#[error("Could not find chroot directory {0}")]
NotFound(PathBuf),
#[error("Failed to unshare FS resources with parent: {0}")]
Unshare(io::Error),
#[error("Failed to chroot: {0}")]
Chroot(io::Error),
#[error("Failed to change process working directory: {0}")]
ChDir(io::Error),
}

@ -84,12 +84,9 @@ impl TaskTrait for BaseTask {
} else {
Option::<()>::None.into_value()
};
let exec = ExecBuilder::create(script, config.to_owned(), task_config)?;
Ok(Some(ExecBuilder {
script,
os_config: config.to_owned(),
task_config,
}))
Ok(Some(exec))
}
#[tracing::instrument(level = "trace", skip_all)]
@ -101,12 +98,9 @@ impl TaskTrait for BaseTask {
} else {
Option::<()>::None.into_value()
};
let exec = ExecBuilder::create(script, config.to_owned(), task_config)?;
Ok(Some(ExecBuilder {
script,
os_config: config.to_owned(),
task_config,
}))
Ok(Some(exec))
}
fn order(&self) -> usize {

@ -0,0 +1,61 @@
use std::{
ffi::{c_int, CString},
io,
os::unix::prelude::OsStrExt,
path::{Path, PathBuf},
};
use libc::CLONE_FS;
use tokio::task::JoinHandle;
use crate::error::ChrootError;
pub struct ChrootedTask {
root_path: PathBuf,
}
impl ChrootedTask {
/// Creates a new chrooted thread with the given path
pub fn new<P: Into<PathBuf>>(root_path: P) -> Self {
Self {
root_path: root_path.into(),
}
}
/// Runs the given future in a new chroot
pub fn run<F, T>(self, call: F) -> JoinHandle<Result<T, ChrootError>>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
let root_path = self.root_path;
let handle = std::thread::spawn(move || {
unsafe {
init_chroot(&root_path)?;
}
Ok(call())
});
tokio::task::spawn_blocking(|| handle.join().unwrap())
}
}
unsafe fn init_chroot(path: &Path) -> Result<(), ChrootError> {
handle_err_code(libc::unshare(CLONE_FS)).map_err(ChrootError::Unshare)?;
let path_str = CString::new(path.as_os_str().as_bytes().to_vec()).unwrap();
handle_err_code(libc::chroot(
path_str.as_bytes_with_nul().as_ptr() as *const libc::c_char
))
.map_err(ChrootError::Chroot)?;
std::env::set_current_dir(path).map_err(ChrootError::ChDir)?;
std::env::set_var("PWD", "/");
Ok(())
}
fn handle_err_code(code: c_int) -> Result<(), io::Error> {
if code != 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}

@ -45,11 +45,9 @@ impl TaskTrait for CustomTask {
if self.skip_on_false && self.config_key.is_some() && config_is_falsy(&task_config) {
Ok(None)
} else {
Ok(Some(ExecBuilder {
script: self.up_script.to_owned(),
os_config: config.to_owned(),
task_config,
}))
let exec =
ExecBuilder::create(self.up_script.to_owned(), config.to_owned(), task_config)?;
Ok(Some(exec))
}
}
@ -64,11 +62,9 @@ impl TaskTrait for CustomTask {
if self.skip_on_false && self.config_key.is_some() && config_is_falsy(&task_config) {
Ok(None)
} else {
Ok(Some(ExecBuilder {
script: self.down_script.to_owned(),
os_config: config.to_owned(),
task_config,
}))
let exec =
ExecBuilder::create(self.down_script.to_owned(), config.to_owned(), task_config)?;
Ok(Some(exec))
}
}

@ -1,24 +1,41 @@
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use embed_nu::{Argument, CommandGroupConfig, Context, ValueIntoExpression};
use tokio::fs;
use std::fs;
use crate::{distro::OSConfig, error::ScriptError, utils::CFG_PATH};
#[derive(Clone)]
pub struct ExecBuilder {
pub script: PathBuf,
pub os_config: OSConfig,
pub task_config: embed_nu::Value,
script: PathBuf,
script_contents: String,
os_config: OSConfig,
task_config: embed_nu::Value,
}
impl ExecBuilder {
pub fn create(
script: PathBuf,
os_config: OSConfig,
task_config: embed_nu::Value,
) -> Result<Self, ScriptError> {
let script_contents = Self::get_script_contents(&script)?;
Ok(Self {
script,
script_contents,
os_config,
task_config,
})
}
#[tracing::instrument(level = "trace", skip_all)]
pub async fn exec(self) -> Result<(), ScriptError> {
let script_contents = self.get_script_contents().await?;
pub fn exec(self) -> Result<(), ScriptError> {
let mut ctx = Context::builder()
.with_command_groups(CommandGroupConfig::default().all_groups(true))?
.add_parent_env_vars()
.add_var("TRM_CONFIG", self.os_config)?
.add_script(script_contents)?
.add_script(self.script_contents)?
.build()?;
if ctx.has_fn("main") {
@ -35,11 +52,11 @@ impl ExecBuilder {
}
#[tracing::instrument(level = "trace", skip_all)]
async fn get_script_contents(&self) -> Result<String, ScriptError> {
let path = CFG_PATH.join(&self.script);
fn get_script_contents(path: &Path) -> Result<String, ScriptError> {
let path = CFG_PATH.join(path);
if path.exists() {
fs::read_to_string(path).await.map_err(ScriptError::from)
fs::read_to_string(path).map_err(ScriptError::from)
} else {
Err(ScriptError::ScriptNotFound(path))
}

@ -4,6 +4,7 @@ use crate::{distro::OSConfig, error::AppResult};
use self::{base_task::BaseTask, custom_task::CustomTask, exec_builder::ExecBuilder};
pub mod base_task;
mod chrooting;
pub mod custom_task;
pub mod exec_builder;
pub mod task_executor;

@ -3,7 +3,9 @@ use crate::{
error::AppResult,
};
use super::{base_task::ALL_BASE_TASKS, custom_task::CustomTask, Task, TaskTrait};
use super::{
base_task::ALL_BASE_TASKS, chrooting::ChrootedTask, custom_task::CustomTask, Task, TaskTrait,
};
pub struct TaskExecutor {
distro_config: DistroConfig,
@ -63,7 +65,10 @@ impl TaskExecutor {
for task in &self.tasks {
if let Some(up_task) = task.up(&self.os_config)? {
up_task.exec().await?
ChrootedTask::new("chroot/root")
.run(move || up_task.exec())
.await
.unwrap()??;
}
}

Loading…
Cancel
Save