Watch
0
0
Fork
You've already forked hyperhive
0

bao: let the UI's admins login write the review classifier's API key

The docs tell the operator to write
`secret/swarm/services/review-classifier/anthropic-api-key` by hand in
the bao UI, but the UI's OIDC login for authelia's `admins` group lands
on `swarm-operator-viewer`, which is list+read on `secret/metadata/*`
and nothing under `secret/data/`. bao refuses the write, so an admin
can't set the key and every classifier run fails with "no API key".

The viewer policy gains `create` and `update` on that one leaf, no
`read`: a UI session still never sees a secret value. The leaf is a
named binding, shared with the classifier's reader policy.

module-eval pins the viewer's two stanzas and that no `data/` grant
carries `read`. docs/swarm/secrets.md and services.md describe the
write.

Closes #5148
This commit is contained in:
atlas 2026-10-12 18:17:28 +02:00
commit 133d5578d2
4 changed files with 32 additions and 11 deletions

View file

@ -1836,10 +1836,11 @@ let
# ── the UI's OIDC login ─────────────────────────────────────────────────
{
# What an `admins` login through the UI holds: the key tree and KV
# metadata. Any `data/` path would hand a browser session every secret
# in the store, and any `sys/` one more than the UI needs.
name = "the operator viewer policy is list and read on KV metadata, and nothing under data/ or sys/";
# What an `admins` login through the UI holds: the key tree, KV
# metadata, and a write on the one leaf the operator fills by hand.
# A `read` under `data/` would hand a browser session a secret's
# value, and any `sys/` path more than the UI needs.
name = "the operator viewer policy is list and read on KV metadata, a write on the classifier's API key, and no data/ read or sys/";
ok =
let
viewer = grantsIn baoGrantHere.systemd.services.swarm-bao-operator-viewer-policy.script;
@ -1852,10 +1853,18 @@ let
"read"
];
}
{
path = "secret/data/swarm/services/review-classifier/anthropic-api-key";
caps = [
"create"
"update"
];
}
]
&& grantFor viewer "secret/data/swarm/agents/x/queue" == null
&& grantFor viewer "secret/metadata/swarm/agents/x/queue" != null
&& !(lib.any (g: lib.hasPrefix "secret/data" g.path || lib.hasPrefix "sys/" g.path) viewer);
&& !(lib.any (g: lib.hasPrefix "secret/data" g.path && lib.elem "read" g.caps) viewer)
&& !(lib.any (g: lib.hasPrefix "sys/" g.path) viewer);
}
{
# Route (a): enabling an auth method stays a bootstrap-token act. The