matrix: remove the registration token
Nothing reads it any more: hive-c0re creates accounts as the hive's appservice, so the mint, the host file, the bind mount, the `LoadCredential` entry and tuwunel's `registration_token_file` all go. ⚠️ `allow_registration` has to go to `false` in the same change, and not as hardening. tuwunel refuses to START when registration is allowed with no token configured — it demands `yes_i_am_very_very_sure_…_open_registration_…` instead — so dropping the token and leaving the flag true is not a lax homeserver, it is one that does not boot. The flag is checked only for requests arriving without an appservice token, so hive-c0re provisions exactly as before and everyone else is refused outright. The swarm secret store keeps its role, repointed at the credential that replaced the token (`swarm/hives/<hive>/matrix/appservice-token`). Its unit now also re-runs hive-matrix's own registration renderer after writing the file: the token is half an agreement, and a registration still naming the previous value authenticates nobody. The renderer is shared through an internal option rather than copied, so the registration's shape has one home. Both spellings of `registrationTokenFile` become `mkRemovedOptionModule` with a message naming what replaced them. A hive that never set the option — the default — is unaffected; one that pinned it fails to evaluate with instructions instead of a silent no-op. An upgraded hive needs no intervention: the activation script has both halves in place before the homeserver restarts, existing agents keep the tokens their devices already hold, and the old token file is left on disk read by nothing. docs/integrations/matrix.md spells the path out. Refs #4402
This commit is contained in:
parent
43cd8607ba
commit
7ee7080b21
11 changed files with 410 additions and 242 deletions
|
|
@ -116,24 +116,45 @@ let
|
|||
];
|
||||
};
|
||||
|
||||
# The homeserver's turn to split. All eight host-side options are set through
|
||||
# their pre-rename paths — including `gui.enable`, whose value is deliberately
|
||||
# the opposite of its default so the definition has to actually land, and both
|
||||
# packages, stubbed to a derivation neither option defaults to for the same
|
||||
# reason. All eight so that dropping any single shim entry fails the eval, not
|
||||
# just the ones the assertions read.
|
||||
# The homeserver's turn to split. All seven remaining host-side options are
|
||||
# set through their pre-rename paths — including `gui.enable`, whose value is
|
||||
# deliberately the opposite of its default so the definition has to actually
|
||||
# land, and both packages, stubbed to a derivation neither option defaults to
|
||||
# for the same reason. All seven so that dropping any single shim entry fails
|
||||
# the eval, not just the ones the assertions read.
|
||||
#
|
||||
# `registrationTokenFile` used to be the eighth: both spellings of it are now
|
||||
# `mkRemovedOptionModule`, exercised by `matrixRemovedToken` below instead —
|
||||
# setting it here would make every case that reads this fixture fail on that
|
||||
# one removed option.
|
||||
matrixOldPath = hive {
|
||||
deploy.matrix.enable = true;
|
||||
swarm.matrix.openFirewall = true;
|
||||
swarm.matrix.trustedServers = [ "matrix.example.invalid" ];
|
||||
swarm.matrix.maxRequestSize = 31457280;
|
||||
swarm.matrix.registrationTokenFile = "/etc/matrix/register.token";
|
||||
swarm.matrix.gui.enable = false;
|
||||
swarm.matrix.package = pkgs.emptyDirectory;
|
||||
swarm.matrix.gui.package = pkgs.emptyDirectory;
|
||||
swarm.matrix.sso.clientSecretFile = "/etc/matrix/oidc.secret";
|
||||
};
|
||||
|
||||
# Moving the appservice token, which desyncs hive-c0re's independently
|
||||
# derived path from the homeserver's. Its own fixture because the assertion
|
||||
# it trips would otherwise fire for every case reading `matrixOldPath`.
|
||||
matrixMovedAppserviceToken = hive {
|
||||
deploy.matrix.enable = true;
|
||||
deploy.matrix.appserviceTokenFile = "/etc/matrix/as.token";
|
||||
swarm.matrix.sso.clientSecretFile = "/etc/matrix/oidc.secret";
|
||||
};
|
||||
|
||||
# A config still naming the removed registration token, through the
|
||||
# pre-rename spelling — the one an old deployment is most likely to carry.
|
||||
matrixRemovedToken = hive {
|
||||
deploy.matrix.enable = true;
|
||||
swarm.matrix.registrationTokenFile = "/etc/matrix/register.token";
|
||||
swarm.matrix.sso.clientSecretFile = "/etc/matrix/oidc.secret";
|
||||
};
|
||||
|
||||
# The queue's callout identity, fourth split slice. `autoGenerateCallout` is
|
||||
# left FALSE on purpose: that is what makes the seed paths the thing deciding
|
||||
# `responderConfigured`, so the assertion below is about the seeds rather
|
||||
|
|
@ -840,33 +861,41 @@ let
|
|||
}
|
||||
{
|
||||
# Third split, and the one whose readers were hardest to see: the
|
||||
# registration token is read only through a `let` alias in another
|
||||
# appservice token is read only through a `let` alias in another
|
||||
# module, so no full path names it anywhere. Both arms read a rendered
|
||||
# effect — the host firewall and the container's bind-mount table — so a
|
||||
# rename that resolves but stops reaching the module still fails.
|
||||
name = "a config written against the pre-rename matrix paths still opens the port and mounts the token";
|
||||
name = "a config written against the pre-rename matrix paths still opens the port and mounts the appservice registration";
|
||||
ok =
|
||||
let
|
||||
ports = matrixOldPath.networking.firewall.allowedTCPPorts;
|
||||
httpPort = matrixOldPath.services.hyperhive.swarm.matrix.httpPort;
|
||||
in
|
||||
builtins.elem httpPort ports
|
||||
&& matrixOldPath.containers.hive-matrix.bindMounts ? "/etc/matrix/register.token";
|
||||
&& matrixOldPath.containers.hive-matrix.bindMounts ? "/var/lib/hyperhive/matrix-appservice";
|
||||
}
|
||||
{
|
||||
# registrationTokenFile lost its override capability entirely
|
||||
# (bao-delivered secrets don't need one — glue-matrix-bao-token.nix
|
||||
# already writes into the fixed path instead of moving it), unlike its
|
||||
# five siblings in the same rename. `matrixOldPath` above proves the
|
||||
# override still *resolves* (mkDefault, not a crash) — this proves it
|
||||
# also gets *rejected*, by a named assertion rather than nixpkgs' generic
|
||||
# conflicting-definition text. Reads `.assertions` directly (cheap: a
|
||||
# list of `{assertion; message;}`, not `system.build.toplevel`) rather
|
||||
# than forcing a real build just to observe a boolean.
|
||||
name = "overriding registrationTokenFile (even via the pre-rename shim) trips a named assertion, not a silent desync";
|
||||
# appserviceTokenFile has no override capability (a store-delivered
|
||||
# secret doesn't need one — glue-matrix-bao-token.nix writes into the
|
||||
# fixed path instead of moving it), unlike its siblings in the same
|
||||
# rename. The fixture proves the override still *resolves* (mkDefault,
|
||||
# not a crash); this proves it also gets *rejected*, by a named
|
||||
# assertion rather than nixpkgs' generic conflicting-definition text.
|
||||
# Reads `.assertions` directly (cheap: a list of `{assertion;
|
||||
# message;}`, not `system.build.toplevel`) rather than forcing a real
|
||||
# build just to observe a boolean.
|
||||
name = "moving appserviceTokenFile trips a named assertion, not a silent desync";
|
||||
ok = lib.any (
|
||||
a: !a.assertion && lib.hasInfix "registrationTokenFile" a.message
|
||||
) matrixOldPath.assertions;
|
||||
a: !a.assertion && lib.hasInfix "appserviceTokenFile" a.message
|
||||
) matrixMovedAppserviceToken.assertions;
|
||||
}
|
||||
{
|
||||
# The removal has to SAY so. A config carrying the old option through
|
||||
# an upgrade is the common case, and nixpkgs' bare "option does not
|
||||
# exist" would name neither what replaced the token nor where to read
|
||||
# about it.
|
||||
name = "a config still setting registrationTokenFile is refused with a message naming the appservice";
|
||||
ok = lib.any (a: !a.assertion && lib.hasInfix "appservice" a.message) matrixRemovedToken.assertions;
|
||||
}
|
||||
{
|
||||
# Reads the DELIVERY UNIT, not the options: `responderConfigured` gates
|
||||
|
|
@ -1518,7 +1547,7 @@ let
|
|||
s = baoWithMatrix.systemd.services.swarm-bao-matrix-token.script;
|
||||
in
|
||||
lib.hasInfix "secret/swarm/hives/" s
|
||||
&& lib.hasInfix "/matrix/registration-token" s
|
||||
&& lib.hasInfix "/matrix/appservice-token" s
|
||||
# The shape it used to have: `matrix` where a principal kind belongs,
|
||||
# which no grant covers.
|
||||
&& !(lib.hasInfix "secret/swarm/matrix/" s);
|
||||
|
|
@ -2471,7 +2500,7 @@ let
|
|||
# The store's first reader. Its unit belongs to the pairing, not to
|
||||
# either service: matrix must not learn the store exists, and the store
|
||||
# must not know who reads it.
|
||||
name = "a store deployed beside the homeserver fetches its registration token";
|
||||
name = "a store deployed beside the homeserver fetches its appservice token";
|
||||
ok = baoWithMatrix.systemd.services ? swarm-bao-matrix-token;
|
||||
}
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue