Watch
0
0
Fork
You've already forked hyperhive
0

swarm-bao: deny the secret publisher the callout xkey's seed

`swarm-secret-publisher` holds create and update on
`secret/data/swarm/services/*`, which covers the callout xkey's seed at
`swarm/services/swarm-nats/callout/xkey-seed`. It never writes that
path, but a write there would replace the key the queue's server seals
every callout request to. An exact-path `deny` outranks the prefix.

A create-only grant would not do: KV v2 honours create versus update, so
it would stop an overwrite, but it would still let the publisher plant a
seed of its choosing whenever the path is empty, on a first boot or
after a key replacement. The publisher needs no access to the path at
all.
This commit is contained in:
atlas 2026-10-05 22:13:58 +02:00 • committed by mara
commit 531d44d34b
2 changed files with 26 additions and 1 deletions

View file

@ -453,7 +453,7 @@ let
secretPublisherPolicyName = "swarm-secret-publisher";
secretPublisherCn = baoDeploy.secretPublisherCommonName;
# Three grants, and every narrowing in each is load-bearing.
# Three grants and one denial, and every narrowing in each is load-bearing.
#
# `secret/data/` is KV v2's ACL prefix, inserted by the engine rather than
# written by the caller — same trap as the controller's grant above.
@ -468,6 +468,10 @@ let
#
# Write-only. It copies secrets in and never reads one back; a read
# capability would let a file-copier recover every hive's credentials.
#
# The callout xkey's seed sits under `swarm/services/` and is no secret this
# principal copies, so its exact path, which outranks the prefix, is denied:
# a write there would replace the key every queue request is sealed to.
secretPublisherPolicyText = ''
path "${credentialMountPath}/data/swarm/hives/*" {
capabilities = ["create", "update"]
@ -477,6 +481,10 @@ let
capabilities = ["create", "update"]
}
path "${credentialMountPath}/data/${xkeySeedLeaf}" {
capabilities = ["deny"]
}
path "${credentialMountPath}/data/${controllerClientLeaf}" {
capabilities = ["create", "update"]
}

View file

@ -425,6 +425,23 @@ let
&& !(lib.hasInfix "secret/data/swarm/*" s)
&& !(lib.hasInfix "sys/policies/acl" s);
}
{
# The publisher's `services/*` write covers the callout xkey's seed, and
# only the exact-path denial, which outranks the prefix, keeps it from
# replacing the key. The control is an OIDC leaf beside it, which the
# prefix still grants.
name = "the publisher may not write the callout xkey's seed";
ok =
let
grants = grantsIn baoGrantHere.systemd.services.swarm-bao-secret-publisher-policy.script;
in
(grantFor grants "secret/data/swarm/services/swarm-nats/callout/xkey-seed").caps == [ "deny" ]
&&
(grantFor grants "secret/data/swarm/services/swarm-nats/oidc/client").caps == [
"create"
"update"
];
}
{
# The ordering is load-bearing and invisible at runtime: the controller's
# unit creates the KV and cert-auth mounts this one writes into, so