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); } {