Watch
0
0
Fork
You've already forked hyperhive
0

swarm-nats: fetch the callout xkey before the server starts and seal callout traffic

swarm-bao-nats-callout-xkey runs on the queue's host before the
swarm-nats container, under the responder's store leaf: it runs
`swarm-nats-auth ensure-xkey`, reads the public half back from the
store, and writes it where the container's runtime config dir is. On a
change it restarts the server.

nats-server now always starts through the runtime wrapper, in manual
mode too. A privileged ExecStartPre renders auth_callout with the xkey
(and, in auto mode, the two generated nkeys) at every start, and fails
the server when no xkey has been fetched. The auto-mode key unit now
only mints seeds and writes their public halves.

The responder is wanted by nats.service, so a server started late, once
its key arrived, brings the responder up with it.
This commit is contained in:
atlas 2026-10-05 21:25:40 +02:00 • committed by mara
commit 9a241a1df1
4 changed files with 362 additions and 48 deletions

View file

@ -102,6 +102,109 @@ let
name = "the queue's payload ceiling is set, not inherited from the server's default";
ok = natsOldPath.containers.swarm-nats.config.services.nats.settings.max_payload == 8388608;
}
{
# What keeps a callout request inside one principal: the responder is
# the callout account's only member, and no system account exists to
# observe it from. The runtime fragment is rendered by the same
# `calloutBlocks`, so the count is the same there. The account name is
# the control that the assertions read the callout block at all.
name = "the callout account has exactly one member and no system account is configured";
ok =
lib.all
(
h:
let
settings = h.containers.swarm-nats.config.services.nats.settings;
in
settings.authorization.auth_callout.account == "AUTH"
&& lib.length settings.accounts.AUTH.users == 1
&& !(settings ? system_account)
)
[
natsOldPath
natsOnStoreHost
];
}
{
# The xkey exists only at runtime, so the server's config is rendered as
# it starts: always the wrapper, and never the store-rendered settings
# alone, which carry no xkey. `+` so the render may write `runtimeDir`.
name = "the queue's server starts only through the config rendered with its xkey";
ok =
lib.all
(
h:
let
c = h.containers.swarm-nats.config;
nats = c.systemd.services.nats.serviceConfig;
pre = nats.ExecStartPre or "";
in
lib.hasSuffix "/bin/nats-server -c /var/lib/nats-callout/nats.conf" nats.ExecStart
&& lib.hasPrefix "+/nix/store/" pre
&& lib.hasSuffix "-swarm-nats-render-config" pre
&& !(c.services.nats.settings.authorization.auth_callout ? xkey)
)
[
natsOldPath
natsOnStoreHost
];
}
{
# A server that starts late, once its xkey arrives, must bring the
# responder with it: the responder requires the server, so a server that
# failed to start left the responder inactive rather than failed.
name = "starting the queue's server starts its responder";
ok = lib.elem "nats.service" (responderOf natsOldPath).wantedBy;
}
{
# The key is created before anything needs it, by the responder's own
# verb under its own leaf, then read back from the public path and left
# where the server's render reads it. Before the container and wanted by
# it, and a fetch that finds nothing fails so `storeRetry` retries.
name = "the callout xkey is created, then fetched from the store before the queue's container starts";
ok =
let
u = natsWithStoreIdentity.systemd.services.swarm-bao-nats-callout-xkey;
s = builtins.unsafeDiscardStringContext u.script;
at = needle: lib.stringLength (lib.head (lib.splitString needle s));
ensure = "swarm-nats-auth ensure-xkey --store-cert-role swarm-nats-auth";
fetch = "bao kv get -field=value secret/swarm/public/swarm-nats/callout-xkey";
file = "/var/lib/nixos-containers/swarm-nats/var/lib/nats-callout/callout-xkey.pub";
in
lib.elem "container@swarm-nats.service" u.before
&& lib.elem "container@swarm-nats.service" u.wantedBy
&& u.environment.BAO_CLIENT_CERT == "/etc/pki/nats-auth.pem"
&& lib.hasInfix ensure s
&& lib.hasInfix fetch s
&& at ensure < at fetch
&& lib.hasInfix "atomic_write_secret 0444 \"\" ${file}" s
&& lib.hasInfix "refresh_consumer swarm-nats nats.service ${file}" s
&& !(lib.hasInfix "exit 0" s)
&& u.serviceConfig.Restart == "on-failure";
}
{
name = "a queue host with no responder leaf renders an xkey fetch that fails naming the options";
ok =
let
u = natsOldPath.systemd.services.swarm-bao-nats-callout-xkey;
in
!(u.environment ? BAO_CLIENT_CERT)
&& lib.hasInfix "deploy.nats.authBaoClientCertFile" u.script
&& lib.hasInfix "exit 1" u.script
&& !(lib.hasInfix "ensure-xkey" u.script);
}
{
# Its creates are what the responder's role was granted; on the store's
# host the role's unit has to have run first.
name = "on the store's host the callout xkey fetch waits for the responder's role";
ok =
let
u = natsOnStoreHost.systemd.services.swarm-bao-nats-callout-xkey;
in
u.environment.BAO_CLIENT_CERT == "/var/lib/swarm-bao-pki/nats-auth.pem"
&& lib.elem "swarm-bao-nats-auth-policy.service" u.after
&& lib.elem "swarm-bao-nats-auth-policy.service" u.wants;
}
{
# The responder refuses the hive's shared agent client and takes no flag
# granting it subjects: an `--agent-publish-subject` fails the unit at