The question this answers is "what do I have to configure, and where" — so the table of secrets is the deliverable and the prose is scaffolding around it. The organising idea worth keeping: a secret belongs in-container when nothing outside that container reads it. Every one of authelia's own secrets passes that test; the client secret's plaintext fails it, which is what makes delivery a problem at all rather than a detail.
92 lines
4 KiB
Markdown
92 lines
4 KiB
Markdown
# Swarm SSO
|
|
|
|
The swarm runs one authelia, and it is two things at once: the **session
|
|
provider** every protected vhost checks (`auth_request`), and — once any
|
|
client is declared — an **OIDC provider** issuing tokens to relying
|
|
parties like the forge.
|
|
|
|
The second role is derived rather than switched:
|
|
`services.hyperhive.swarm.authelia.oidc.clients` being non-empty turns it
|
|
on. authelia refuses to start with a provider that has no clients, so a
|
|
separate `enable` would be a second fact free to disagree with the first.
|
|
|
|
## What secrets exist, and where each one lives
|
|
|
|
| secret | generated by | rests in | read by |
|
|
|---|---|---|---|
|
|
| `jwt.key`, `session.key`, `storage-encryption.key` | authelia's first-boot unit | `/var/lib/authelia-swarm/` | authelia |
|
|
| `oidc-hmac.key` | same unit | same directory | authelia |
|
|
| `oidc-issuer.key` (RSA) | same unit | same directory | authelia signs with it; clients verify the **public** half at `/jwks.json` |
|
|
| `oidc-clients/<id>.digest` | same unit, via `authelia crypto hash generate` | same directory, merged in through `settingsFiles` | authelia |
|
|
| `oidc-clients/<id>.secret` | the same mint — this is its plaintext half | same directory | **the relying party, in another container** |
|
|
|
|
Everything above the last row is generated in-container because nothing
|
|
outside that container ever reads it. That is the test worth applying to
|
|
any secret added here. The last row fails it, and that is the entire
|
|
reason a delivery step exists.
|
|
|
|
**None of it is ever written into a nix expression.** authelia's
|
|
`settings` are rendered into the nix store, which is world-readable and
|
|
permanent, so the client digest reaches authelia through `settingsFiles`
|
|
(merged at runtime) and every other secret through a `*File` option
|
|
carrying a path rather than a value.
|
|
|
|
## Getting the plaintext to the relying party
|
|
|
|
Three cases, and they are genuinely different mechanisms rather than one
|
|
mechanism with flags.
|
|
|
|
### 1. All-local — one host runs both
|
|
|
|
Nothing to configure beyond `swarm.forge.sso.enable = true`. A host-side
|
|
unit waits for authelia's first boot to mint the secret and copies it
|
|
into the forge container, and the forge module contributes its own client
|
|
entry — callback URL included — to authelia's client list.
|
|
|
|
The callback is built from the same source name the registration uses, so
|
|
the redirect URI authelia is told to allow and the one forgejo actually
|
|
sends cannot drift apart. A mismatch there is a rejected login with no
|
|
error text worth reading.
|
|
|
|
⚠️ The delivery is a copy, not a `bindMounts` entry, and deliberately so:
|
|
nixos-container refuses to start a container whose bind source is
|
|
missing, and this secret does not exist until authelia's first boot has
|
|
run. Binding it would make the forge wait on a file that waits on a
|
|
container that starts after it — on a fresh hive, a permanent stall
|
|
presenting as "the forge is broken", several layers from its cause.
|
|
|
|
### 2. Swarm-managed services
|
|
|
|
The controller side owns provisioning: `swarmctl` writes both halves, the
|
|
same way it already owns authelia's user store (`users.json` canonical,
|
|
`users.yml` a rendered artifact).
|
|
|
|
### 3. A hive elsewhere
|
|
|
|
No shared host, so no automatic path. The operator provides the file and
|
|
names it:
|
|
|
|
```nix
|
|
services.hyperhive.swarm = {
|
|
authelia.url = "https://auth.example.com";
|
|
forge.sso = {
|
|
enable = true;
|
|
clientSecretFile = "/var/lib/hyperhive/forge-oidc-secret";
|
|
};
|
|
};
|
|
```
|
|
|
|
**Both are asserted at eval.** A hive that boots with SSO
|
|
half-configured shows a login button that always fails — a symptom
|
|
several layers from its cause, and far worse to diagnose than an
|
|
evaluation error.
|
|
|
|
## What this does not do
|
|
|
|
- **It does not disable local login.** The forge keeps its password
|
|
database and gains a second door. An identity provider that can take
|
|
the forge offline when it hiccups is a worse forge than one with two
|
|
ways in.
|
|
- **It does not provision users.** Agents are created and destroyed
|
|
continuously, so the subject set belongs to a program rather than to a
|
|
config file; today that program is `swarmctl`.
|