nix: issue the swarm-services leaf from bao's pki mount
The `pki` mount had no issuer and no principal could log in to it, so the swarm's service certificates were still minted by two openssl hops from a root key on disk. Close both halves and retire the openssl path with them. The mount now generates its own root, once. The granting unit asks bao whether an issuer already exists (`bao list pki/issuers`) before calling `pki/root/generate/internal`, so a rebuild or a reboot re-asserts the role and the grant without touching the anchor — a root that changed per boot would invalidate every certificate issued under it and every browser taught to trust it. The guard asks the store rather than looking for a marker file on this host's disk: a file is a claim about a mount that may have been restored from a snapshot or disabled and re-enabled underneath it. `swarm-services-issuer` stops being an inert policy. A fourth cert-auth role attaches it, following the shape the controller, the publisher and matrix-ctl already use, and glue-bao-tls.nix signs the leaf carrying its CN — that credential is what opens the mount, so it cannot come out of it. `swarm-services-cert.service` logs in with that leaf, calls `pki/issue/swarm-services`, and writes the result to the path hive-tls.nix already wrote and the gateway already copies from. The sub-CA layer does not move; it stops existing. The role's `allowed_domains`, read from the same `swarm.serviceDomains` the SANs come from, enforces at issue time what the sub-CA encoded in x509 `nameConstraints`, and with the root inside the mount there is nothing left for an intermediate to be an intermediate of. Not a flag day: the issuing root is published beside the leaf as `swarm-services-root.pem` (0644) and joins `trust-bundle.pem`, where the swarm root still sits. A leaf chaining to the old sub-CA and one issued by the store both verify against the same bundle, so hives can be rebuilt in any order. The same file is what an operator hands a browser — readable without a store login, which matters because every listener demands a client certificate. The eval-time warning about uncovered service names is gone rather than reworded. It fired on "this host does not hold the swarm root key", which was the reason a hive could end up serving its own leaf on a swarm-service name. Every hive now asks the store with its own identity, so that stopped being the thing that decides. Closes #4586
This commit is contained in:
parent
ebcc5bde89
commit
f4df4fc4a9
10 changed files with 700 additions and 315 deletions
|
|
@ -37,26 +37,42 @@ Moving the swarm CA onto its own host is then a matter of moving
|
|||
`services.hyperhive.swarm.ca.stateDir` and leaving `autoConfigure` off —
|
||||
there is no second code path to switch to.
|
||||
|
||||
### The warning about uncovered service names
|
||||
### The swarm's service names come from the secret store
|
||||
|
||||
The swarm's service names (`swarm.forge.domain`,
|
||||
`swarm.matrix.gatewayHost`, `swarm.authelia.domain`) default to
|
||||
siblings of the hive domain — `forge.<swarm.domain>`, not
|
||||
`forge.<hive domain>`. The hive CA's leaf is a **single-label** wildcard
|
||||
over the hive's own domain, so it can't cover them; only the
|
||||
swarm-services leaf can, and this host signs that one only under
|
||||
`autoConfigure`. A hive with neither serves the hive leaf on those names
|
||||
and every client sees a name mismatch.
|
||||
over the hive's own domain, so it can't cover them.
|
||||
|
||||
`hive-tls` emits an eval-time **warning** naming the uncovered names.
|
||||
It's deliberately not an assertion: this module can see what _it_ is
|
||||
able to issue, but not an operator-installed sub-CA in
|
||||
`swarm.ca.stateDir`, an external ACME setup, or a certificate delivered
|
||||
by any other means. If you have arranged one, expect the warning and
|
||||
ignore it. Otherwise either install the sub-CA, or pin the
|
||||
names back under the hive domain — a supported migration, since the
|
||||
sub-CA carries a name constraint on the _configured_ names and the
|
||||
swarm root carries no name constraints at all.
|
||||
That leaf is issued by the secret store's `pki` mount instead, from a
|
||||
root generated inside it. `swarm-services-cert.service` logs in with
|
||||
this host's services-issuer certificate, calls
|
||||
`pki/issue/swarm-services`, and writes the result to
|
||||
`/var/lib/hive-tls/swarm-services.pem` — the path the gateway already
|
||||
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 role's `allowed_domains` is read from the same
|
||||
`services.hyperhive.swarm.serviceDomains` the SANs are built from, so
|
||||
asking for a name nobody configured is a refusal from the store naming
|
||||
that name — not a certificate quietly issued for it.
|
||||
|
||||
**The root's public certificate is a file, on every hive:**
|
||||
`/var/lib/hive-tls/swarm-services-root.pem` (0644), written beside the
|
||||
leaf it signed and folded into `trust-bundle.pem`. On the host running
|
||||
the store it is also at
|
||||
`/var/lib/swarm-bao-services-pki/services-root.pem`. That is the file to
|
||||
hand a browser, and no store login is needed to read it — which matters,
|
||||
because every store listener demands a client certificate.
|
||||
|
||||
**The root is generated once and never again.** The granting unit asks
|
||||
the mount whether it already has an issuer (`bao list pki/issuers`)
|
||||
before calling `pki/root/generate/internal`, so a rebuild or a reboot
|
||||
re-asserts the role and the grant without touching the anchor. A root
|
||||
that changed per boot would invalidate every certificate issued under it
|
||||
and every browser taught to trust it.
|
||||
|
||||
## Constraints on the material
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue