priv: move shared consts to hive-sh4re; WriteNspawnFlags uses Vec<String> + per-flag validation
This commit is contained in:
parent
89d0937473
commit
aa7f8e5553
3 changed files with 37 additions and 20 deletions
|
|
@ -59,10 +59,10 @@ pub async fn list_containers() -> Result<String> {
|
||||||
Ok(stdout)
|
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 {
|
ok(call(&PrivRequest::WriteNspawnFlags {
|
||||||
container: container.to_owned(),
|
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?)
|
}).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,27 +20,14 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::{Context as _, Result, bail};
|
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::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||||
use tokio::net::{UnixListener, UnixStream};
|
use tokio::net::{UnixListener, UnixStream};
|
||||||
use tokio::process::Command;
|
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.
|
/// Root of the per-agent unix-socket dirs on the host.
|
||||||
const SOCKET_DIR_ROOT: &str = "/run/hive-agent";
|
const SOCKET_DIR_ROOT: &str = "/run/hive-agent";
|
||||||
|
|
||||||
/// Host path of the meta flake (mirrors `meta::meta_dir()`).
|
|
||||||
/// The flake ref for agent `<name>` is `{META_DIR}#{name}`.
|
|
||||||
const META_DIR: &str = "/var/lib/hyperhive/meta";
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
|
|
@ -188,6 +175,9 @@ async fn exec(req: PrivRequest) -> Result<(String, String)> {
|
||||||
|
|
||||||
PrivRequest::WriteNspawnFlags { ref container, ref extra_nspawn_flags } => {
|
PrivRequest::WriteNspawnFlags { ref container, ref extra_nspawn_flags } => {
|
||||||
validate_container_system_name(container)?;
|
validate_container_system_name(container)?;
|
||||||
|
for flag in extra_nspawn_flags {
|
||||||
|
validate_nspawn_flag(flag)?;
|
||||||
|
}
|
||||||
write_nspawn_flags(container, extra_nspawn_flags)?;
|
write_nspawn_flags(container, extra_nspawn_flags)?;
|
||||||
Ok((String::new(), String::new()))
|
Ok((String::new(), String::new()))
|
||||||
}
|
}
|
||||||
|
|
@ -362,11 +352,22 @@ fn agent_flake_ref(name: &str) -> String {
|
||||||
format!("{META_DIR}#{name}")
|
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/<container>.conf`: strips network-isolation
|
/// Update `/etc/nixos-containers/<container>.conf`: strips network-isolation
|
||||||
/// vars (`PRIVATE_NETWORK`, `HOST_ADDRESS*`, `LOCAL_ADDRESS*`, `HOST_BRIDGE`,
|
/// vars (`PRIVATE_NETWORK`, `HOST_ADDRESS*`, `LOCAL_ADDRESS*`, `HOST_BRIDGE`,
|
||||||
/// `EXTRA_NSPAWN_FLAGS`), forces `PRIVATE_NETWORK=0` and blank network vars,
|
/// `EXTRA_NSPAWN_FLAGS`), forces `PRIVATE_NETWORK=0` and blank network vars,
|
||||||
/// then appends `EXTRA_NSPAWN_FLAGS="<flags>"`.
|
/// then appends `EXTRA_NSPAWN_FLAGS="<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 path = format!("/etc/nixos-containers/{container}.conf");
|
||||||
let original = std::fs::read_to_string(&path)
|
let original = std::fs::read_to_string(&path)
|
||||||
.with_context(|| format!("read {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("HOST_ADDRESS6=\n");
|
||||||
out.push_str("LOCAL_ADDRESS6=\n");
|
out.push_str("LOCAL_ADDRESS6=\n");
|
||||||
out.push_str("HOST_BRIDGE=\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}"))
|
std::fs::write(&path, out).with_context(|| format!("write {path}"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,20 @@ use serde::{Deserialize, Serialize};
|
||||||
/// Default socket path for the privileged helper.
|
/// Default socket path for the privileged helper.
|
||||||
pub const PRIV_SOCK: &str = "/run/hive/priv.sock";
|
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-<agent_name>`.
|
||||||
|
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 `<name>` 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.
|
/// A request to the privileged helper.
|
||||||
///
|
///
|
||||||
/// Wire format: one JSON object per line over `/run/hive/priv.sock`.
|
/// Wire format: one JSON object per line over `/run/hive/priv.sock`.
|
||||||
|
|
@ -46,8 +60,9 @@ pub enum PrivRequest {
|
||||||
|
|
||||||
/// Update `/etc/nixos-containers/<container>.conf`: strip network-isolation
|
/// Update `/etc/nixos-containers/<container>.conf`: strip network-isolation
|
||||||
/// vars, force `PRIVATE_NETWORK=0`, and set `EXTRA_NSPAWN_FLAGS`.
|
/// vars, force `PRIVATE_NETWORK=0`, and set `EXTRA_NSPAWN_FLAGS`.
|
||||||
/// Written by `lifecycle::set_nspawn_flags`.
|
/// Each entry in `extra_nspawn_flags` is one flag (e.g. `"--bind=/path"`);
|
||||||
WriteNspawnFlags { container: String, extra_nspawn_flags: String },
|
/// hive-priv validates and space-joins them. Written by `lifecycle::set_nspawn_flags`.
|
||||||
|
WriteNspawnFlags { container: String, extra_nspawn_flags: Vec<String> },
|
||||||
|
|
||||||
/// Write `/run/systemd/system/container@<container>.service.d/hyperhive-limits.conf`
|
/// Write `/run/systemd/system/container@<container>.service.d/hyperhive-limits.conf`
|
||||||
/// with `[Service]\nMemoryMax=<memory_max>\nCPUQuota=<cpu_quota>\n`.
|
/// with `[Service]\nMemoryMax=<memory_max>\nCPUQuota=<cpu_quota>\n`.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue