docs/swarm/credentials.md: make the target section bao-shaped, not file-shaped
The target section described the target as a shorter list of secret files — the intro promised "what changes is how many paths there are", and the per-secret contract pointed at the current-state table's file-shaped columns. That is today's shape with fewer rows, not the target. Rewrite it around the invariant: no secret the store holds is ever written to disk. The mTLS client certificate is the sole credential on disk, and the section now says why — it authenticates the puller to the store, so it is the one credential that cannot be fetched from it. Per secret the target still states minter, reader and renewal, but the reader is now a process pulling a store path at runtime under its own certificate, not a path being read off disk. The current-state section and all of its citations are untouched: the current state genuinely is files, and that contrast is what the page is for.
This commit is contained in:
parent
ae388c48a7
commit
f1e5fd5592
1 changed files with 56 additions and 33 deletions
|
|
@ -11,12 +11,13 @@ gets deleted and this file moves into its place. Until then the two coexist and
|
|||
neither is wrong: that one says where a file is, this one says whether it should
|
||||
be a file at all.
|
||||
|
||||
Two conventions carry over unchanged, because the target doesn't relax them.
|
||||
Two conventions frame everything below.
|
||||
|
||||
**A secret is a path, never a value.** Every option carrying key material or a
|
||||
token takes a file path, because a literal in a nix expression lands in the nix
|
||||
store — world-readable and permanent. That rule survives the migration; what
|
||||
changes is how many paths there are.
|
||||
**Where a nix option does carry a secret, it takes a path, never a value.** A
|
||||
literal in a nix expression lands in the nix store — world-readable and
|
||||
permanent. The target doesn't relax that rule; it removes almost every option
|
||||
the rule applies to, because in the target shape there is almost nothing on
|
||||
disk for an option to point at.
|
||||
|
||||
**Public material is a value.** A certificate or a public nkey is handed to
|
||||
every client that connects, so the store is a fine place for it. Nothing below
|
||||
|
|
@ -73,34 +74,56 @@ not credentials this page governs. Their absence isn't an oversight.
|
|||
|
||||
## Target state
|
||||
|
||||
⚠️ **This section describes what the swarm is moving toward — not what exists
|
||||
today.** Read the current-state table above for that. In particular, the first
|
||||
line below states intent, not current state, so it's stated as a target twice.
|
||||
⚠️ **This section describes the shape the swarm is meant to have — not what
|
||||
exists today, and not a claim of movement toward it.** Read the current-state
|
||||
table above for what exists. Nothing here is partly done.
|
||||
|
||||
**Every host needs a store mTLS certificate. Everything else is done through
|
||||
the store.** That's the target. Today only the host running the store itself
|
||||
mints its own leaves automatically — for itself, and pre-emptively for a controller and a
|
||||
publisher that might be co-located. For every other host, `swarm-bao.nix:529-531`
|
||||
says it outright: this is _"the credential an operator places by hand."_ No
|
||||
automated issuance path exists for an ordinary hive. The target is one
|
||||
out-of-band credential per host and zero out-of-band credentials anywhere else;
|
||||
the current state is one out-of-band credential per host **plus** every file in
|
||||
the last column of the table above.
|
||||
**No secret the store holds is ever written to disk.** That's the invariant,
|
||||
and everything else in this section follows from it. A value pulled from the
|
||||
store — bao — lives in the memory of the process that asked for it and nowhere
|
||||
else: not in a state directory, not in a bind-mounted file, not in a systemd
|
||||
credential, not in a rendered config, not for a moment before a unit deletes
|
||||
it. The target isn't a shorter list of secret files. It's the store, plus
|
||||
exactly one file.
|
||||
|
||||
**Agent containers get an mTLS certificate passed in from the hive, and pull
|
||||
their secrets themselves.** The hive's job becomes handing the agent an
|
||||
identity, not handing it secrets. Today no agent has an identity of any kind
|
||||
and every agent secret arrives as a file the host wrote.
|
||||
**That one file is the mTLS client certificate, and it's the only credential on
|
||||
disk.** It has to be a file, and the reason is the whole asymmetry: the
|
||||
certificate is what authenticates a principal to the store, so it's the one
|
||||
credential that can't be fetched from the store. Something has to exist on disk
|
||||
before the first request, or there's nothing to make the request with. Every
|
||||
host has one, and its only power is to ask the store for the rest. Today only
|
||||
the host running the store mints its own leaves automatically — for itself, and
|
||||
pre-emptively for a controller and a publisher that might be co-located. For
|
||||
every other host, `swarm-bao.nix:529-531` says it outright: this is _"the
|
||||
credential an operator places by hand."_
|
||||
|
||||
**No store-provided secret is persisted.** A value read from the store lives in
|
||||
the memory of the process that needs it and nowhere else — not in a state
|
||||
directory, not in a bind-mounted file, not in a systemd credential. Today every
|
||||
one of the four is persisted, and one of them twice.
|
||||
**The hive hands an agent an identity, never a secret.** An agent container is
|
||||
passed an mTLS certificate from its hive and from then on authenticates to the
|
||||
store under its own name, pulling what it needs when it needs it. No process
|
||||
reads a secret on another principal's behalf: the principal that needs a value
|
||||
is the principal that authenticates for it.
|
||||
|
||||
**Per secret, the docs specify minter, reader, and renewal strategy.** Those
|
||||
three columns are the contract. A secret whose renewal strategy is `NONE` is
|
||||
allowed to exist only as an honest record of where the swarm is — it's not a
|
||||
strategy, and a new secret doesn't get to write it.
|
||||
**Per secret, the target specifies minter, reader, and renewal strategy.**
|
||||
Those three are the contract, and in this shape the **reader is a process
|
||||
pulling a store path at runtime** — not a path on disk, and not a unit whose
|
||||
job is to turn a store value into a file.
|
||||
|
||||
| store path | minter | reader — pulls at runtime, holds in memory | renewal |
|
||||
| -------------------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| `swarm/agents/<agent>/matrix/<account>` | `swarm-controller` | the agent container itself, under the certificate its hive passed in | **must be stated** — no strategy is chosen for this credential, and `NONE` isn't one |
|
||||
| `swarm/hives/<hive>/matrix/appservice-token` | one minter, on the authelia host | the hive process that presents the token to its homeserver, under the hive's own certificate | **must be stated** — as above |
|
||||
| `swarm/hives/<hive>/queue/agent` | authelia | the agent container presenting the OIDC client to the swarm queue, under its own certificate | **must be stated** — as above |
|
||||
| `swarm/services/<clientId>/oidc/client` | authelia | the service process that presents the client secret, under the certificate of the host it runs on | **must be stated** — as above |
|
||||
| _(not in the store)_ the mTLS client leaf | the store's own PKI, or an operator placing it by hand | its own client, off disk — **the sole exception above**, because it's what makes every other row's pull possible | **must be stated** — as above |
|
||||
|
||||
A renewal cell reading `NONE` is allowed only in the current-state table above,
|
||||
as an honest record of where the swarm is. It isn't a strategy, and a new
|
||||
secret doesn't get to write it.
|
||||
|
||||
The appservice token's second, uncoordinated minter isn't a row here for a
|
||||
reason: in the target each credential has exactly one minter, so a fallback
|
||||
mint on the reading host is a thing the target deletes, not a thing it
|
||||
describes.
|
||||
|
||||
## Progressive enhancement
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue