hive-sh4re: type infra containers as an InfraContainer enum
Replace the stringly-typed infra-control path with an InfraContainer enum
(Ci/Forge/Gateway/Matrix). The variants are the allowlist: serde rejects any
unknown or unsafe name (hive-c0re has no variant) at the wire boundary, so
hive-priv no longer needs a root-side SIBLING_CONTAINERS.contains() check on
ControlInfraContainer — the type enforces it, and 'the daemon can't stop
itself' is a compile-time guarantee.
- priv_proto: InfraContainer enum; manual Serialize/Deserialize + FromStr +
unit_name() all key off one mapping, so the wire form ('hive-ci', …) is
unchanged and there's no drift. ControlInfraContainer.container: String ->
InfraContainer.
- hive-priv / priv_client / server.rs: thread the enum; scoped_infra returns
Vec<InfraContainer>; the control handler uses unit_name().
- agent_server: the infra_admin restart gate parses the name via FromStr
instead of a slice .contains().
- SIBLING_CONTAINERS stays (validate_container_name/_system_name still use it
for journals / general container validation); a test keeps the enum and the
slice in lockstep.
This commit is contained in:
parent
6b1dbebe5a
commit
cd025b3790
5 changed files with 146 additions and 71 deletions
|
|
@ -535,10 +535,11 @@ async fn handle_restart_child(coord: &Arc<Coordinator>, agent: &str, name: &str)
|
|||
// Infra-container restart: an agent holding the `infra_admin`
|
||||
// capability can restart a hive infrastructure container (hive-ci /
|
||||
// hive-gateway / hive-forge / hive-matrix) by passing its name to the
|
||||
// same restart tool. These names are never agent children, so this
|
||||
// branch is disjoint from the child-restart path below.
|
||||
if hive_sh4re::priv_proto::SIBLING_CONTAINERS.contains(&name) {
|
||||
return handle_restart_infra(coord, agent, name).await;
|
||||
// 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.
|
||||
if let Ok(container) = name.parse::<hive_sh4re::priv_proto::InfraContainer>() {
|
||||
return handle_restart_infra(coord, agent, container).await;
|
||||
}
|
||||
if let Some(err) = require_child(agent, name, "restart") {
|
||||
return err;
|
||||
|
|
@ -556,15 +557,16 @@ async fn handle_restart_child(coord: &Arc<Coordinator>, agent: &str, name: &str)
|
|||
}
|
||||
|
||||
/// Restart a hive infrastructure container on behalf of an agent that
|
||||
/// holds the `infra_admin` capability. The container name is already
|
||||
/// known to be in `SIBLING_CONTAINERS`; this gates on the
|
||||
/// capability and routes the systemctl restart through hive-priv (which
|
||||
/// re-validates the name root-side). Direct, not approval-gated.
|
||||
/// holds the `infra_admin` capability. The `container` is already a valid
|
||||
/// [`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: &str,
|
||||
container: hive_sh4re::priv_proto::InfraContainer,
|
||||
) -> AgentResponse {
|
||||
let name = container.unit_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
|
||||
|
|
@ -572,27 +574,26 @@ async fn handle_restart_infra(
|
|||
// 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", container, outcome, detail)
|
||||
if let Some(entry) = coord
|
||||
.audit_log
|
||||
.record(agent, "restart_infra", name, outcome, detail)
|
||||
{
|
||||
coord.emit_audit_entry(entry);
|
||||
}
|
||||
};
|
||||
if !crate::capabilities::has_cap(agent, hive_sh4re::Capability::InfraAdmin) {
|
||||
tracing::warn!(%agent, %container, "agent: infra restart denied (no infra_admin capability)");
|
||||
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 AgentResponse::Err {
|
||||
message: format!(
|
||||
"restarting infra container `{container}` requires the `infra_admin` capability"
|
||||
"restarting infra container `{name}` requires the `infra_admin` capability"
|
||||
),
|
||||
};
|
||||
}
|
||||
tracing::info!(%agent, %container, "agent: restart infra container");
|
||||
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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue