Skip to content

Commit

Permalink
client/postblit: Rework trigger scopes to allow ephemeral roots
Browse files Browse the repository at this point in the history
We rejigger the scope to incorporate the client scope and tightly
control the logic behind the bind paths, ensuring all possible paths
for trigger isolation and execution are properly accounted for.

As a result we can now take advantage of system + transaction triggers
from within ephemeral roots, powering the new boulder port.

Signed-off-by: Ikey Doherty <[email protected]>
  • Loading branch information
ikeycode committed Mar 3, 2024
1 parent d6473bd commit fd984f0
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 18 deletions.
31 changes: 28 additions & 3 deletions moss/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// SPDX-License-Identifier: MPL-2.0

use std::{
fs, io,
fs::{self, create_dir_all},
io,
os::{fd::RawFd, unix::fs::symlink},
path::{Path, PathBuf},
time::Duration,
Expand Down Expand Up @@ -219,7 +220,10 @@ impl Client {
record_os_release(&self.installation.staging_dir(), Some(state.id))?;

// Run all of the transaction triggers
let triggers = postblit::triggers(postblit::TriggerScope::Transaction(&self.installation), &fstree)?;
let triggers = postblit::triggers(
postblit::TriggerScope::Transaction(&self.installation, &self.scope),
&fstree,
)?;
create_root_links(&self.installation.isolation_dir())?;
for trigger in triggers {
trigger.execute()?;
Expand All @@ -235,7 +239,8 @@ impl Client {
}

// At this point we're allowed to run system triggers
let sys_triggers = postblit::triggers(postblit::TriggerScope::System(&self.installation), &fstree)?;
let sys_triggers =
postblit::triggers(postblit::TriggerScope::System(&self.installation, &self.scope), &fstree)?;
for trigger in sys_triggers {
trigger.execute()?;
}
Expand All @@ -245,6 +250,25 @@ impl Client {
Scope::Ephemeral { blit_root } => {
record_os_release(blit_root, None)?;
create_root_links(blit_root)?;
create_root_links(&self.installation.isolation_dir())?;

let etc = blit_root.join("etc");
create_dir_all(etc)?;

// ephemeral tx triggers
let triggers = postblit::triggers(
postblit::TriggerScope::Transaction(&self.installation, &self.scope),
&fstree,
)?;
for trigger in triggers {
trigger.execute()?;
}
// ephemeral system triggers
let sys_triggers =
postblit::triggers(postblit::TriggerScope::System(&self.installation, &self.scope), &fstree)?;
for trigger in sys_triggers {
trigger.execute()?;
}
Ok(None)
}
}
Expand Down Expand Up @@ -629,6 +653,7 @@ BUG_REPORT_URL="https://github.com/serpent-os""#,
Ok(())
}

#[derive(Clone, Debug)]
enum Scope {
Stateful,
Ephemeral { blit_root: PathBuf },
Expand Down
80 changes: 65 additions & 15 deletions moss/src/client/postblit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
//!
//! Note that currently we only load from `/usr/share/moss/triggers/{tx,sys.d}/*.yaml`
//! and do not yet support local triggers
use std::{path::Path, process};
use std::{
path::{Path, PathBuf},
process,
};

use container::Container;
use itertools::Itertools;
Expand Down Expand Up @@ -46,8 +49,52 @@ impl config::Config for SystemTrigger {
/// Defines the scope of triggers
#[derive(Clone, Copy, Debug)]
pub(super) enum TriggerScope<'a> {
Transaction(&'a Installation),
System(&'a Installation),
Transaction(&'a Installation, &'a super::Scope),
System(&'a Installation, &'a super::Scope),
}

impl<'a> TriggerScope<'a> {
// Determine the correct root directory
fn root_dir(&self) -> PathBuf {
match self {
TriggerScope::Transaction(install, scope) => match scope {
super::Scope::Stateful => install.staging_dir().clone(),
super::Scope::Ephemeral { blit_root } => blit_root.clone(),
},
TriggerScope::System(install, scope) => match scope {
super::Scope::Stateful => install.root.clone(),
super::Scope::Ephemeral { blit_root } => blit_root.clone(),
},
}
}

/// Join "host" paths, outside the staging filesystem. Ensure no sandbox break for ephemeral
fn host_path(&self, path: impl AsRef<Path>) -> PathBuf {
match self {
TriggerScope::Transaction(install, scope) => match scope {
super::Scope::Stateful => install.root.join(path),
super::Scope::Ephemeral { blit_root } => blit_root.join(path),
},
TriggerScope::System(install, scope) => match scope {
super::Scope::Stateful => install.root.join(path),
super::Scope::Ephemeral { blit_root } => blit_root.join(path),
},
}
}

/// Join guest paths, inside the staging filesystem. Ensure no sandbox break for ephemeral
fn guest_path(&self, path: impl AsRef<Path>) -> PathBuf {
match self {
TriggerScope::Transaction(install, scope) => match scope {
super::Scope::Stateful => install.staging_path(path),
super::Scope::Ephemeral { blit_root } => blit_root.join(path),
},
TriggerScope::System(install, scope) => match scope {
super::Scope::Stateful => install.root.join(path),
super::Scope::Ephemeral { blit_root } => blit_root.join(path),
},
}
}
}

#[derive(Debug)]
Expand All @@ -66,12 +113,14 @@ pub(super) fn triggers<'a>(

// Load appropriate triggers from their locations and convert back to a vec of Trigger
let triggers = match scope {
TriggerScope::Transaction(install) => config::Manager::custom(install.staging_dir().join(trigger_root))
.load::<TransactionTrigger>()
.into_iter()
.map(|t| t.0)
.collect_vec(),
TriggerScope::System(install) => config::Manager::custom(install.root.join(trigger_root))
TriggerScope::Transaction(install, client_scope) => {
config::Manager::custom(scope.root_dir().join(trigger_root))
.load::<TransactionTrigger>()
.into_iter()
.map(|t| t.0)
.collect_vec()
}
TriggerScope::System(install, client_scope) => config::Manager::custom(scope.root_dir().join(trigger_root))
.load::<SystemTrigger>()
.into_iter()
.map(|t| t.0)
Expand All @@ -92,28 +141,29 @@ pub(super) fn triggers<'a>(
impl<'a> TriggerRunner<'a> {
pub fn execute(&self) -> Result<(), Error> {
match self.scope {
TriggerScope::Transaction(install) => {
TriggerScope::Transaction(install, client_scope) => {
// TODO: Add caching support via /var/
let isolation = Container::new(install.isolation_dir())
.networking(false)
.override_accounts(false)
.bind_ro(install.root.join("etc"), "/etc")
.bind_rw(install.staging_path("usr"), "/usr")
.bind_ro(self.scope.host_path("etc"), "/etc")
.bind_rw(self.scope.guest_path("usr"), "/usr")
.work_dir("/");

Ok(isolation.run(|| execute_trigger_directly(&self.trigger))?)
}
TriggerScope::System(install) => {
TriggerScope::System(install, client_scope) => {
// OK, if the root == `/` then we can run directly, otherwise we need to containerise with RW.
if install.root.to_string_lossy() == "/" {
Ok(execute_trigger_directly(&self.trigger)?)
} else {
let isolation = Container::new(install.isolation_dir())
.networking(false)
.override_accounts(false)
.bind_rw(install.root.join("etc"), "/etc")
.bind_rw(install.root.join("usr"), "/usr")
.bind_rw(self.scope.host_path("etc"), "/etc")
.bind_rw(self.scope.guest_path("usr"), "/usr")
.work_dir("/");

Ok(isolation.run(|| execute_trigger_directly(&self.trigger))?)
}
}
Expand Down

0 comments on commit fd984f0

Please sign in to comment.