diff --git a/docs/gateway.md b/docs/gateway.md index e569b0fc..4274892d 100644 --- a/docs/gateway.md +++ b/docs/gateway.md @@ -209,7 +209,7 @@ The issuer is a **host-held hive CA**, not a bare self-signed leaf. A host servi **Why on by default**: matrix-dart-sdk (FluffyChat's SDK) hardcodes `https:///.well-known/matrix/client` for homeserver discovery and refuses to fall back to plain http. Without TLS the browser client cannot bootstrap. -**Cert shape**: leaf subject CN = bare hive domain; subjectAltName covers ``, `forge.`, `matrix.`, and wildcard `*.` so all current and future sub-domain vhosts validate under the same leaf + the hive CA. +**Cert shape**: leaf subject CN = bare hive domain; subjectAltName is `` plus wildcard `*.`, so all current and future sub-domain vhosts validate under the same leaf + the hive CA. A swarm service whose name is *not* under this hive's domain cannot be added here — the hive CA is name-constrained to ``, and a violating SAN invalidates the whole leaf, not just that name. Those names get the swarm-services leaf instead ([`swarm/ca.md`](swarm/ca.md)). **Rotation**: `hive-tls-ca.service` is idempotent — it re-signs the leaf when it is missing or within 30 days of expiry, always under the same CA (so consumer trust is undisturbed). The CA itself is regenerated only if missing or already expired. To force a leaf rotation, delete `gateway.pem` under the state dir and restart the unit, then reload `nginx`. diff --git a/nix/host-modules/hive-tls.nix b/nix/host-modules/hive-tls.nix index 1df51f5f..5d6a6db5 100644 --- a/nix/host-modules/hive-tls.nix +++ b/nix/host-modules/hive-tls.nix @@ -163,9 +163,22 @@ let # The hive's own leaf: signed by the hive CA, covering the hive domain # and its sub-domains. + # + # `forge.` and `matrix.` were listed here explicitly and both were + # redundant with the wildcard beside them — a wildcard covers exactly + # one label, and those are one label. Naming them read as policy + # ("these two services live under this leaf"), which is the reason they + # outlived it: a configured service name is no longer necessarily under + # this domain, and when it isn't, this is the one list it cannot join. + # The hive CA is `nameConstraints=permitted;DNS:`, and a + # violation invalidates the CERTIFICATE rather than the offending name + # — measured with openssl, not reasoned about — so one sibling name + # added here takes the leaf down for the dashboard and every other + # vhost sharing it. Configured service names get the swarm-services + # leaf below, whose sub-CA is constrained to exactly those names. signHiveLeaf = '' ${signLeafScript} "$d" gateway ${lib.escapeShellArg domain} \ - ${lib.escapeShellArg "DNS:${domain},DNS:forge.${domain},DNS:matrix.${domain},DNS:*.${domain}"} \ + ${lib.escapeShellArg "DNS:${domain},DNS:*.${domain}"} \ "$d/ca.pem" "$d/ca-key.pem" '';