feat(#1930): forward otel headers credential into agent containers via nspawn --load-credential

This commit is contained in:
damocles 2026-06-23 21:12:39 +02:00 committed by mara
commit 21ec7dc23d
6 changed files with 140 additions and 20 deletions

View file

@ -21,9 +21,9 @@ use std::path::{Path, PathBuf};
use anyhow::{Context as _, Result, bail};
use hive_sh4re::priv_proto::{
AGENT_PREFIX, AGENT_STATE_ROOT, BindMount, InfraAction, InfraContainer, JournalQuery, META_DIR,
NetworkIsolation, PRIV_SOCK, PrivEvent, PrivRequest, PrivResponse, PrivStream, PrivStreamLine,
SIBLING_CONTAINERS,
AGENT_PREFIX, AGENT_STATE_ROOT, BindMount, CredentialMount, InfraAction, InfraContainer,
JournalQuery, META_DIR, NetworkIsolation, PRIV_SOCK, PrivEvent, PrivRequest, PrivResponse,
PrivStream, PrivStreamLine, SIBLING_CONTAINERS,
};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::unix::OwnedWriteHalf;
@ -202,7 +202,8 @@ async fn exec(req: PrivRequest, writer: &mut OwnedWriteHalf) -> Result<(String,
ref container,
ref binds,
ref isolation,
} => handle_write_nspawn_flags(container, binds, isolation.as_ref()),
ref load_credentials,
} => handle_write_nspawn_flags(container, binds, isolation.as_ref(), load_credentials),
PrivRequest::WriteResourceLimits {
ref container,
@ -323,22 +324,44 @@ async fn container_flake_action(
}
}
/// `WriteNspawnFlags` — validate the container + every bind path, then
/// write the container's nspawn flag overrides.
/// `WriteNspawnFlags` — validate the container + every bind path + every
/// credential entry, then write the container's nspawn flag overrides.
fn handle_write_nspawn_flags(
container: &str,
binds: &[BindMount],
isolation: Option<&NetworkIsolation>,
load_credentials: &[CredentialMount],
) -> Result<(String, String)> {
validate_container_system_name(container)?;
for bind in binds {
validate_bind_path(&bind.host_path)?;
validate_bind_path(&bind.container_path)?;
}
write_nspawn_flags(container, binds, isolation)?;
for cred in load_credentials {
validate_credential_name(&cred.name)?;
// Same path rules as binds (absolute, no colon/newline/quote/null):
// the colon ban is essential since `--load-credential=name:path`
// uses `:` as the name/path separator.
validate_bind_path(&cred.host_path)?;
}
write_nspawn_flags(container, binds, isolation, load_credentials)?;
Ok((String::new(), String::new()))
}
/// A systemd credential id must be a short token — restrict to
/// `[A-Za-z0-9_.-]` so it can't inject extra `--load-credential` argv or
/// break the `name:path` shape.
fn validate_credential_name(name: &str) -> Result<()> {
if name.is_empty()
|| !name
.bytes()
.all(|b| b.is_ascii_alphanumeric() || matches!(b, b'_' | b'.' | b'-'))
{
bail!("invalid credential name {name:?}: must be non-empty [A-Za-z0-9_.-]");
}
Ok(())
}
/// `RemoveServiceDropin` — remove the container service's drop-in dir
/// if present (idempotent).
fn remove_service_dropin(container: &str) -> Result<(String, String)> {
@ -1306,6 +1329,7 @@ fn write_nspawn_flags(
container: &str,
binds: &[BindMount],
isolation: Option<&NetworkIsolation>,
load_credentials: &[CredentialMount],
) -> Result<()> {
use std::fmt::Write as _;
let path = format!("/etc/nixos-containers/{container}.conf");
@ -1350,13 +1374,23 @@ fn write_nspawn_flags(
out.push_str("LOCAL_ADDRESS6=\n");
out.push_str("HOST_BRIDGE=\n");
}
let flags: Vec<String> = binds
let mut 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();
// Credential forwarding: nspawn loads each host secret into the
// container's credential store under `<name>`; inner units inherit it
// via `LoadCredential=<name>`. Validated (name charset + bind-path
// rules) in handle_write_nspawn_flags above.
for cred in load_credentials {
flags.push(format!(
"--load-credential={}:{}",
cred.name, cred.host_path
));
}
let flags_joined = flags.join(" ");
let _ = writeln!(out, "EXTRA_NSPAWN_FLAGS=\"{flags_joined}\"");
std::fs::write(&path, out).with_context(|| format!("write {path}"))?;