priv: WriteNspawnFlags takes Vec<BindMount> instead of raw flag strings
This commit is contained in:
parent
a922376778
commit
c9eb520e7c
3 changed files with 38 additions and 24 deletions
|
|
@ -7,7 +7,7 @@
|
|||
//! a persistent connection.
|
||||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use hive_sh4re::priv_proto::{PRIV_SOCK, PrivRequest, PrivResponse};
|
||||
use hive_sh4re::priv_proto::{PRIV_SOCK, BindMount, PrivRequest, PrivResponse};
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::net::UnixStream;
|
||||
|
||||
|
|
@ -59,13 +59,15 @@ pub async fn list_containers() -> Result<String> {
|
|||
Ok(stdout)
|
||||
}
|
||||
|
||||
pub async fn write_nspawn_flags(container: &str, extra_nspawn_flags: &[&str]) -> Result<()> {
|
||||
pub async fn write_nspawn_flags(container: &str, binds: &[BindMount]) -> Result<()> {
|
||||
ok(call(&PrivRequest::WriteNspawnFlags {
|
||||
container: container.to_owned(),
|
||||
extra_nspawn_flags: extra_nspawn_flags.iter().map(|s| s.to_string()).collect(),
|
||||
binds: binds.to_vec(),
|
||||
}).await?)
|
||||
}
|
||||
|
||||
pub use hive_sh4re::priv_proto::BindMount;
|
||||
|
||||
pub async fn write_resource_limits(
|
||||
container: &str,
|
||||
memory_max: &str,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use hive_sh4re::priv_proto::{AGENT_PREFIX, MANAGER_NAME, META_DIR, PRIV_SOCK, SIBLING_CONTAINERS, PrivRequest, PrivResponse};
|
||||
use hive_sh4re::priv_proto::{AGENT_PREFIX, MANAGER_NAME, META_DIR, PRIV_SOCK, SIBLING_CONTAINERS, BindMount, PrivRequest, PrivResponse};
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::net::{UnixListener, UnixStream};
|
||||
use tokio::process::Command;
|
||||
|
|
@ -173,12 +173,13 @@ async fn exec(req: PrivRequest) -> Result<(String, String)> {
|
|||
|
||||
PrivRequest::ListContainers => container_run(&["list"]).await,
|
||||
|
||||
PrivRequest::WriteNspawnFlags { ref container, ref extra_nspawn_flags } => {
|
||||
PrivRequest::WriteNspawnFlags { ref container, ref binds } => {
|
||||
validate_container_system_name(container)?;
|
||||
for flag in extra_nspawn_flags {
|
||||
validate_nspawn_flag(flag)?;
|
||||
for bind in binds {
|
||||
validate_bind_path(&bind.host_path)?;
|
||||
validate_bind_path(&bind.container_path)?;
|
||||
}
|
||||
write_nspawn_flags(container, extra_nspawn_flags)?;
|
||||
write_nspawn_flags(container, binds)?;
|
||||
Ok((String::new(), String::new()))
|
||||
}
|
||||
|
||||
|
|
@ -352,17 +353,15 @@ 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 (would split the entry when the start script
|
||||
/// expands `$EXTRA_NSPAWN_FLAGS` unquoted), double-quotes (would
|
||||
/// break the `EXTRA_NSPAWN_FLAGS="..."` conf line), or null bytes.
|
||||
fn validate_nspawn_flag(flag: &str) -> Result<()> {
|
||||
if flag.is_empty()
|
||||
|| flag.bytes().any(|b| b == 0 || b == b'"' || b.is_ascii_whitespace())
|
||||
/// Validate a bind-mount path: must be absolute, non-empty, and contain
|
||||
/// no newlines, null bytes, or double-quotes (which would break the
|
||||
/// `EXTRA_NSPAWN_FLAGS="..."` conf line format).
|
||||
fn validate_bind_path(path: &str) -> Result<()> {
|
||||
if path.is_empty()
|
||||
|| !path.starts_with('/')
|
||||
|| path.bytes().any(|b| b == 0 || b == b'\n' || b == b'"')
|
||||
{
|
||||
bail!(
|
||||
"invalid nspawn flag {flag:?}: must be non-empty and contain no whitespace, double-quotes, or null bytes"
|
||||
);
|
||||
bail!("invalid bind path {path:?}: must be an absolute path with no newlines, null bytes, or double-quotes");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -371,7 +370,7 @@ fn validate_nspawn_flag(flag: &str) -> Result<()> {
|
|||
/// 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="<flags>"`.
|
||||
fn write_nspawn_flags(container: &str, extra_nspawn_flags: &[String]) -> Result<()> {
|
||||
fn write_nspawn_flags(container: &str, binds: &[BindMount]) -> Result<()> {
|
||||
let path = format!("/etc/nixos-containers/{container}.conf");
|
||||
let original = std::fs::read_to_string(&path)
|
||||
.with_context(|| format!("read {path}"))?;
|
||||
|
|
@ -398,7 +397,11 @@ fn write_nspawn_flags(container: &str, extra_nspawn_flags: &[String]) -> Result<
|
|||
out.push_str("HOST_ADDRESS6=\n");
|
||||
out.push_str("LOCAL_ADDRESS6=\n");
|
||||
out.push_str("HOST_BRIDGE=\n");
|
||||
let flags_joined = extra_nspawn_flags.join(" ");
|
||||
let flags: Vec<String> = binds.iter().map(|b| {
|
||||
let flag = if b.read_only { "--bind-ro" } else { "--bind" };
|
||||
format!("{flag}={}:{}", b.host_path, b.container_path)
|
||||
}).collect();
|
||||
let flags_joined = flags.join(" ");
|
||||
out.push_str(&format!("EXTRA_NSPAWN_FLAGS=\"{flags_joined}\"\n"));
|
||||
std::fs::write(&path, out).with_context(|| format!("write {path}"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,16 @@ pub const SIBLING_CONTAINERS: &[&str] = &["hive-forge", "hive-matrix", "hive-gat
|
|||
/// `{META_DIR}#{name}`, derived by `hive-priv` — never passed over the wire.
|
||||
pub const META_DIR: &str = "/var/lib/hyperhive/meta";
|
||||
|
||||
/// One bind-mount entry for `WriteNspawnFlags`.
|
||||
/// hive-priv constructs `--bind=<host_path>:<container_path>` (or `--bind-ro=`)
|
||||
/// and validates both paths before writing the conf file.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BindMount {
|
||||
pub host_path: String,
|
||||
pub container_path: String,
|
||||
pub read_only: bool,
|
||||
}
|
||||
|
||||
/// A request to the privileged helper.
|
||||
///
|
||||
/// Wire format: one JSON object per line over `/run/hive/priv.sock`.
|
||||
|
|
@ -59,10 +69,9 @@ pub enum PrivRequest {
|
|||
// --- Config file writes ---
|
||||
|
||||
/// Update `/etc/nixos-containers/<container>.conf`: strip network-isolation
|
||||
/// vars, force `PRIVATE_NETWORK=0`, and set `EXTRA_NSPAWN_FLAGS`.
|
||||
/// 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<String> },
|
||||
/// vars, force `PRIVATE_NETWORK=0`, and set `EXTRA_NSPAWN_FLAGS` from the
|
||||
/// provided bind-mount list. Written by `lifecycle::set_nspawn_flags`.
|
||||
WriteNspawnFlags { container: String, binds: Vec<BindMount> },
|
||||
|
||||
/// Write `/run/systemd/system/container@<container>.service.d/hyperhive-limits.conf`
|
||||
/// with `[Service]\nMemoryMax=<memory_max>\nCPUQuota=<cpu_quota>\n`.
|
||||
|
|
|
|||
Loading…
Reference in a new issue