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"