swarm-bao: drop the default-policy grant; swarm/public/ is read by name
Writing bao's `default` policy needed a new stanza in the granter's own policy, which only the bootstrap token can upload, so deploying it took a manual root-token step. Nothing needed it: the one reader of the callout xkey's public half is `swarm-bao-nats-callout-xkey`, which logs in as the `swarm-nats-auth` role, and that role's policy already names the path. `swarm-bao-nats-auth-policy` writes that policy as the granter on every deploy, under the granter's existing `sys/policies/acl/swarm-*` grant. Removed: the `swarm-bao-public-read-policy` unit, its marker block, the granter's `sys/policies/acl/default` stanza, the granter-role restart of that unit, the bao.md section and the module-eval cases pinning them.
This commit is contained in:
parent
52c931c3a7
commit
06784e55fa
4 changed files with 31 additions and 224 deletions
|
|
@ -170,8 +170,8 @@ let
|
|||
_: u: (u.environment.BAO_CLIENT_CERT or null) == granterCertFile
|
||||
) baoGrantWithConsumers.systemd.services;
|
||||
|
||||
# The fifteen units that write a grant as the granter, by name, for the
|
||||
# discovery control below.
|
||||
# The fourteen units that write a `swarm-*` grant, by name, for the discovery
|
||||
# control below.
|
||||
grantingUnitNames = [
|
||||
"swarm-bao-controller-policy"
|
||||
"swarm-bao-secret-publisher-policy"
|
||||
|
|
@ -187,7 +187,6 @@ let
|
|||
"swarm-bao-operator-viewer-policy"
|
||||
"swarm-bao-hive-leaf-policy"
|
||||
"swarm-bao-ci-runner-policy"
|
||||
"swarm-bao-public-read-policy"
|
||||
];
|
||||
|
||||
# Comment lines dropped first: both the HCL and the scripts explain
|
||||
|
|
@ -813,24 +812,24 @@ let
|
|||
}
|
||||
{
|
||||
# A store host without the granter's pair writes its grants some other
|
||||
# way, so none of the fifteen units may exist. Without this arm
|
||||
# way, so none of the fourteen units may exist. Without this arm
|
||||
# `lib.mkIf haveGranter` could be dropped from any of them and every other
|
||||
# case here would still pass.
|
||||
name = "without the granter's pair none of the fifteen granting units render";
|
||||
name = "without the granter's pair none of the fourteen granting units render";
|
||||
ok =
|
||||
let
|
||||
s = baoGranterOptOut.systemd.services;
|
||||
in
|
||||
lib.all (unit: !(s ? ${unit})) (grantingUnitNames ++ [ "swarm-bao-granter-role" ])
|
||||
# The control: the same store with the pair renders all fifteen.
|
||||
# The control: the same store with the pair renders all fourteen.
|
||||
&& lib.all (unit: baoGrantHere.systemd.services ? ${unit}) grantingUnitNames;
|
||||
}
|
||||
{
|
||||
# 🩸 What replaced the silent skip. With no bootstrap token the fifteen still
|
||||
# 🩸 What replaced the silent skip. With no bootstrap token the fourteen still
|
||||
# render, and a refused granter fails them with the step that fixes it.
|
||||
# A store host that never named a token is told to name one, since the
|
||||
# unit that sets the granter up renders only where it has.
|
||||
name = "a store host without a bootstrap token renders the fifteen, each failing loudly with the one-time step";
|
||||
name = "a store host without a bootstrap token renders the fourteen, each failing loudly with the one-time step";
|
||||
ok =
|
||||
let
|
||||
s = baoGranterNoToken.systemd.services;
|
||||
|
|
@ -937,7 +936,7 @@ let
|
|||
{
|
||||
# The granter's grants, whole. Pinned as the full list, because an added
|
||||
# path or capability is exactly what a presence check misses.
|
||||
name = "the granter's policy is exactly these twenty-one stanzas";
|
||||
name = "the granter's policy is exactly these twenty stanzas";
|
||||
ok =
|
||||
let
|
||||
cu = [
|
||||
|
|
@ -1029,14 +1028,6 @@ let
|
|||
path = "secret/data/swarm/services/swarm-bao-ui/oidc/client";
|
||||
caps = [ "read" ];
|
||||
}
|
||||
{
|
||||
path = "sys/policies/acl/default";
|
||||
caps = [
|
||||
"create"
|
||||
"read"
|
||||
"update"
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
|
|
@ -1051,10 +1042,9 @@ let
|
|||
];
|
||||
}
|
||||
{
|
||||
# Outside `swarm-*`, the store's own mounts and the `default` policy it
|
||||
# holds nothing: no hive's policy or role, no auth mount, no token, no
|
||||
# secret.
|
||||
name = "the granter grants nothing outside swarm-*, the store's own mounts and the default policy";
|
||||
# Outside `swarm-*` and the store's own mounts it holds nothing: no
|
||||
# hive's policy or role, no auth mount, no token, no secret.
|
||||
name = "the granter grants nothing outside swarm-* and the store's own mounts";
|
||||
ok =
|
||||
lib.all (p: grantFor granterGrants p == null) [
|
||||
"sys/policies/acl/hive-x"
|
||||
|
|
@ -1470,8 +1460,8 @@ let
|
|||
}
|
||||
{
|
||||
# What makes the case above mean something: discovery by the granter's
|
||||
# certificate reaches all fifteen units, and each yields calls.
|
||||
name = "the granter-policy check sees all fifteen granting units, and parses calls from each";
|
||||
# certificate reaches all fourteen units, and each yields calls.
|
||||
name = "the granter-policy check sees all fourteen granting units, and parses calls from each";
|
||||
ok =
|
||||
lib.sort lib.lessThan (lib.attrNames granterUnits) == lib.sort lib.lessThan grantingUnitNames
|
||||
&& lib.all (u: baoCalls u.script != [ ]) (lib.attrValues granterUnits)
|
||||
|
|
@ -1733,77 +1723,6 @@ let
|
|||
&& !(lib.elem "swarm-bao-operator-viewer-policy.service" g.after)
|
||||
&& !(v ? postStart && lib.hasInfix "swarm-bao-granter-role" v.postStart);
|
||||
}
|
||||
{
|
||||
# Every token carries bao's `default` policy, so a read added there is
|
||||
# a read for every authenticated client. Exactly one stanza, a read on
|
||||
# `swarm/public/` and nothing wider; the markers are what a later run
|
||||
# replaces it by.
|
||||
name = "the default policy gains one read, on swarm/public/, between markers";
|
||||
ok =
|
||||
let
|
||||
s = baoGrantHere.systemd.services.swarm-bao-public-read-policy.script;
|
||||
block = lib.head (
|
||||
lib.tail (
|
||||
lib.splitString "# hyperhive: swarm/public begin\n" (builtins.unsafeDiscardStringContext s)
|
||||
)
|
||||
);
|
||||
stanzas = grantsIn (lib.head (lib.splitString "# hyperhive: swarm/public end" block));
|
||||
in
|
||||
stanzas == [
|
||||
{
|
||||
path = "secret/data/swarm/public/*";
|
||||
caps = [ "read" ];
|
||||
}
|
||||
]
|
||||
&& lib.hasInfix "-v e='# hyperhive: swarm/public end'" s
|
||||
&& lib.hasInfix "printf '%s\\n' \"$wanted\" |\n bao policy write default -" s;
|
||||
}
|
||||
{
|
||||
# A read-modify-write of a policy every token depends on: the read
|
||||
# comes first, an empty one stops the unit before anything is written,
|
||||
# and the capability probe comes before both, so a granter without the
|
||||
# grant writes nothing.
|
||||
name = "the default policy is read before it is written, and never written from an empty read";
|
||||
ok =
|
||||
let
|
||||
s = builtins.unsafeDiscardStringContext baoGrantHere.systemd.services.swarm-bao-public-read-policy.script;
|
||||
at = needle: lib.stringLength (lib.head (lib.splitString needle s));
|
||||
probe = "caps=\"$(bao token capabilities sys/policies/acl/default)\"";
|
||||
read = "bao read -field=policy sys/policies/acl/default";
|
||||
emptyGuard = "if [ -z \"$current\" ]; then";
|
||||
write = "bao policy write default -";
|
||||
in
|
||||
lib.hasInfix probe s
|
||||
&& at probe < at "exit 0"
|
||||
&& at "exit 0" < at read
|
||||
&& at read < at emptyGuard
|
||||
&& at emptyGuard < at write
|
||||
&& lib.hasInfix "swarm-bao-granter-role re-runs this unit once it succeeds." s;
|
||||
}
|
||||
{
|
||||
# The granter's own policy is what lets that unit write `default`, and a
|
||||
# changed granter policy lands only with the bootstrap step, so the
|
||||
# step's success restarts the unit as it does the viewer's.
|
||||
name = "the granter may write the default policy, and its step restarts the public-read unit";
|
||||
ok =
|
||||
let
|
||||
s = baoGrantHere.systemd.services;
|
||||
g = s.swarm-bao-granter-role;
|
||||
p = s.swarm-bao-public-read-policy;
|
||||
in
|
||||
lib.any (
|
||||
gr:
|
||||
gr.path == "sys/policies/acl/default"
|
||||
&&
|
||||
gr.caps == [
|
||||
"create"
|
||||
"read"
|
||||
"update"
|
||||
]
|
||||
) granterGrants
|
||||
&& lib.hasInfix "swarm-bao-public-read-policy.service" g.serviceConfig.ExecStartPost
|
||||
&& lib.elem "swarm-bao-granter-role.service" p.after;
|
||||
}
|
||||
{
|
||||
# The client secret is on stdin, never an argument in /proc; the rest is
|
||||
# the swarm's authelia and the UI's own client id.
|
||||
|
|
|
|||
Loading…
Reference in a new issue