docs(swarm): cut the CA material into its own page

Second split under docs/swarm/, following the shape docs/turn-loop/ and
docs/web-ui/ already use.

The README keeps the `## Swarm CA` heading rather than deleting it: an
existing cross-reference targets that anchor, and a heading is the
cheapest thing to preserve. It now carries the one-paragraph summary and
a pointer, so a reader who lands on the anchor still arrives somewhere
that answers the question.

The CA page also absorbs three things that were true but undocumented —
why the trust bundle rather than the bare intermediate (openssl will not
terminate a chain at a non-self-signed anchor without -partial_chain),
that autoConfigure derives from enableAllLocalDefaults, and that the
name constraint excludes both IP families because a permitted-DNS-only
constraint says nothing about IP SANs.
This commit is contained in:
atlas 2026-08-05 18:34:39 +02:00
commit bcd4fbd918
3 changed files with 100 additions and 56 deletions

View file

@ -57,61 +57,13 @@ and `qualify()` / `qualified_label()` semantics.
## Swarm CA
A hive's internal TLS chains to a **swarm root CA**: the root signs each
hive's own CA, and that hive CA signs the gateway leaf. A peer that
trusts the root once validates every hive in the swarm, present and
future, instead of being pinned to each one by hand.
hive's own CA, and that hive CA signs the gateway leaf, so a peer that
trusts the root once validates every hive in the swarm rather than being
pinned to each one by hand.
Two provisioning modes, one structure — what differs is who puts the
artifacts on disk:
| | swarm root | this hive's CA |
| --- | --- | --- |
| default | operator-provided | operator-provided, else self-signed as before |
| `autoConfigure = true` (all on one host) | generated by `swarm-ca.service` on first boot | issued by `hive-tls-ca.service` under the root |
`services.hyperhive.swarm.ca.autoConfigure` selects between them, and is
**off by default**: a swarm's services and its hives can live on
different hosts, and a host cannot tell whether it is the one holding
the root, so setting the swarm CA up is an operator action rather than
something a host assumes. Turn it on for an all-on-one-host deployment
and the hierarchy costs no configuration.
**A hive given neither artifact keeps the self-signed CA it has always
had.** It serves TLS exactly as before and simply isn't part of a
swarm's trust hierarchy — the right outcome for a hive nobody has
federated yet. Only `autoConfigure` issues a hive sub-CA, because only
that case can: signing one needs the root's private key.
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 root's private key never reaches the nix store: the store is
world-readable, so a key committed to a flake is a key published to
everyone who builds it. Only certificates are distributed.
Each hive CA is **name-constrained** (X.509 `nameConstraints`) to that
hive's own domain, so a hive CA that leaks can only mint names inside
its own subdomain — enforced by every verifier rather than by
convention.
### What to hand a peer
`hivectl peer-config` prints the `cp` line. The file is
`<tls.stateDir>/trust-bundle.pem` — the hive CA plus the swarm root —
not `ca.pem`: an intermediate on its own is not something a verifier can
build a chain to. On a hive that predates the swarm root the bundle is
just that hive's self-signed CA, so the recipe does not change.
### Adopting the hierarchy on an existing hive
An existing `ca.pem` is never re-rooted automatically — swapping it
would break every consumer that already trusts it, and agents only pick
up new trust when their container restarts. To adopt, delete `ca.pem` +
`ca-key.pem` under `tls.stateDir` and restart `hive-tls-ca.service`;
the CA is re-issued under the root and the leaf re-signed. Until then
the hive serves TLS exactly as before and is simply not part of the
swarm's trust hierarchy.
Provisioning modes, what to hand a peer (`trust-bundle.pem`, never
`ca.pem`), the name constraints on a hive CA, and how an existing hive
adopts the hierarchy: [`ca.md`](ca.md).
## Running the swarm's shared services