The hive directory and the WireGuard mesh become handbook pages of their own, the controller's mint-failure handling moves to controller.md, and the credential-by-credential mechanics and the secret store UI's proxy move to a new implementation page.
91 lines
5.3 KiB
Markdown
91 lines
5.3 KiB
Markdown
# Credentials
|
|
|
|
The swarm's credential store is bao. This page is the overview of how the swarm
|
|
treats credentials; [`credentials-reference.md`](../../4-implementation/swarm/credentials-reference.md)
|
|
lists each one: who mints it, who reads it, and how it renews, with the notes for
|
|
operating them, and [`credentials.md`](../../4-implementation/swarm/credentials.md)
|
|
holds the store's mechanics.
|
|
|
|
**Store secrets stay off disk.** A process pulls a value from the store at
|
|
runtime and holds it in memory only.
|
|
|
|
**The mTLS client certificate is the only credential on disk.** Each
|
|
identity (an agent, a hive, a swarm-level service) has one. It's what logs a
|
|
principal in to the store, so you place a hive's or service's certificate by
|
|
hand. The nix option for it takes a path to the certificate, never its bytes,
|
|
because a literal in a nix expression lands in the world-readable nix store.
|
|
A hive hands each agent its certificate; the agent pulls everything else
|
|
itself.
|
|
|
|
**Each credential has a minter, a reader and a renewal story.** Who mints it, who
|
|
reads it, and whether it renews without an operator are what the
|
|
[reference table](../../4-implementation/swarm/credentials-reference.md) gives for every credential, with the
|
|
operating notes that go with them.
|
|
|
|
## Where a secret lives
|
|
|
|
A swarm generates its credentials in three different places and reads them
|
|
in a fourth, so "where does this file go" has a different answer per deployment.
|
|
This page is that answer, one row per secret.
|
|
|
|
Two rules run through all of it.
|
|
|
|
**Private key material and access tokens are paths, never values.** Every option
|
|
carrying one takes a file path (`*File`), because a literal written into a nix
|
|
expression ends up in the nix store — world-readable and permanent. No
|
|
option in this tree accepts one inline, and adding one would be a leak rather
|
|
than a convenience.
|
|
|
|
The rule is about what must stay secret, not about credentials generally.
|
|
**Public material is a value**: a certificate, or a public nkey like
|
|
the queue callout's user key, reaches every client that connects,
|
|
so the store is a perfectly good place for it.
|
|
|
|
**The generator and the reader typically live in different containers.** They share
|
|
the host's network namespace, which makes them feel co-located, but their
|
|
filesystem roots are separate. That's why delivery is a **host-side copy rather
|
|
than a bind mount**: `nixos-container` refuses to start when a bind source is
|
|
missing, and a secret minted on another container's first boot doesn't exist
|
|
yet. Binding it would make one container wait on a file that waits on a
|
|
container that starts after it.
|
|
|
|
### Topologies, by who places secrets
|
|
|
|
Read every row of the [reference tables](../../4-implementation/swarm/credentials-reference.md#where-each-secret-lives-on-disk) against one of these. This is a different cut
|
|
from the [deployment shapes](services.md#deployment-shapes) --- those
|
|
say _where services run_, these say _who is responsible for a secret
|
|
file being there_ --- the two lists don't line up one-to-one, and
|
|
neither is a renaming of the other.
|
|
|
|
| topology | what it means | who places secrets |
|
|
| ------------------ | ---------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
| **all-local** | one host runs the swarm's shared services and its own hive | nobody — the reader itself generates each secret, or a host unit copies it |
|
|
| **swarm-managed** | the swarm's services run on a host with `swarmctl` | `swarmctl` writes what it owns; the rest is still generated in place |
|
|
| **hive elsewhere** | a hive that federates with a swarm it doesn't host | the operator provides the file and names it in config |
|
|
|
|
## Swarm-level and hive-level secrets
|
|
|
|
Swarm-level secrets belong to a swarm service (authelia's keys and client
|
|
secrets, the collectors' and Grafana's OIDC secrets, the forge and queue
|
|
credentials); hive-level ones belong to a hive and its agents. Each has a unit
|
|
that mints or copies it and a reader that holds it only in memory. Which unit,
|
|
where the value lands and what a missing identity costs, secret by secret:
|
|
[how each credential reaches its reader](../../4-implementation/swarm/credential-delivery.md).
|
|
|
|
## The browser UI
|
|
|
|
OpenBao's built-in web UI is at `https://bao-ui.<swarm domain>/`
|
|
(`swarm.bao.ui.domain`), for members of authelia's `admins` group only. Log in
|
|
with the **OIDC** tab, or with a bao token under **Other**. ⚠️ The UI's
|
|
listener asks for **no client certificate**, because a browser has none to
|
|
present, so on this one door a bao token is the whole credential: anything on
|
|
the store's host that can dial loopback, and any `admins` session through the
|
|
vhost, needs only a token to use the API. Unseal from the host's `bao` CLI;
|
|
the UI's unseal form gets a 403. The proxy:
|
|
[the secret store browser UI](../../4-implementation/swarm/credential-delivery.md#the-secret-store-browser-ui).
|
|
|
|
## Adding a secret
|
|
|
|
State three things, in the row you add to the [reference](../../4-implementation/swarm/credentials-reference.md#where-each-secret-lives-on-disk): **who mints it**, **which
|
|
container reads it**, and **what happens when they differ**. If they differ, it
|
|
needs a delivery unit, and the unit copies — it doesn't bind.
|