Watch
0
0
Fork
You've already forked hyperhive
0

count readers, not writers, of the pull token in the cache's roster pin

This commit is contained in:
atlas 2026-10-29 00:09:12 +01:00
commit 525cbe0355

View file

@ -316,6 +316,15 @@ let
best: g: if best == null || lib.stringLength g.path > lib.stringLength best.path then g else best best: g: if best == null || lib.stringLength g.path > lib.stringLength best.path then g else best
) null globs; ) null globs;
# Whether the policy text in `text` lets its holder read `secret/data/swarm/<leaf>`,
# by `grantFor`'s rule, so a glob that reaches the leaf counts and a write does not.
grantReads =
leaf: text:
let
g = grantFor (grantsIn text) "secret/data/swarm/${leaf}";
in
g != null && lib.elem "read" g.caps;
ungranted = ungranted =
grants: units: grants: units:
lib.concatLists ( lib.concatLists (
@ -2139,20 +2148,42 @@ let
{ {
# The signing key is the cache's alone; the push token is also the # The signing key is the cache's alone; the push token is also the
# builder's, and is read there. The pull token is also the controller's, # builder's, and is read there. The pull token is also the controller's,
# which only reads it (pinned above). # which only reads it (pinned above). The pull token's value is what
name = "no granting unit but the cache's names its signing key, and only it and the controller name its pull token"; # the roster guards, so it counts the units that may read it: the
# operator's write-only grant on it, which replaces the token and
# exposes nothing, stays outside.
name = "no granting unit but the cache's names its signing key, and only it and the controller read its pull token";
ok = ok =
lib.filter ( lib.filter (
u: lib.hasInfix "services/attic/jwt-secret" baoGrantHere.systemd.services.${u}.script u: lib.hasInfix "services/attic/jwt-secret" baoGrantHere.systemd.services.${u}.script
) grantingUnitNames == [ "swarm-bao-attic-policy" ] ) grantingUnitNames == [ "swarm-bao-attic-policy" ]
&& &&
lib.filter ( lib.filter (
u: lib.hasInfix "services/attic/pull-token" baoGrantHere.systemd.services.${u}.script u: grantReads "services/attic/pull-token" baoGrantHere.systemd.services.${u}.script
) grantingUnitNames == [ ) grantingUnitNames == [
"swarm-bao-controller-policy" "swarm-bao-controller-policy"
"swarm-bao-attic-policy" "swarm-bao-attic-policy"
]; ];
} }
{
# Control for the property above: `grantReads` tells a read from a write
# on the same path, and the operator's policy does name the pull token.
name = "a grant that only writes the pull token is not a read of it, and one that reads it is";
ok =
let
stanza = caps: ''
path "secret/data/swarm/services/attic/pull-token" {
capabilities = [${caps}]
}
'';
viewer = baoGrantHere.systemd.services.swarm-bao-operator-viewer-policy.script;
in
!(grantReads "services/attic/pull-token" (stanza ''"create", "update"''))
&& grantReads "services/attic/pull-token" (stanza ''"read"'')
&& grantReads "services/attic/pull-token" (stanza ''"create", "read"'')
&& lib.hasInfix "services/attic/pull-token" viewer
&& !(grantReads "services/attic/pull-token" viewer);
}
{ {
name = "the PKI unit signs the cache's leaf under its own subject"; name = "the PKI unit signs the cache's leaf under its own subject";
ok = ok =