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`.
|
/// Default path to the setuid jail-spawn helper used in `PrivMode::Helper`.
|
||||||
pub const DEFAULT_JAIL_HELPER: &str = "/usr/local/libexec/colibri-jail-spawn";
|
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.
|
/// 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('/'));
|
let host_stage_dir = Path::new(stage_root).join(jail_stage_dir.trim_start_matches('/'));
|
||||||
tokio::fs::create_dir_all(&host_stage_dir).await?;
|
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[1], "/bin/sh");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
prepared.argv[2],
|
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());
|
assert!(prepared.env.is_empty());
|
||||||
|
|
||||||
let launcher = root.join("var/run/colibri-stage/agent-test/launch.sh");
|
let launcher = root.join("home/clawdie/.cache/colibri/stage/agent-test/launch.sh");
|
||||||
let env_file = root.join("var/run/colibri-stage/agent-test/env.sh");
|
let env_file = root.join("home/clawdie/.cache/colibri/stage/agent-test/env.sh");
|
||||||
assert!(launcher.exists());
|
assert!(launcher.exists());
|
||||||
assert!(env_file.exists());
|
assert!(env_file.exists());
|
||||||
let env_raw = std::fs::read_to_string(env_file).unwrap();
|
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()`
|
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
|
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
|
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
|
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
|
binary. This bypasses the env-passthrough problem entirely — no reliance on
|
||||||
`jexec`/`mdo` inheriting env vars.
|
`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.
|
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.
|
The daemon runs as `clawdie` and could not write staging files there.
|
||||||
|
|
||||||
**Fix:** `chmod 777 <jail_root>/var/run/colibri-stage` (or, better:
|
**Fix (history):** initially `chmod 777 <jail_root>/var/run/colibri-stage`, then
|
||||||
`agent-jail-bootstrap.sh` should pre-create this directory with appropriate
|
`agent-jail-bootstrap.sh` pre-created it clawdie-owned `0700` (#134). **Final
|
||||||
ownership).
|
(#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
|
## The Winning Spawn
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ set -eu
|
||||||
|
|
||||||
JAIL_NAME="${1:-}"
|
JAIL_NAME="${1:-}"
|
||||||
PKG_CACHE_DIR="${PKG_CACHE_DIR:-/var/cache/pkg}"
|
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
|
# The jail name becomes a path component, so reject anything that could escape
|
||||||
# /usr/local/bastille/jails/<name>/root (empty, traversal, odd characters).
|
# /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"
|
>> "${JAIL_ROOT}/etc/profile"
|
||||||
fi
|
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."
|
echo "Done — ${JAIL_NAME} ready for vault provision."
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue