docs(3149): what the SSO secrets are and where each one lives
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.
This commit is contained in:
parent
cdb3c612b2
commit
b4a3eb75b0
3 changed files with 101 additions and 6 deletions
|
|
@ -88,6 +88,12 @@ One authelia, one matrix, one forge per swarm — which host runs them,
|
|||
and what a hive that runs none of them configures instead:
|
||||
[`services.md`](services.md).
|
||||
|
||||
## Single sign-on
|
||||
|
||||
Which secrets the SSO provider generates, which one has a reader in
|
||||
another container, and the three ways that one gets delivered:
|
||||
[`sso.md`](sso.md).
|
||||
|
||||
## The swarm's hive directory
|
||||
|
||||
```nix
|
||||
|
|
|
|||
92
docs/swarm/sso.md
Normal file
92
docs/swarm/sso.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
# 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`.
|
||||
|
|
@ -18,12 +18,9 @@
|
|||
# for LDAP: what makes a directory necessary is the size of the subject
|
||||
# set, and this deployment's is bounded by one swarm.
|
||||
#
|
||||
# Two roles, and only the first is unconditional: this is a **session**
|
||||
# provider (`auth_request`) always, and an **OIDC** provider when
|
||||
# `oidc.clients` is non-empty. The second is derived from the client list
|
||||
# instead of carrying its own flag, because authelia refuses to start
|
||||
# with a provider that has no clients — a separate `enable` would be a
|
||||
# second fact that can disagree with the first.
|
||||
# Two roles: a **session** provider always, an **OIDC** provider when
|
||||
# `oidc.clients` is non-empty (derived, not flagged — authelia will not
|
||||
# start with a clientless provider). Secrets map: docs/swarm/sso.md.
|
||||
#
|
||||
# Per-service integration — putting authelia's `auth_request` in front
|
||||
# of the gateway's existing `auth_basic` locations — is deliberately NOT
|
||||
|
|
|
|||
Loading…
Reference in a new issue