fix(nix): a missing swarm-services leaf must not kill the whole gateway

main is already unborked -- the constraint-syntax fix landed directly in
31e47632 and the hive-tls-ca ordering race in b5066ad0. What is left is
the reason the syntax bug became an OUTAGE rather than a warning, and it
is independent of both.

nginx refuses to load a config naming a cert file that does not exist:
`cannot load certificate ... no such file` fails the pre-start test, so
the vhost does not degrade -- the ENTIRE proxy dies and restart-loops
into start-limit-hit, taking the forge, the dashboard and matrix with
it. Signing tolerated the leaf's absence and the container import
tolerated it; the vhost reference did not, and that is the one that
decides whether anything serves.

Absence stays a normal state here: the leaf only exists where the swarm
CA is autoconfigured. So the import now installs the HIVE leaf at the
services path instead of removing the file. That is a name mismatch on
those vhosts -- browsers warn, strict clients refuse -- and every other
name keeps working. A bad certificate is a bad afternoon; a dead gateway
is a dead hive.

Also keeps the comment explaining why every nameConstraints entry needs
its own `permitted;` prefix, so the next reader doesn't re-derive it
from an outage.
This commit is contained in:
atlas 2026-08-05 23:40:04 +02:00
commit cf4a60b837
2 changed files with 28 additions and 4 deletions

View file

@ -383,14 +383,29 @@ in
# names are siblings of it.
#
# Absent is a normal state, not a failure: the leaf exists
# only where the swarm CA is autoconfigured. Copying it
# conditionally keeps a hive whose certs come from its
# operator working unchanged.
# only where the swarm CA is autoconfigured, and issuance
# can also fail on a host that wants one.
#
# ⚠️ When it is absent the HIVE leaf goes to this path
# anyway, and that fallback is load-bearing rather than
# tidy. nginx refuses to load a config naming a cert file
# that does not exist — `cannot load certificate … no such
# file` fails the pre-start test, so the vhost does not
# degrade, the ENTIRE gateway dies and takes the forge, the
# dashboard and matrix with it. Serving the hive leaf on a
# swarm-service name is a name mismatch: browsers warn,
# strict clients refuse, everything else keeps working, and
# the operator gets a bad cert instead of no hive.
#
# Measured, not theorised: this exact path took pr1ma's
# gateway down when the services sub-CA failed to issue.
if [ -s /run/hive-ca/swarm-services.pem ]; then
install -m 0644 /run/hive-ca/swarm-services.pem ${svcCert}
install -m 0640 -g nginx /run/hive-ca/swarm-services-key.pem ${svcKey}
else
rm -f ${svcCert} ${svcKey}
echo "no swarm-services leaf serving the hive leaf on those names (mismatch, not an outage)" >&2
install -m 0644 /run/hive-ca/gateway.pem ${svcCert}
install -m 0640 -g nginx /run/hive-ca/gateway-key.pem ${svcKey}
fi
'';
};

View file

@ -270,6 +270,15 @@ in
# redundant — a DNS constraint says nothing about an
# iPAddress SAN, and an unconstrained name type is a name
# type this CA is unconstrained for.
#
# ⚠️ EVERY entry carries its own `permitted;` / `excluded;`
# prefix. openssl's parser takes the qualifier per subtree, not
# once for a run of them: `permitted;DNS:a,DNS:b` is rejected
# outright with `v2i_NAME_CONSTRAINTS: invalid syntax`, which
# fails the whole unit. The hive CA next door emits exactly one
# permitted name, so the missing-prefix form is accidentally
# valid there and does NOT generalise — this list is always
# longer than one.
printf 'nameConstraints=critical,%s,excluded;IP:0.0.0.0/0.0.0.0,excluded;IP:0:0:0:0:0:0:0:0/0:0:0:0:0:0:0:0\n' \
"$(echo "$want" | sed 's/^/permitted;DNS:/' | paste -sd, -)"
} > "$ext"