count readers, not writers, of the pull token in the cache's roster pin
This commit is contained in:
parent
7d78869104
commit
525cbe0355
1 changed files with 34 additions and 3 deletions
|
|
@ -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 =
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue