docs(swarm): document the two adoption paths

The CA page described adoption as a documented `rm`, which is now only
half true: where the host owns the root it happens by itself, and the
split between the two cases is the part worth explaining rather than the
commands.
This commit is contained in:
atlas 2026-08-05 18:48:21 +02:00 committed by mara
commit 591d0e789f

View file

@ -78,15 +78,51 @@ on.
## 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.
A hive that predates the swarm root carries a self-signed `ca.pem`, and
adopting the hierarchy means replacing it. That invalidates an anchor
consumers already trust, and they refresh on their own schedule — agents
only pick up new trust when their container restarts, peers only on
their own rebuild. **Who is allowed to decide that is what splits the
two cases.**
Making that adoption a first-class, non-disruptive operation — rather
than a documented `rm` — is tracked separately; the mechanism it needs
(carrying the previous CA in the trust bundle across the overlap)
already exists.
### Where this host owns the root (`autoConfigure`)
Adoption happens by itself, once. `hive-tls-ca.service` notices that
`ca.pem` does not chain to the root, keeps the old certificate as
`ca-previous.pem`, and re-issues under the root; the next leaf is signed
by the new CA.
It is safe to automate here precisely because this is the
all-on-one-host shape: every consumer is on this box, so "when will they
have refreshed" is knowable rather than guessed.
The old CA stays in `trust-bundle.pem` afterwards, so adoption is
**additive to the anchor set before it is subtractive** — a container
that has not restarted yet still validates. Removing `ca-previous.pem`
is a deliberate later step: how long is long enough is a property of the
deployment, not something the unit can know.
A marker file (`.swarm-ca-adopted`) records that this ran. Its *absence*
is the trigger, so adoption fires once per hive rather than being
re-decided on every activation.
### Everywhere else
No automatic adoption. `hive-tls-ca.service` fails, loudly, naming both
certificates and giving the two-command recipe:
```
rm <tls.stateDir>/ca.pem <tls.stateDir>/ca-key.pem
systemctl restart hive-tls-ca.service
```
Failing rather than warning is deliberate: a hive whose CA does not
chain to the root it has been given is misconfigured, and a warning in a
build log is not something anyone reads twice.
To keep the current CA on purpose — a hive that deliberately stays
outside the hierarchy, or one mid-migration — `touch` the marker file
named in the message. That is a decision, and it is recorded as one.
A hive with **no** root configured at all is not affected by any of
this: it self-signs exactly as it always has.