From cf4a60b837d06ffdb58da5082d96c1abbb180f5e Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 5 Aug 2026 23:40:04 +0200 Subject: [PATCH] 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. --- nix/host-modules/hive-gateway/default.nix | 23 +++++++++++++++++++---- nix/host-modules/swarm-ca.nix | 9 +++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/nix/host-modules/hive-gateway/default.nix b/nix/host-modules/hive-gateway/default.nix index fc484b13..1ebd33c0 100644 --- a/nix/host-modules/hive-gateway/default.nix +++ b/nix/host-modules/hive-gateway/default.nix @@ -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 ''; }; diff --git a/nix/host-modules/swarm-ca.nix b/nix/host-modules/swarm-ca.nix index 2e6d2bd3..684b5de6 100644 --- a/nix/host-modules/swarm-ca.nix +++ b/nix/host-modules/swarm-ca.nix @@ -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"