remove hive-level infra-container restart from web ui and agents
This commit is contained in:
parent
ccf9951e5d
commit
7516a4e10e
17 changed files with 112 additions and 272 deletions
|
|
@ -1,6 +1,5 @@
|
|||
//! Container-lifecycle request handlers (`Start` / `Restart` / `Kill` /
|
||||
//! `Update` / `ListDescendants`), including the capability-gated
|
||||
//! infra-container restart path. All are topology-guarded via
|
||||
//! `Update` / `ListDescendants`). All are topology-guarded via
|
||||
//! `super::require_descendant`.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
|
@ -27,20 +26,10 @@ pub(super) async fn handle_start(coord: &Arc<Coordinator>, agent: &str, name: &s
|
|||
}
|
||||
|
||||
/// `Restart` — enqueue a restart for a container. The caller must be an
|
||||
/// ancestor of `name` in the topology. The infra-container branch is
|
||||
/// orthogonal: it is gated on the `infra_admin` capability and audited, so it
|
||||
/// stays ahead of the topology guard.
|
||||
/// ancestor of `name` in the topology. Agents have no infra-container
|
||||
/// restart path: an infra name here just falls through to the topology
|
||||
/// guard like any other non-descendant name.
|
||||
pub(super) async fn handle_restart(coord: &Arc<Coordinator>, agent: &str, name: &str) -> Response {
|
||||
// Infra restart: an agent holding the `infra_admin` capability can
|
||||
// restart a hive infrastructure service (hive-ci / hive-forge /
|
||||
// hive-matrix) by passing its name to the same restart tool. The
|
||||
// `InfraContainer` enum parse both recognises these (never agent
|
||||
// children, so disjoint from the child path below) and yields the typed
|
||||
// value the restart path needs. It recognises `hive-gateway` too, which
|
||||
// is then refused — a name the agent surface knows but may not act on.
|
||||
if let Ok(container) = name.parse::<hive_priv_sock::InfraContainer>() {
|
||||
return handle_restart_infra(coord, agent, container).await;
|
||||
}
|
||||
if let Some(err) = require_descendant(agent, name, "restart") {
|
||||
return err;
|
||||
}
|
||||
|
|
@ -51,73 +40,6 @@ pub(super) async fn handle_restart(coord: &Arc<Coordinator>, agent: &str, name:
|
|||
Response::Ok
|
||||
}
|
||||
|
||||
/// Restart a hive infrastructure container on behalf of an agent that
|
||||
/// holds the `infra_admin` capability. The `container` is already a valid
|
||||
/// [`hive_priv_sock::InfraContainer`] (the caller parsed it); this gates on the capability
|
||||
/// and routes the systemctl restart through hive-priv. Direct, not
|
||||
/// approval-gated.
|
||||
async fn handle_restart_infra(
|
||||
coord: &Arc<Coordinator>,
|
||||
agent: &str,
|
||||
container: hive_priv_sock::InfraContainer,
|
||||
) -> Response {
|
||||
let name = container.name();
|
||||
// Record the attempt in the operator-visible privileged-action audit
|
||||
// trail, then emit a live `AuditEntryAdded` so the dashboard audit view
|
||||
// appends it off `/dashboard/stream`. Best-effort: `record` returns the
|
||||
// canonical row (or `None` on a sqlite blip), and we stream exactly that
|
||||
// row so the stored + streamed views can't drift. `action` is stable so
|
||||
// the dashboard can group/filter.
|
||||
let audit = |outcome: crate::audit_log::AuditOutcome, detail: Option<&str>| {
|
||||
if let Some(entry) = coord
|
||||
.audit_log
|
||||
.record(agent, "restart_infra", name, outcome, detail)
|
||||
{
|
||||
coord.emit_audit_entry(entry);
|
||||
}
|
||||
};
|
||||
// Some targets are off-limits to agents regardless of capability — the
|
||||
// gateway, because nginx fronts every hive service from the host and an
|
||||
// agent bouncing it takes out the forge, the dashboard and matrix at
|
||||
// once, including the route its own fix would have to travel. Checked
|
||||
// before the capability so the refusal doesn't read as "ask for
|
||||
// infra_admin"; no capability grants this.
|
||||
if !container.agent_restartable() {
|
||||
tracing::warn!(%agent, %name, "agent: infra restart denied (not agent-restartable)");
|
||||
audit(
|
||||
crate::audit_log::AuditOutcome::Err,
|
||||
Some("denied: target is not agent-restartable"),
|
||||
);
|
||||
return Response::Err {
|
||||
message: format!("`{name}` cannot be restarted by an agent; ask the operator"),
|
||||
};
|
||||
}
|
||||
if !crate::capabilities::has_cap(agent, hive_sh4re::permissions::Capability::InfraAdmin) {
|
||||
tracing::warn!(%agent, %name, "agent: infra restart denied (no infra_admin capability)");
|
||||
audit(
|
||||
crate::audit_log::AuditOutcome::Err,
|
||||
Some("denied: missing infra_admin capability"),
|
||||
);
|
||||
return Response::Err {
|
||||
message: format!(
|
||||
"restarting infra container `{name}` requires the `infra_admin` capability"
|
||||
),
|
||||
};
|
||||
}
|
||||
tracing::info!(%agent, %name, "agent: restart infra container");
|
||||
match crate::priv_client::restart_infra_container(container).await {
|
||||
Ok(()) => {
|
||||
audit(crate::audit_log::AuditOutcome::Ok, None);
|
||||
Response::Ok
|
||||
}
|
||||
Err(e) => {
|
||||
let msg = format!("{e:#}");
|
||||
audit(crate::audit_log::AuditOutcome::Err, Some(&msg));
|
||||
Response::Err { message: msg }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `Kill` — kill a container, unregister it, notify the swarm. The caller
|
||||
/// must be an ancestor of `name` in the topology.
|
||||
pub(super) async fn handle_kill(coord: &Arc<Coordinator>, agent: &str, name: &str) -> Response {
|
||||
|
|
|
|||
Loading…
Reference in a new issue