The mint creates each client's secret in the swarm secret store and writes only the digest to disk. It mints only when the store answers and holds none. A new unit moves the plaintext files an earlier release left into the store and removes each once the store holds the same value. The bridge reads its secret from the store into /run before each start. The secret publisher unit and its path unit are gone.
183 lines
8.2 KiB
Nix
183 lines
8.2 KiB
Nix
# The swarm's matrix homeserver as every hive sees it: the name it mints ids
|
||
# under, the URLs it is reached on, its encryption policy and the
|
||
# OIDC client id it is registered under, identical on every host. What the
|
||
# host running it decides, and the container itself, are in ./hive-matrix.nix.
|
||
{
|
||
lib,
|
||
config,
|
||
...
|
||
}:
|
||
let
|
||
cfg = config.services.hyperhive.swarm.matrix;
|
||
swarmDomain = config.services.hyperhive.swarm.domain;
|
||
in
|
||
{
|
||
imports = [
|
||
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "apiUrl" ] ''
|
||
hive-c0re no longer talks to matrix, so it has no API URL to set: matrix is swarm-level.
|
||
'')
|
||
];
|
||
|
||
options.services.hyperhive.swarm.matrix = {
|
||
serverName = lib.mkOption {
|
||
type = lib.types.nullOr lib.types.str;
|
||
default = null;
|
||
example = "chat.example.org";
|
||
description = ''
|
||
Matrix `server_name` — the host part of every user ID
|
||
(`@argus:<server_name>`) and room ID minted on this
|
||
homeserver. CRITICAL: must be stable from day one because
|
||
it's embedded irrevocably in the identifiers.
|
||
|
||
Defaults to `services.hyperhive.swarm.domain` (the bare swarm
|
||
domain), because **a swarm runs one homeserver** — tying its
|
||
identity to a single hive's domain would make relocating the
|
||
container between hives look like a different homeserver.
|
||
Combined with the `.well-known/matrix/{client,server}` routes
|
||
the controller's host serves at that domain, clients auto-discover the
|
||
actual matrix endpoint without needing a subdomain. Override
|
||
here only if you need a different server_name shape (e.g.
|
||
`chat.example.org` for a bespoke hostname).
|
||
|
||
**Changing this cannot be undone by rebuilding.** The default
|
||
is the swarm domain, so a homeserver that minted ids under
|
||
another value (`matrix.''${services.hyperhive.domain}` or the
|
||
bare hive domain) must pin that value, e.g.
|
||
|
||
```nix
|
||
services.hyperhive.swarm.matrix.serverName =
|
||
config.services.hyperhive.domain; # or "matrix.''${…domain}"
|
||
```
|
||
|
||
before rebuilding. Adopting a new `server_name` does not rename
|
||
the old users and rooms — it strands them, because their ids
|
||
still name a homeserver that no longer answers.
|
||
'';
|
||
};
|
||
|
||
gatewayHost = lib.mkOption {
|
||
type = lib.types.nullOr lib.types.str;
|
||
# `chat.` under the SWARM domain — both halves change: a swarm runs
|
||
# one homeserver, and the label follows the service rather than the
|
||
# protocol.
|
||
#
|
||
# Total on a null swarm domain so the required-domain assertion in
|
||
# hive-network.nix is the thing that fires; see the comment there.
|
||
default = if swarmDomain == null then "chat.invalid" else "chat.${swarmDomain}";
|
||
defaultText = lib.literalExpression ''"chat.''${services.hyperhive.swarm.domain}"'';
|
||
example = "matrix.example.com";
|
||
description = ''
|
||
Public hostname for the matrix homeserver behind the gateway.
|
||
Defaults to `chat.''${services.hyperhive.swarm.domain}` — the
|
||
swarm's domain, because a swarm runs **one** homeserver. Set to
|
||
`null` to skip the gateway vhost, which leaves nothing able to
|
||
reach the homeserver. See `docs/networking/gateway.md` for the vhost map + matrix
|
||
discovery flow, and the federation port-8448 caveat at the
|
||
bottom of that doc.
|
||
|
||
⚠️ **`gatewayHost` and `serverName` are different things, and
|
||
they carry very different costs.** `gatewayHost` is the API
|
||
listener hostname (where nginx proxies `/_matrix/*`) and is
|
||
free to change: it is a routing detail clients rediscover
|
||
through `.well-known`. `serverName` is the matrix-identifier
|
||
domain embedded **irrevocably** in every user and room id —
|
||
adopting a new one is a different homeserver, not a rename.
|
||
Both defaults sit under the swarm domain, but only this
|
||
one is safe to change on a running deployment.
|
||
|
||
To use the per-hive name `matrix.''${services.hyperhive.domain}`
|
||
instead, pin it here.
|
||
'';
|
||
};
|
||
|
||
adminSync.ignoreUsers = lib.mkOption {
|
||
type = lib.types.listOf (lib.types.strMatching "@[^:[:space:]]+:[^[:space:]]+");
|
||
default = [ ];
|
||
example = [ "@mara:example.org" ];
|
||
description = ''
|
||
Full matrix user ids `swarm-controller`'s operator admin sync never
|
||
revokes homeserver admin from. The sync revokes admin only from
|
||
accounts it made admin itself, once their owner leaves authelia's
|
||
`admins` group; an account listed here keeps admin regardless.
|
||
'';
|
||
};
|
||
|
||
# This homeserver always delegates login to the swarm's authelia, as
|
||
# an OIDC relying party — matrix SSO (`m.login.sso`), offered
|
||
# alongside password login. No toggle: a homeserver in a swarm is a
|
||
# client of that swarm's identity provider.
|
||
#
|
||
# ⚠️ Not to be confused with tuwunel's `oidc_*` settings, which point
|
||
# the other way: those make this homeserver an *authorization server*
|
||
# for matrix clients. This family makes it a *client* of an external
|
||
# identity provider. The two share the protocol's name and answer
|
||
# opposite questions.
|
||
#
|
||
# This **adds** a way in. Password login keeps working: an identity
|
||
# provider that can take the homeserver offline when it hiccups is a
|
||
# worse homeserver than one with two ways in — which is also what
|
||
# makes always-on safe. Making authelia the *only* path is a
|
||
# separate, reversible switch (tuwunel's `login_with_password`),
|
||
# deliberately not folded in here.
|
||
#
|
||
# ⚠️ Matrix SSO lives **inside** the homeserver, never behind a
|
||
# forward-auth proxy: the client-server API is spoken by non-browser
|
||
# clients holding matrix access tokens — every agent's own
|
||
# `hive-matrix-daemon` — plus federation, and a proxy in front of
|
||
# `/_matrix/` breaks all of it.
|
||
sso = {
|
||
clientId = lib.mkOption {
|
||
type = lib.types.str;
|
||
default = "tuwunel";
|
||
description = ''
|
||
OAuth2 client id this homeserver identifies itself with. The host
|
||
running authelia registers it, whichever host runs the homeserver.
|
||
|
||
The homeserver's host reads the secret it pairs with out of the
|
||
swarm secret store, into
|
||
`services.hyperhive.deploy.matrix.sso.clientSecretFile`.
|
||
'';
|
||
};
|
||
|
||
redirectUri = lib.mkOption {
|
||
type = lib.types.nullOr lib.types.str;
|
||
readOnly = true;
|
||
default =
|
||
if cfg.gatewayHost == null then
|
||
null
|
||
else
|
||
"https://${cfg.gatewayHost}/_matrix/client/unstable/login/sso/callback/${cfg.sso.clientId}";
|
||
defaultText = lib.literalExpression ''"https://''${services.hyperhive.swarm.matrix.gatewayHost}/_matrix/client/unstable/login/sso/callback/''${services.hyperhive.swarm.matrix.sso.clientId}"'';
|
||
description = ''
|
||
SSO callback authelia sends the browser back to, and the URI it
|
||
matches **exactly**. Format-locked by tuwunel: the callback host
|
||
must point directly at the homeserver, and the path is fixed.
|
||
|
||
Read-only: the homeserver's config and its authelia client entry
|
||
both read it from here. `null` when `gatewayHost` is, because the
|
||
homeserver then has no name a browser can reach.
|
||
'';
|
||
};
|
||
};
|
||
|
||
otel.clientId = lib.mkOption {
|
||
type = lib.types.str;
|
||
readOnly = true;
|
||
# Not `hive-matrix-collector`: `hive-<name>` is a hive's own client id,
|
||
# and `matrix-collector` is a name a hive may take.
|
||
default = "swarm-matrix-collector";
|
||
description = ''
|
||
OAuth2 client id the journal forwarder inside the homeserver's
|
||
container authenticates as, and the audience it asks its token for.
|
||
Its own, not {option}`services.hyperhive.swarm.matrix.sso.clientId`,
|
||
which is the browser login's.
|
||
|
||
Read-only because three hosts have to agree on it: authelia
|
||
registers the client (`glue-matrix-otel-oidc-client.nix`), the
|
||
swarm collector admits it on the container's receiver
|
||
(`swarm.otel.serviceProducers`), and authelia's mint stores
|
||
its secret in the store.
|
||
'';
|
||
};
|
||
};
|
||
}
|