Skip to content

Commit

Permalink
feat: support services with multiple ExecStartPre/ExecStart/ExecStart…
Browse files Browse the repository at this point in the history
…Post directives (WIP)
  • Loading branch information
desbma committed Dec 4, 2023
1 parent e549755 commit 4a81a19
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 27 deletions.
2 changes: 2 additions & 0 deletions src/cl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ pub enum HardeningMode {
pub enum Action {
/// Run a program to profile its behavior
Run {
// TODO profile data dir
/// The command line to run
command: Vec<String>,
/// How hard we should harden
#[arg(short, long, default_value_t, value_enum)]
mode: HardeningMode,
},
// TODO merge profile data
/// Act on a systemd service unit
#[clap(subcommand)]
Service(ServiceAction),
Expand Down
51 changes: 24 additions & 27 deletions src/systemd/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,29 +61,12 @@ impl Service {
"Hardening config already exists at {harden_fragment_path:?} and may conflict with profiling"
);

// Check we have no ExecStartPre/ExecStartPost
// Currently we don't profile those, so we'd rather throw an explicit error
let config_paths_bufs = self.config_paths()?;
let config_paths = config_paths_bufs
.iter()
.map(|p| p.as_path())
.collect::<Vec<_>>();
if !Self::config_vals("ExecStartPre", &config_paths)?.is_empty()
|| !Self::config_vals("ExecStartPost", &config_paths)?.is_empty()
{
anyhow::bail!("Services with ExecStartPre/ExecStartPost are not supported")
}

// Read current service startup command
log::info!("Located unit config file(s): {config_paths:?}");
let cmd = Self::config_vals("ExecStart", &config_paths)?
.into_iter()
.at_most_one()
.map_err(|_| {
anyhow::anyhow!("Services with multiple ExecStart directives are not supported")
})?
.ok_or_else(|| anyhow::anyhow!("Unable to get service ExecStart command"))?;
log::info!("Startup command: {cmd}");

// Write new fragment
fs::create_dir_all(fragment_path.parent().unwrap())?;
Expand All @@ -104,16 +87,30 @@ impl Service {
}
// strace may slow down enough to risk reaching some service timeouts
writeln!(fragment_file, "TimeoutStartSec=infinity")?;
writeln!(fragment_file, "ExecStart=")?;
writeln!(
fragment_file,
"ExecStart={} run -m {} -- {}",
env::current_exe()?
.to_str()
.ok_or_else(|| anyhow::anyhow!("Unable to decode current executable path"))?,
mode,
cmd
)?;

// TODO setup profile dir tmpfs

let shh_bin = env::current_exe()?
.to_str()
.ok_or_else(|| anyhow::anyhow!("Unable to decode current executable path"))?
.to_string();

// wrap all ExecStartXxx directives
for exec_start_opt in ["ExecStartPre", "ExecStart", "ExecStartPost"] {
let exec_start_cmds = Self::config_vals(exec_start_opt, &config_paths)?;
if !exec_start_cmds.is_empty() {
writeln!(fragment_file, "{}=", exec_start_opt)?;
}
for cmd in exec_start_cmds {
writeln!(
fragment_file,
"{}={} run -m {} -- {}",
exec_start_opt, shh_bin, mode, cmd
)?;
}
}

// TODO ExecStopPost shh invocation that merges previous profiles

log::info!("Config fragment written in {fragment_path:?}");
Ok(())
Expand Down

0 comments on commit 4a81a19

Please sign in to comment.