fix(#702): route container journal reads through hive-priv

The privsep drop to the hive-core user left four journalctl -M <container>
call sites shelling out directly. -M enters the container namespace via the
machine bus, which needs root, so all container-journal reads failed with
Permission denied. Add a ReadContainerJournal verb to hive-priv and route
dashboard get_journal, manager get_logs, the rebuild-failure journal tail,
and the agent host-journal -M path through it. Host-journal reads (no -M)
stay direct via systemd-journal group membership.
This commit is contained in:
müde 2026-06-02 23:43:02 +02:00
commit 9e12012a95
7 changed files with 279 additions and 65 deletions

View file

@ -7,7 +7,7 @@
//! a persistent connection.
use anyhow::{Context as _, Result, bail};
use hive_sh4re::priv_proto::{BindMount, PRIV_SOCK, PrivRequest, PrivResponse};
use hive_sh4re::priv_proto::{BindMount, JournalOutput, PRIV_SOCK, PrivRequest, PrivResponse};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::UnixStream;
@ -81,6 +81,37 @@ pub async fn list_containers() -> Result<String> {
Ok(stdout)
}
/// Read a container's journal via the root helper (`journalctl -M`).
/// Returns `(stdout, stderr)`; a non-zero journalctl exit is reported in
/// `stderr` rather than as an `Err`, so callers can surface either.
#[allow(clippy::too_many_arguments)]
pub async fn read_container_journal(
container: &str,
lines: u32,
boot: bool,
output: JournalOutput,
unit: Option<String>,
priority: Option<String>,
grep: Option<String>,
since: Option<String>,
until: Option<String>,
) -> Result<(String, String)> {
check(
call(&PrivRequest::ReadContainerJournal {
container: container.to_owned(),
lines,
boot,
output,
unit,
priority,
grep,
since,
until,
})
.await?,
)
}
pub async fn write_nspawn_flags(container: &str, binds: &[BindMount]) -> Result<()> {
ok(call(&PrivRequest::WriteNspawnFlags {
container: container.to_owned(),