From aa7f8e55532eb444552470be3e63296c7d94cffa Mon Sep 17 00:00:00 2001 From: damocles Date: Mon, 1 Jun 2026 17:19:39 +0200 Subject: [PATCH] priv: move shared consts to hive-sh4re; WriteNspawnFlags uses Vec + per-flag validation --- hive-c0re/src/priv_client.rs | 4 ++-- hive-priv/src/main.rs | 34 ++++++++++++++++++---------------- hive-sh4re/src/priv_proto.rs | 19 +++++++++++++++++-- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/hive-c0re/src/priv_client.rs b/hive-c0re/src/priv_client.rs index 06ae9603..cd5fa42b 100644 --- a/hive-c0re/src/priv_client.rs +++ b/hive-c0re/src/priv_client.rs @@ -59,10 +59,10 @@ pub async fn list_containers() -> Result { Ok(stdout) } -pub async fn write_nspawn_flags(container: &str, extra_nspawn_flags: &str) -> Result<()> { +pub async fn write_nspawn_flags(container: &str, extra_nspawn_flags: &[&str]) -> Result<()> { ok(call(&PrivRequest::WriteNspawnFlags { container: container.to_owned(), - extra_nspawn_flags: extra_nspawn_flags.to_owned(), + extra_nspawn_flags: extra_nspawn_flags.iter().map(|s| s.to_string()).collect(), }).await?) } diff --git a/hive-priv/src/main.rs b/hive-priv/src/main.rs index be805fd0..69ebd9f3 100644 --- a/hive-priv/src/main.rs +++ b/hive-priv/src/main.rs @@ -20,27 +20,14 @@ use std::path::{Path, PathBuf}; use anyhow::{Context as _, Result, bail}; -use hive_sh4re::priv_proto::{PRIV_SOCK, PrivRequest, PrivResponse}; +use hive_sh4re::priv_proto::{AGENT_PREFIX, MANAGER_NAME, META_DIR, PRIV_SOCK, SIBLING_CONTAINERS, PrivRequest, PrivResponse}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::net::{UnixListener, UnixStream}; use tokio::process::Command; -/// Sub-agent container prefix (mirrors `lifecycle::AGENT_PREFIX`). -const AGENT_PREFIX: &str = "h-"; - -/// Manager container name (mirrors `lifecycle::MANAGER_NAME`). -const MANAGER_NAME: &str = "root"; - -/// Sibling service containers managed by hive-c0re. -const SIBLING_CONTAINERS: &[&str] = &["hive-forge", "hive-matrix", "hive-gateway"]; - /// Root of the per-agent unix-socket dirs on the host. const SOCKET_DIR_ROOT: &str = "/run/hive-agent"; -/// Host path of the meta flake (mirrors `meta::meta_dir()`). -/// The flake ref for agent `` is `{META_DIR}#{name}`. -const META_DIR: &str = "/var/lib/hyperhive/meta"; - #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt() @@ -188,6 +175,9 @@ async fn exec(req: PrivRequest) -> Result<(String, String)> { PrivRequest::WriteNspawnFlags { ref container, ref extra_nspawn_flags } => { validate_container_system_name(container)?; + for flag in extra_nspawn_flags { + validate_nspawn_flag(flag)?; + } write_nspawn_flags(container, extra_nspawn_flags)?; Ok((String::new(), String::new())) } @@ -362,11 +352,22 @@ fn agent_flake_ref(name: &str) -> String { format!("{META_DIR}#{name}") } +/// Validate one nspawn flag entry: must be non-empty and contain no +/// ASCII whitespace or null bytes. Whitespace would split the entry +/// into multiple flags when the start script expands +/// `$EXTRA_NSPAWN_FLAGS` unquoted. +fn validate_nspawn_flag(flag: &str) -> Result<()> { + if flag.is_empty() || flag.bytes().any(|b| b == 0 || b.is_ascii_whitespace()) { + bail!("invalid nspawn flag {flag:?}: must be non-empty and contain no whitespace or null bytes"); + } + Ok(()) +} + /// Update `/etc/nixos-containers/.conf`: strips network-isolation /// vars (`PRIVATE_NETWORK`, `HOST_ADDRESS*`, `LOCAL_ADDRESS*`, `HOST_BRIDGE`, /// `EXTRA_NSPAWN_FLAGS`), forces `PRIVATE_NETWORK=0` and blank network vars, /// then appends `EXTRA_NSPAWN_FLAGS=""`. -fn write_nspawn_flags(container: &str, extra_nspawn_flags: &str) -> Result<()> { +fn write_nspawn_flags(container: &str, extra_nspawn_flags: &[String]) -> Result<()> { let path = format!("/etc/nixos-containers/{container}.conf"); let original = std::fs::read_to_string(&path) .with_context(|| format!("read {path}"))?; @@ -393,6 +394,7 @@ fn write_nspawn_flags(container: &str, extra_nspawn_flags: &str) -> Result<()> { out.push_str("HOST_ADDRESS6=\n"); out.push_str("LOCAL_ADDRESS6=\n"); out.push_str("HOST_BRIDGE=\n"); - out.push_str(&format!("EXTRA_NSPAWN_FLAGS=\"{extra_nspawn_flags}\"\n")); + let flags_joined = extra_nspawn_flags.join(" "); + out.push_str(&format!("EXTRA_NSPAWN_FLAGS=\"{flags_joined}\"\n")); std::fs::write(&path, out).with_context(|| format!("write {path}")) } diff --git a/hive-sh4re/src/priv_proto.rs b/hive-sh4re/src/priv_proto.rs index 5a3ef08a..63cf028a 100644 --- a/hive-sh4re/src/priv_proto.rs +++ b/hive-sh4re/src/priv_proto.rs @@ -8,6 +8,20 @@ use serde::{Deserialize, Serialize}; /// Default socket path for the privileged helper. pub const PRIV_SOCK: &str = "/run/hive/priv.sock"; +/// Manager container name. Used by `hive-priv` to skip the `h-` prefix +/// and by `hive-c0re` for identity checks. +pub const MANAGER_NAME: &str = "root"; + +/// Sub-agent container prefix. System container name = `h-`. +pub const AGENT_PREFIX: &str = "h-"; + +/// Sibling service containers managed by hive-c0re. +pub const SIBLING_CONTAINERS: &[&str] = &["hive-forge", "hive-matrix", "hive-gateway"]; + +/// Host path of the meta flake. The flake ref for agent `` is +/// `{META_DIR}#{name}`, derived by `hive-priv` — never passed over the wire. +pub const META_DIR: &str = "/var/lib/hyperhive/meta"; + /// A request to the privileged helper. /// /// Wire format: one JSON object per line over `/run/hive/priv.sock`. @@ -46,8 +60,9 @@ pub enum PrivRequest { /// Update `/etc/nixos-containers/.conf`: strip network-isolation /// vars, force `PRIVATE_NETWORK=0`, and set `EXTRA_NSPAWN_FLAGS`. - /// Written by `lifecycle::set_nspawn_flags`. - WriteNspawnFlags { container: String, extra_nspawn_flags: String }, + /// Each entry in `extra_nspawn_flags` is one flag (e.g. `"--bind=/path"`); + /// hive-priv validates and space-joins them. Written by `lifecycle::set_nspawn_flags`. + WriteNspawnFlags { container: String, extra_nspawn_flags: Vec }, /// Write `/run/systemd/system/container@.service.d/hyperhive-limits.conf` /// with `[Service]\nMemoryMax=\nCPUQuota=\n`.