Watch
0
0
Fork
You've already forked hyperhive
0

Compare commits

...
Author SHA1 Message Date
atlas
ebcc5bde89 swarm-bao: shrink the read-grant comment under the comment-block max 2026-09-23 18:55:02 +02:00
atlas
87174863ca swarm-bao: grant the controller read on the agent credential prefix
mint_and_verify reads the queue credential back before writing it, so a
re-run keeps the value a live agent already authenticates with. that read
is read_optional, which maps only a 404 to absence — so with create/update
alone every mint aborted on a 403 at its first store read.

read on the same paths the stanza already grants create and update, and
nothing else: no list, no delete, no patch.
2026-09-23 18:55:02 +02:00
2 changed files with 19 additions and 15 deletions

View file

@ -180,9 +180,9 @@ let
# `secret`, and the engine inserts `data/`. Matching the code's spelling # `secret`, and the engine inserts `data/`. Matching the code's spelling
# literally would grant nothing. # literally would grant nothing.
# #
# Write-only on purpose. The controller mints these; nothing in its job reads # `read` too: `mint_and_verify` reads a credential back before writing so a
# one back, and a read capability here would let it recover every agent's # re-run keeps the value a live agent already holds instead of rotating it —
# credentials rather than merely replace them. # the read is required, not incidental.
controllerPolicyText = '' controllerPolicyText = ''
path "auth/cert/certs/hive-*" { path "auth/cert/certs/hive-*" {
capabilities = ["create", "update", "read", "delete"] capabilities = ["create", "update", "read", "delete"]
@ -201,7 +201,7 @@ let
} }
path "${credentialMountPath}/data/swarm/agents/*" { path "${credentialMountPath}/data/swarm/agents/*" {
capabilities = ["create", "update"] capabilities = ["create", "read", "update"]
} }
''; '';

View file

@ -182,11 +182,13 @@ let
&& !(lib.hasInfix "sys/policies/acl" s); && !(lib.hasInfix "sys/policies/acl" s);
} }
{ {
# 🩸 `read` is load-bearing here and is the one capability neither # 🩸 `read` is load-bearing here, and the publisher — the one sibling
# sibling has. matrix-ctl's first act is to read this path back and stop # that still has no `read` — shows what its absence costs. matrix-ctl's
# if something is there — that read IS "and only once", so without the # first act is to read this path back and stop if something is there —
# capability every container restart would mint a second access token and # that read IS "and only once", so without the capability every container
# invalidate the hive's. # restart would mint a second access token and invalidate the hive's.
# (The controller holds `read` for the same idempotency reason, on the
# agent prefix.)
name = "matrix-ctl may read back the one path it writes"; name = "matrix-ctl may read back the one path it writes";
ok = ok =
let let
@ -473,16 +475,18 @@ let
&& !(lib.hasInfix "path \"secret/*\"" s); && !(lib.hasInfix "path \"secret/*\"" s);
} }
{ {
# Write-only is the property, not an accident of how it was typed: a # The exact list is the property, not an accident of how it was typed.
# `read` here would let the controller recover every agent's credentials # `read` is in it because `mint_and_verify` reads a queue credential back
# instead of only replacing them. Pinned as the whole capability list, # before rewriting it; `list` is not, so the controller can fetch a
# because an added capability is exactly what a presence check misses. # credential only for an agent it was handed the name of, never enumerate
name = "the controller's grant on agent credentials is write-only"; # the tree. Pinned as the whole capability list, because an added
# capability is exactly what a presence check misses.
name = "the controller's grant on agent credentials is create/read/update and nothing else";
ok = ok =
let let
s = baoGrantHere.systemd.services.swarm-bao-controller-policy.script; s = baoGrantHere.systemd.services.swarm-bao-controller-policy.script;
in in
lib.hasInfix "path \"secret/data/swarm/agents/*\" {\n capabilities = [\"create\", \"update\"]" s; lib.hasInfix "path \"secret/data/swarm/agents/*\" {\n capabilities = [\"create\", \"read\", \"update\"]" s;
} }
{ {
# The policy above grants paths under a mount nothing else creates, so # The policy above grants paths under a mount nothing else creates, so