fix(spawner): stage jail spawn files under daemon-owned home, not /var/run #136
4 changed files with 26 additions and 17 deletions
|
|
@ -121,7 +121,20 @@ impl PrivMode {
|
|||
|
||||
/// Default path to the setuid jail-spawn helper used in `PrivMode::Helper`.
|
||||
pub const DEFAULT_JAIL_HELPER: &str = "/usr/local/libexec/colibri-jail-spawn";
|
||||
const STAGED_JAIL_RUN_DIR: &str = "/var/run/colibri-stage";
|
||||
|
||||
/// Jail-relative directory under which the daemon stages per-spawn `launch.sh` /
|
||||
/// `env.sh` files. It lives under the daemon user's home (`clawdie`) rather than
|
||||
/// root-owned `/var/run`, so the daemon can create per-spawn subdirs itself with
|
||||
/// no privileged pre-creation step — and `/home` is persistent, unlike a tmpfs
|
||||
/// `/var/run`. Overridable via `COLIBRI_JAIL_STAGE_DIR`.
|
||||
const DEFAULT_JAIL_STAGE_DIR: &str = "/home/clawdie/.cache/colibri/stage";
|
||||
|
||||
fn staged_jail_run_dir() -> String {
|
||||
std::env::var("COLIBRI_JAIL_STAGE_DIR")
|
||||
.ok()
|
||||
.filter(|s| !s.is_empty())
|
||||
.unwrap_or_else(|| DEFAULT_JAIL_STAGE_DIR.to_string())
|
||||
}
|
||||
|
||||
/// Optional FreeBSD jail confinement for a spawned agent.
|
||||
///
|
||||
|
|
@ -383,7 +396,7 @@ pub async fn prepare_spawn_command(
|
|||
)
|
||||
})?;
|
||||
|
||||
let jail_stage_dir = format!("{STAGED_JAIL_RUN_DIR}/{stage_id}");
|
||||
let jail_stage_dir = format!("{}/{stage_id}", staged_jail_run_dir());
|
||||
let host_stage_dir = Path::new(stage_root).join(jail_stage_dir.trim_start_matches('/'));
|
||||
tokio::fs::create_dir_all(&host_stage_dir).await?;
|
||||
|
||||
|
|
@ -1030,12 +1043,12 @@ mod jail_tests {
|
|||
assert_eq!(prepared.argv[1], "/bin/sh");
|
||||
assert_eq!(
|
||||
prepared.argv[2],
|
||||
"/var/run/colibri-stage/agent-test/launch.sh"
|
||||
"/home/clawdie/.cache/colibri/stage/agent-test/launch.sh"
|
||||
);
|
||||
assert!(prepared.env.is_empty());
|
||||
|
||||
let launcher = root.join("var/run/colibri-stage/agent-test/launch.sh");
|
||||
let env_file = root.join("var/run/colibri-stage/agent-test/env.sh");
|
||||
let launcher = root.join("home/clawdie/.cache/colibri/stage/agent-test/launch.sh");
|
||||
let env_file = root.join("home/clawdie/.cache/colibri/stage/agent-test/env.sh");
|
||||
assert!(launcher.exists());
|
||||
assert!(env_file.exists());
|
||||
let env_raw = std::fs::read_to_string(env_file).unwrap();
|
||||
|
|
|
|||
|
|
@ -65,7 +65,9 @@ just `jexec`. We choose the escalation per host via `PrivMode`
|
|||
When a jailed spawn needs env vars or a working dir, `prepare_spawn_command()`
|
||||
writes a 0600 `env.sh` (sorted, single-quoted exports) and a `launch.sh` wrapper
|
||||
into a staged directory under the jail's `root_path` at
|
||||
`/var/run/colibri-stage/<id>/`. The jail command runs `/bin/sh launch.sh`, which
|
||||
`/home/clawdie/.cache/colibri/stage/<id>/` (the daemon user's home, so the
|
||||
daemon creates it with no privileged step; overridable via
|
||||
`COLIBRI_JAIL_STAGE_DIR`). The jail command runs `/bin/sh launch.sh`, which
|
||||
sources the env file and `cd`s to the working dir before `exec`-ing the agent
|
||||
binary. This bypasses the env-passthrough problem entirely — no reliance on
|
||||
`jexec`/`mdo` inheriting env vars.
|
||||
|
|
|
|||
|
|
@ -30,9 +30,11 @@ For jailed spawns with environment variables, the daemon's
|
|||
created by a previous run (as root) and was mode 755 root:wheel.
|
||||
The daemon runs as `clawdie` and could not write staging files there.
|
||||
|
||||
**Fix:** `chmod 777 <jail_root>/var/run/colibri-stage` (or, better:
|
||||
`agent-jail-bootstrap.sh` should pre-create this directory with appropriate
|
||||
ownership).
|
||||
**Fix (history):** initially `chmod 777 <jail_root>/var/run/colibri-stage`, then
|
||||
`agent-jail-bootstrap.sh` pre-created it clawdie-owned `0700` (#134). **Final
|
||||
(#135):** staging moved out of root-owned `/var/run` to the daemon user's home
|
||||
at `/home/clawdie/.cache/colibri/stage/<id>/`, so the daemon creates it itself
|
||||
with no privileged pre-creation step (overridable via `COLIBRI_JAIL_STAGE_DIR`).
|
||||
|
||||
## The Winning Spawn
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ set -eu
|
|||
|
||||
JAIL_NAME="${1:-}"
|
||||
PKG_CACHE_DIR="${PKG_CACHE_DIR:-/var/cache/pkg}"
|
||||
DAEMON_USER="${DAEMON_USER:-clawdie}"
|
||||
|
||||
# The jail name becomes a path component, so reject anything that could escape
|
||||
# /usr/local/bastille/jails/<name>/root (empty, traversal, odd characters).
|
||||
|
|
@ -98,11 +97,4 @@ if ! grep -q '/etc/profile.d/clawdie-npm.sh' "${JAIL_ROOT}/etc/profile" 2>/dev/n
|
|||
>> "${JAIL_ROOT}/etc/profile"
|
||||
fi
|
||||
|
||||
# Pre-create the daemon's per-spawn staging directory. The daemon runs as
|
||||
# ${DAEMON_USER} and stages launch.sh/env.sh under <stage_id> subdirs here, so
|
||||
# it must own this directory. Created clawdie-owned 0700 rather than left for a
|
||||
# root-owned /var/run to block (the spawn EACCES) or patched world-writable.
|
||||
install -d -o "${DAEMON_USER}" -g "${DAEMON_USER}" -m 0700 \
|
||||
"${JAIL_ROOT}/var/run/colibri-stage"
|
||||
|
||||
echo "Done — ${JAIL_NAME} ready for vault provision."
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue