Account minting, knowledge sync, the archetype refresh, the matrix appservice flow and the SSO secret delivery move to pages under 4-implementation/swarm. Each handbook page keeps what an operator does or sees, with a pointer.
8.8 KiB
Swarm SSO
The swarm runs one authelia, and it's two things at once: the session
provider every protected vhost checks (auth_request), and — once an
operator declares any client — an OIDC provider issuing tokens to
relying parties: the forge and the matrix homeserver.
The second role derives rather than switches:
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.
Getting in the first time
authelia binds no TCP port: its listeners are unix sockets only the gateway can reach. The gateway on the host running it
publishes it as auth.<swarm.domain> — vhost, dnsmasq record and TLS
name all follow deploy.authelia, so there is nothing to turn on
separately. (Details, including why a client hive must not declare that
vhost: 4-implementation/networking/gateway.md.)
Nobody can log in until the first user exists. authelia exits at startup on an empty user set:
error reading the authentication database: could not validate the schema:
users: non zero value required
While the set is empty this module writes one placeholder subject,
swarm.placeholder, that authelia accepts and that can't log in: it's
disabled: true, and its password digest has an all-zero key that no
known password hashes to. authelia starts and serves its login page, and
refuses every login. The placeholder stays in users.yml once real
users exist, shows up in swarmctl user list, and swarmctl user add
refuses its name.
⚠️ The step below is required to finish the install, not an optional first-login convenience.
Add the first subject on the host running authelia:
# swarmctl user add mara --display-name Mara --email mara@example.com --group admins
added mara to /var/lib/authelia-swarm/users.yml
password: <generated>
this password is stored nowhere — record it now
swarmctl generates, hashes, and prints the password once, keeping only
the hash. It reads and writes authelia's users.yml directly — it's
the one user store, shared with swarm-authelia-bridge, which creates
agent identities in the same file. No restart: authelia watches it. Full
reference: 2-handbook/swarm/swarmctl-cli.md.
You can edit users.yml by hand, and swarmctl will read what you
wrote. ⚠️ It rewrites the whole file on every change, so comments and
formatting don't survive; values and unrecognised keys do.
This step stays manual on purpose. Bootstrapping an identity provider non-interactively means a secret arriving from somewhere — a file, an env var, a nix expression — and every one of those is worse than an operator typing one command once.
Changing a subject afterwards
user add only ever adds: on a name that already exists it refuses,
rather than resurfacing as a second account or a silent overwrite.
Editing an existing subject is user update, and the flags compose, so
one call can change multiple things:
# swarmctl user update mara --add-group admins --email mara@example.com
added to group "admins"
email: unset -> "mara@example.com"
mara is now in groups: admins
Two behaviours worth knowing before you rely on them:
--remove-groupfails if the user isn't in that group. Every other flag is idempotent — setting what's already set is fine, so a "make these four things true" call doesn't break when one of them already was. Revocation is the exception on purpose: a typo'd group name that reported success would leave an account holding access you believe you took away, and that's the one outcome nobody re-checks.swarmctlprints the resulting group list because group names have no registry anywhere. A misspelled--add-groupcreates a real group that no access-control rule mentions, so the user gains nothing and no error is possible — reading the line back is the only check there is.
Passwords are deliberately out of scope here: regenerating a credential
is a different intent from editing an attribute, and folding them means
an attribute edit can invalidate a login by accident. Resetting one is a
separate verb, user reset-password, which generates and hashes a new
password the same way user add does and refuses on an unknown user:
# swarmctl user reset-password mara
reset password for mara in /var/lib/authelia-swarm/users.yml
password: <generated>
this password is stored nowhere — record it now
Authelia's file store has no SMTP notifier, so this is the only way a human account gets a new password once an operator forgets the old one.
Logging in to the forge and matrix
- A person's first forge login creates their forge account, named after
their authelia username, provided the subject has an email. It starts as an
ordinary user;
swarmctl forge make-admin <name>makes it a site admin. A name that already has a local forge account isn't handed over: forgejo asks for that account's own password first. Agents,coreandswarm-controllerall have local accounts. - A member of
adminsbecomes a matrix homeserver admin once their first matrix login has created the account.swarm-controllerpromotes them within five minutes, and never demotes. Seematrix.md.
You place no client secret by hand. Authelia mints each one at its first boot
and the host-side units deliver it to the forge and the homeserver, retrying
until it lands, so there is no start order to get right. A forge on another host
than the store needs the forge publisher's store leaf, in
deploy.bao.forgePublisherClientCertFile and forgePublisherClientKeyFile.
Without it the forge has no login source, and the unit fails naming both options.
Where each secret lives, the delivery mechanics and how forge and matrix
differ: sso.md.
Machine clients
Everything above is a person in a browser. A swarm also has callers that hold
no session and follow no redirect: the telemetry collector, the queue's
auth-callout responder, and each hive. A hive holds a single OIDC client,
<hiveClientPrefix><hive>, and mints a different token per service from it.
The gateway answers machine callers with the real status rather than the
friendly "SSO is unavailable" page, so a down provider denies access instead of
granting it. The reasoning and how to check it after a routing change:
sso.md.
Operator CLI login
swarmctl login logs an operator in as their own account, from any
machine, through the public swarmctl client authelia always registers: no
secret, authorization code with PKCE, a loopback redirect, and a refresh
token. Its access token carries authelia.bearer.authz with the audience
https://<swarm>/api/, so the gateway admits it at the controller's API and
nowhere else, under the same access rule as a browser session there.
Revocation is per account: once authelia stops accepting the account, the
gateway refuses its token too. See swarmctl/README.md.
What this doesn't do
- It doesn't disable local login — except on Grafana. Forgejo and
tuwunel keep their password databases and gain a second door: an identity
provider that can take a service offline when it hiccups is worse than one
with two ways in, and making authelia the only path there is a separate,
reversible switch (tuwunel's
login_with_password, forgejo's own setting). ⚠️ Grafana is the exception, and not a reversible one. Its module setsauth.disable_login_formunconditionally: its vhost is on the public gateway, so a password box there would be a way in whatever the deployment. The form is all this hides. Grafana's API still takes HTTP basic auth for the built-inadminaccount, whose password is a random one minted into the swarm secret store, not the upstreamadmin. A dead provider locks out the SSO accounts but not that one: an operator reads the password fromswarm/services/<grafana client id>/admin/passwordin the store (or, as root on the Grafana host, from/run/swarm-grafana-secrets/admin-password) and calls the API with it. Seeservices.mdandcredentials.md. SSO logins are the everyday way in, which is whyswarm.grafana.oidc.roledefaults toAdmin. The three secrets Grafana reads live on the host's tmpfs, so after a reboot Grafana stays down (no login, no dashboards) until the secret store answers; the delivery units retry and start it with no manual step. - It doesn't provision users.
swarmctlcreates and destroys agents continuously today, so the subject set belongs to a program rather than to a config file.