swarm-tls: narrow each gateway's services leaf to the names it fronts

Every gateway asked the store's `pki/issue/swarm-services` for the whole
swarm's service set, so a private key on any gateway host could serve a
valid certificate for services that host does not front and never has.

`swarm.localServiceDomains` derives the per-host subset by filtering
`swarm.serviceDomains` against the vhosts this host actually renders —
the deploy flags those vhosts are already guarded on, read once rather
than copied into a second filter. The leaf request and the coverage
guard that decides whether to re-issue both read it, so they cannot
disagree about which names the leaf owes.

The sub-CA's name constraint and the role's `allowed_domains` stay the
swarm-wide set: every host's subset is inside it, and narrowing the
constraint per host would turn one signing into N.
This commit is contained in:
atlas 2026-09-23 22:31:57 +02:00 • committed by mara
commit ed2ec52fe5
5 changed files with 89 additions and 20 deletions

View file

@ -54,10 +54,12 @@ copies from. Every hive does this with its own identity, so holding the
swarm root's private key stopped being what decides whether a hive can
serve its swarm's names.
The same `services.hyperhive.swarm.serviceDomains` that builds the SANs
also populates the role's `allowed_domains`, so asking for a name nobody
configured is a refusal from the store naming that name — not a
certificate quietly issued for it.
`services.hyperhive.swarm.serviceDomains` populates the role's
`allowed_domains`, so asking for a name nobody configured is a refusal
from the store naming that name — not a certificate quietly issued for
it. A host's own SANs are narrower still: it asks only for the names it
fronts a vhost for (`swarm.localServiceDomains`), so the key on one
gateway can't serve a swarm service that runs behind another.
**The root's public certificate is a file, on every hive:**
`/var/lib/hive-tls/swarm-services-root.pem` (0644), written beside the

View file

@ -92,7 +92,8 @@ own options, the way `swarm-ui.nix` and `swarm-authelia.nix` do.
⚠️ The certificate one is the hardest to predict and the most visible when
missed. `serviceDomains` is _both_ the `allowed_domains` the secret
store's `pki/roles/swarm-services` narrows to and the leaf's SAN list,
store's `pki/roles/swarm-services` narrows to and the list each gateway's
leaf draws its SANs from (it carries the ones that host fronts),
and the apex is a **sibling** of `forge.<swarm>` / `chat.<swarm>` /
`auth.<swarm>`, not a parent — no CA in the hierarchy issues for it
implicitly. Left out, the vhost falls back to the hive leaf and the