Watch
0
0
Fork
You've already forked hyperhive
0

swarm-nats: manual callout needs all four keys, not two

The two callout assertions guarded the halves the server verifies with.
The responder needs the other halves, and nothing related them: a config
supplying only the public keys satisfies both, renders a syntactically
valid auth_callout block, and defines no responder unit.

Callout with no responder is the fail-closed state, so that queue refuses
every client — and a NATS denial arrives as a timeout, so the symptom is
every consumer hanging with nothing logged anywhere.

The build-time config check does run in this case and passes, because the
config is valid; what is missing is a unit, and the absence of a unit is
not an event.
This commit is contained in:
atlas 2026-08-24 22:28:31 +02:00 • committed by mara
commit aad5d3638f
2 changed files with 40 additions and 0 deletions

View file

@ -101,6 +101,13 @@ eval, naming the option it wants. That is deliberate: a queue that started
without them would accept `CONNECT {"user":"auth"}` from anyone sharing the
host's network namespace, and nothing would look wrong until somebody connected.
**All four or none** — the seed paths are required too, not just the public
keys. They are two halves of the same pair: the server verifies with the public
half, the responder signs with the private one. Supplying only the public keys
used to pass eval and leave the queue with an auth-callout nobody answers, which
refuses every client rather than degrading — and a refusal reaches the client as
a timeout, so the symptom is every consumer hanging with nothing logged.
One consequence of the generated path worth knowing before you debug it: with
`autoGenerateCallout` set, the queue's config is assembled at boot rather than at
build time, so a malformed one surfaces when the container starts instead of

View file

@ -448,6 +448,39 @@ in
and no key authenticates anyone who sends that name.
'';
}
{
# The two above guard the halves the SERVER needs; the responder
# needs the other halves, and nothing related them. Satisfying
# only the public ones renders a valid `auth_callout` block and
# defines no responder — and callout with no responder is the
# fail-closed state, so the queue refuses everyone.
#
# Nothing downstream catches it. `nats-server -t` runs in exactly
# this case and passes, because the config IS valid; the missing
# piece is a unit, and the absence of a unit is not an event. The
# symptom is every client timing out, since a NATS denial reaches
# the client as a timeout rather than an error.
# The binding itself, not a copy of its formula: what is being
# asserted IS "the responder is configured", and two copies of one
# boolean is two places for a future edit to land in only one.
assertion = responderConfigured;
message = ''
services.hyperhive.swarm.nats has callout public keys but no
seed files:
nats.calloutUserSeedFile = "${cfg.calloutUserSeedFile}"
nats.calloutIssuerSeedFile = "${cfg.calloutIssuerSeedFile}"
Each seed is the private half of the public key already set
here — the server verifies with the public half, the responder
signs with the private one. Configuring one side alone leaves
this queue with an auth-callout nobody answers, which refuses
every client rather than degrading.
Set both seed paths, or set nats.autoGenerateCallout = true to
have this host mint all four.
'';
}
{
assertion = autheliaUrl != null;
message = ''