implements plan: agent execution safety — QEMU guest agent gate + health guard + policy docs

I — run pre-flights QEMU guest agent before queueing VM execution
  classifyAndGate now checks vm: targets for qemu_guest_agent attribute.
  If not_running/missing, returns immediate error instead of queuing forever.

II — policy.yaml: documented host-mutation classifier rule
  Added comment clarifying that host-level package/kernel mutations
  (apt-get install, dpkg, systemctl enable) always classify as
  config_mutation and thus need operator approval.

III — health attribute read-only in update_entity_attributes
  Strips scheduler-owned keys (health, last_check_at, last_check) from
  attribute updates with a clear message directing agents to
  get_health_summary / list_checks instead.

IV — Recorded discovered dependency edges
  vm:zimaos → depends-on → lxc:nfs-export (NFS /media/library mount)
  vm:zimaos → depends-on → host:strong (NFS /media/ludo-library mount)

Also updated the run tool description to mention both guardrails.
This commit is contained in:
2026-08-05 15:25:14 +02:00
parent a126cfa710
commit 1b9c761274
4 changed files with 54 additions and 5 deletions

View File

@@ -872,6 +872,28 @@ func classifyAndGate(ctx context.Context, pool *db.Pool, agentID, targetID uuid.
}
}
// VM transport pre-flight: qm guest exec requires the QEMU guest agent
// to be running inside the VM. If it's not, the execution would queue
// for approval and never execute — the agent has no way to learn it's
// stuck (spotted live 2026-08-05: vm:zimaos had qemu_guest_agent=not_running,
// the run queued forever, and the agent fell back to unsafe raw SSH).
if strings.HasPrefix(targetSlug, "vm:") {
var rawAttrs []byte
if err := pool.QueryRow(ctx, `SELECT attributes FROM entities WHERE id = $1`, targetID).Scan(&rawAttrs); err == nil {
var attrs map[string]any
if json.Unmarshal(rawAttrs, &attrs) == nil {
if qga, ok := attrs["qemu_guest_agent"]; ok {
qgaStr, _ := qga.(string)
if qgaStr == "not_running" || qgaStr == "" {
return textResult(fmt.Sprintf(
"run on %s blocked: QEMU guest agent is not running (%s). qm guest exec cannot reach this VM. Start the agent inside the guest first (e.g. via SSH/systemctl start qemu-guest-agent), then re-run. If the agent is running but the entity attribute is stale, update it with update_entity_attributes(slug=%s, attributes={\"qemu_guest_agent\":\"running\"}).",
targetSlug, qgaStr, targetSlug))
}
}
}
}
}
// Dedup: an identical pending command (same target, command, and
// purpose) blocks a re-request — stops a tool-calling loop from queuing
// the same approval repeatedly.