swarm-bao: write the secret publisher's policy and cert-auth role

A sibling unit rather than more script in swarm-bao-controller-policy, because
that unit's name is an operator-facing string: docs/getting-started/setup.md
tells a reader to run `systemctl status swarm-bao-controller-policy`. Widening
it to two principals makes the name wrong; renaming it makes the instruction
wrong.

`after` and not `requires`. The controller's unit creates the KV and cert-auth
mounts this one writes into, so the ordering is real — but a failed oneshot
still counts as finished, so `requires` would neither wait for its success nor
re-run this unit when the sibling's own retry eventually lands. Ordering plus
this unit's `Restart=on-failure` is what converges.

Four module-eval cases, because the unit arrived with every claim about it in
prose and the suite still reporting the same count: the grant is write-only and
reaches the hive prefix alone (pinned as the whole capability list, since an
added capability is what a presence check misses, with negative arms for the
agent prefix, the bare swarm prefix and the policy path); it is ordered after
the unit that creates the mounts; it renders on the host; and the control, that
it does not render inside the store's container.

Refs #3853
This commit is contained in:
atlas 2026-09-11 22:54:42 +02:00
commit 98f2a94d82
2 changed files with 98 additions and 0 deletions

View file

@ -841,6 +841,51 @@ let
in
lib.hasInfix "sys/policies/acl/hive-*" s && !(lib.hasInfix "sys/policies/acl/*" s);
}
{
# Same host-side reasoning as the controller's granting unit above: the
# write needs a client certificate and the host is the side that has one.
name = "a store host with a placed bootstrap token renders the publisher's granting unit too";
ok =
let
u = baoGrantHere.systemd.services.swarm-bao-secret-publisher-policy;
in
u.unitConfig.ConditionPathExists == "/run/secrets/bao-bootstrap.token"
&& lib.hasInfix "swarm-secret-publisher" u.script;
}
{
# The control for the case above, and the same one the controller's unit
# has: rendered on the host means NOT rendered in the container, where it
# would have neither an identity nor a route to the store.
name = "the publisher's granting unit is not rendered inside the store's container";
ok =
!(baoGrantHere.containers.swarm-bao.config.systemd.services ? swarm-bao-secret-publisher-policy);
}
{
# The whole point of a second principal. `hives/` and not `swarm/`, so it
# cannot touch an agent's or a service's credentials; and no `read`, so a
# unit whose job is copying a file cannot recover what is already there.
# Pinned as the full capability list, because an added capability is
# exactly what a presence check misses.
name = "the publisher's grant is write-only and reaches the hive prefix alone";
ok =
let
s = baoGrantHere.systemd.services.swarm-bao-secret-publisher-policy.script;
in
lib.hasInfix "path \"secret/data/swarm/hives/*\" {\n capabilities = [\"create\", \"update\"]" s
&& !(lib.hasInfix "secret/data/swarm/agents" s)
&& !(lib.hasInfix "secret/data/swarm/*" s)
&& !(lib.hasInfix "sys/policies/acl" s);
}
{
# 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
# without it a cold boot races and fails with "route entry not found",
# which names neither unit.
name = "the publisher's granting unit is ordered after the one that creates the mounts";
ok = lib.elem "swarm-bao-controller-policy.service" (
baoGrantHere.systemd.services.swarm-bao-secret-publisher-policy.after
);
}
{
# The policy authorising this route lives in another file, and nothing
# else relates the grants to the paths the code actually writes.