feat(#1930): forward otel headers credential into agent containers via nspawn --load-credential
This commit is contained in:
parent
838cc9af9a
commit
21ec7dc23d
6 changed files with 140 additions and 20 deletions
|
|
@ -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}"))?;
|
||||
|
|
|
|||
Loading…
Reference in a new issue