matrix: publish the appservice token from the swarm, not just read it
`glue-matrix-bao-token.nix` has read `secret/swarm/hives/<hive>/matrix/appservice-token` since it landed, but nothing ever wrote that path. The store was empty in every deployment, so every read degraded to "keep what activation minted" and each hive stayed the origin of a value the swarm has to agree on — two hives never converged. `swarm-secret-publish` is now the producer. It already holds a store identity, already writes under the hive prefix, and already runs per hive in the roster, so the mint is a third loop beside the two OIDC copies rather than a second shape of this unit. Idempotence comes from a record of its own, not from the store: this principal is granted `create`/`update` with no `read`, so it cannot ask whether a hive already has a token. It keeps what it minted under `StateDirectory=` (0700 dir, 0600 file) and mints only when that file is missing or empty; the `put` runs every time, because re-putting the same bytes changes nothing for a reader while a mint whose publish failed must not be left as a token this host holds and no hive can reach. The token never becomes a nix literal and never reaches argv: the mint redirects into a file, and the publish hands bao `value=@<path>` so bao opens it itself — the same handling the OIDC loops use. `hive-matrix.nix`'s activation mint stays as the genuine first-boot fallback. It already fires only when the token file is absent, so it cannot clobber a value the store delivered; `hs_token` has no swarm half and is still minted there for real. Refs #4402
This commit is contained in:
parent
7ee7080b21
commit
199afa41c8
5 changed files with 167 additions and 35 deletions
|
|
@ -1477,6 +1477,41 @@ let
|
|||
lib.hasInfix "secret/swarm/hives/h1/queue/agent" s
|
||||
&& lib.hasInfix "secret/swarm/hives/h2/queue/agent" s;
|
||||
}
|
||||
{
|
||||
# The producer's end of the read `glue-matrix-bao-token.nix` already did.
|
||||
# Both hives are asserted for the reason the queue case above gives: a
|
||||
# publisher that knew one name rather than the roster would pass on a
|
||||
# single-hive fixture and strand the second hive ever added — which is
|
||||
# the two-hives-never-converge shape this slice exists to close.
|
||||
name = "the publisher mints an appservice token for every hive and writes it to that hive's matrix path";
|
||||
ok =
|
||||
let
|
||||
s = secretPublisherHere.systemd.services.swarm-secret-publish.script;
|
||||
in
|
||||
lib.hasInfix "secret/swarm/hives/h1/matrix/appservice-token" s
|
||||
&& lib.hasInfix "secret/swarm/hives/h2/matrix/appservice-token" s
|
||||
&& lib.hasInfix "/dev/urandom" s;
|
||||
}
|
||||
{
|
||||
# What makes a re-publish idempotent. This principal is granted
|
||||
# `create`/`update` and no `read`, so it cannot ask the store whether a
|
||||
# hive already has a token — with nowhere to keep one, every run would
|
||||
# mint a fresh value and rotate the swarm's token. A state directory is
|
||||
# that somewhere, and nothing else in this unit needs one, so its absence
|
||||
# means exactly this.
|
||||
#
|
||||
# The second arm is the mint's own guard: the state file is only written
|
||||
# when it is missing or empty. Dropping that test leaves a unit that
|
||||
# still has a state directory and still rotates on every boot.
|
||||
name = "the publisher keeps the tokens it minted, and mints only when it holds none";
|
||||
ok =
|
||||
let
|
||||
u = secretPublisherHere.systemd.services.swarm-secret-publish;
|
||||
in
|
||||
lib.hasInfix "matrix-appservice-token" (u.serviceConfig.StateDirectory or "")
|
||||
&& u.serviceConfig.StateDirectoryMode or null == "0700"
|
||||
&& lib.hasInfix "if [ ! -s \"$src\" ]" u.script;
|
||||
}
|
||||
{
|
||||
# A property of the SET, not of one unit: both of these authenticate by
|
||||
# certificate, and `BAO_CLIENT_CERT` is transport rather than identity, so
|
||||
|
|
|
|||
Loading…
Reference in a new issue