From 642be5767897e9788cfe995d1dbb8657cc116969 Mon Sep 17 00:00:00 2001 From: atlas Date: Sun, 27 Sep 2026 21:55:35 +0200 Subject: [PATCH] swarm: narrow the revoke grant to the queue leaf, not the whole agent prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit revoke_queue_credential only ever deletes swarm/agents//queue (agent_queue_path + a literal "queue" suffix), never anything else under an agent's prefix. secret/metadata/swarm/agents/+/queue matches that exactly — `+` is bao's single-segment glob, the same form swarm-nats-auth's read grant already uses for the data-side path. Also rewords the module-eval test's stale note about a read/list grant handing a "write-only principal" the version history: the controller has held read on secret/data/swarm/agents/* since the mint-and-verify read-before-write change, so it was never write-only on that path. --- nix/host-modules/swarm-bao.nix | 2 +- nix/module-eval/bao-grants.nix | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/nix/host-modules/swarm-bao.nix b/nix/host-modules/swarm-bao.nix index e4a3927d..493749c2 100644 --- a/nix/host-modules/swarm-bao.nix +++ b/nix/host-modules/swarm-bao.nix @@ -400,7 +400,7 @@ let capabilities = ["create", "read", "update"] } - path "${credentialMountPath}/metadata/swarm/agents/*" { + path "${credentialMountPath}/metadata/swarm/agents/+/queue" { capabilities = ["delete"] } diff --git a/nix/module-eval/bao-grants.nix b/nix/module-eval/bao-grants.nix index a02fb9d3..0c4ac164 100644 --- a/nix/module-eval/bao-grants.nix +++ b/nix/module-eval/bao-grants.nix @@ -1155,15 +1155,19 @@ let # it separately — without this grant the revocation is a 403 and a # destroyed agent's credential stays valid. # - # Pinned as the whole capability list for the same reason as the stanza - # above: `read` or `list` here would hand a write-only principal the - # version history of every agent's secrets. - name = "the controller may revoke an agent credential, and only by removing every version of it"; + # `+` is one path segment, so this reaches `swarm/agents//queue` + # and nothing else an agent holds; `agents/*` would reach every object + # under the prefix, which `revoke_queue_credential` never asks the store + # to delete. Pinned as the whole capability list too: `read` or `list` + # here would let the controller read back the queue-secret version + # history it is meant only to delete. + name = "the controller may revoke an agent's queue credential, and only that one"; ok = let s = baoGrantHere.systemd.services.swarm-bao-controller-policy.script; in - lib.hasInfix "path \"secret/metadata/swarm/agents/*\" {\n capabilities = [\"delete\"]" s + lib.hasInfix "path \"secret/metadata/swarm/agents/+/queue\" {\n capabilities = [\"delete\"]" s + && !(lib.hasInfix "secret/metadata/swarm/agents/*" s) && !(lib.hasInfix "secret/metadata/*" s); } {