hive-matrix: reject an override of registrationTokenFile instead of desyncing hive-c0re
This commit is contained in:
parent
5c658e9bcf
commit
9d55cba786
3 changed files with 76 additions and 3 deletions
|
|
@ -255,6 +255,9 @@ pub const FORGE_CORE_TOKEN: &str = "/var/lib/hyperhive/forge-core-token";
|
|||
|
||||
/// `matrix-register-token` — shared matrix registration token.
|
||||
// nix: bind-mounted into the tuwunel/matrix container (hive-matrix.nix) — must match.
|
||||
// Not operator-option-driven: `registrationTokenFile` is `internal` on the nix
|
||||
// side, and an `assertions` entry there rejects any attempt to move it, so this
|
||||
// literal can never diverge from it.
|
||||
#[must_use]
|
||||
pub fn matrix_register_token() -> PathBuf {
|
||||
state_root().join("matrix-register-token")
|
||||
|
|
|
|||
|
|
@ -40,6 +40,12 @@ let
|
|||
# login into an intermittent one.
|
||||
matrixSecretPath = "/var/lib/tuwunel-oidc/${cfg.sso.clientId}.secret";
|
||||
|
||||
# Single source for the registration-token path: the option's `default`,
|
||||
# its `config`-level `mkDefault` self-definition, and the assertion that
|
||||
# rejects a moved path all read this same binding rather than repeating
|
||||
# the string literal. See `registrationTokenFile`'s own comment below.
|
||||
registrationTokenPath = "/var/lib/hyperhive/matrix-register-token";
|
||||
|
||||
# ⚠️ tuwunel does NOT read the path above directly, and this indirection
|
||||
# is not ceremony. Upstream's own words: "under systemd the path must be
|
||||
# visible to the service after sandboxing (ReadWritePaths / ProtectHome),
|
||||
|
|
@ -452,7 +458,8 @@ in
|
|||
|
||||
registrationTokenFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = "/var/lib/hyperhive/matrix-register-token";
|
||||
internal = true;
|
||||
default = registrationTokenPath;
|
||||
description = ''
|
||||
Host path to a file containing the matrix registration token
|
||||
tuwunel reads to authorise new-account creation. The token is
|
||||
|
|
@ -461,8 +468,28 @@ in
|
|||
tuwunel container at the same path. Agents never see this
|
||||
token — hive-c0re uses it to provision per-agent accounts
|
||||
and the agent only receives the resulting `access_token`.
|
||||
Override only when integrating with externally-managed
|
||||
registration tokens.
|
||||
|
||||
Not operator-settable — `hive-c0re`'s Rust side derives this
|
||||
same path independently (`paths::matrix_register_token()`) with
|
||||
nothing wiring an override across, so a moved path used to
|
||||
desync the two silently. An externally-managed token is
|
||||
delivered by writing into *this* fixed path instead of moving it —
|
||||
see `glue-matrix-bao-token.nix`, which fetches from the swarm
|
||||
secret store and overwrites this file in place.
|
||||
|
||||
Enforced by an `assertions` entry below rather than `readOnly`:
|
||||
the `config` block gives this option its own `mkDefault`
|
||||
definition (lowest priority) so any real override — an operator's,
|
||||
or the pre-rename `swarm.matrix.registrationTokenFile` shim's —
|
||||
still resolves cleanly instead of crashing eval with nixpkgs'
|
||||
generic "read-only, set multiple times" message; the assertion
|
||||
then names the actual problem. (`readOnly` was the first attempt —
|
||||
atlas caught that it only rejects a *second* definition,
|
||||
so with nothing else defining the option a lone override sailed
|
||||
through silently, same desync as before with a lock that wasn't
|
||||
locked. Fixed by defining the value here instead of leaving it on
|
||||
`default` alone, but a proper `assertions` message beat re-adding
|
||||
`readOnly` on top once the option had a real definition either way.)
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
@ -507,6 +534,13 @@ in
|
|||
};
|
||||
|
||||
config = lib.mkIf deployCfg.matrix.enable {
|
||||
# The option's own value, defined explicitly rather than left on its
|
||||
# bare `default` — `mkDefault` so a real override (an operator's, or
|
||||
# the pre-rename shim's) still resolves cleanly rather than crashing
|
||||
# eval; the `assertions` entry below is what actually rejects it, with
|
||||
# a message naming the reason. See that option's own comment above.
|
||||
services.hyperhive.deploy.matrix.registrationTokenFile = lib.mkDefault registrationTokenPath;
|
||||
|
||||
# Matrix's own gateway surface: the sub-domain vhost, the name the
|
||||
# hive resolver answers for, and the Accept-header map that vhost's
|
||||
# SPA fallback reads. All three are matrix knowledge and none of
|
||||
|
|
@ -676,6 +710,27 @@ in
|
|||
httpPort and has no such name.
|
||||
'';
|
||||
}
|
||||
{
|
||||
# mkDefault above lets an override resolve instead of crashing
|
||||
# eval — this is the actual rejection, with a message that names
|
||||
# the real fix instead of nixpkgs' generic conflicting-definition
|
||||
# text. Covers both the current path and the pre-rename
|
||||
# `swarm.matrix.registrationTokenFile` shim in one check, since
|
||||
# both land on the same merged option.
|
||||
assertion = deployCfg.matrix.registrationTokenFile == registrationTokenPath;
|
||||
message = ''
|
||||
services.hyperhive.deploy.matrix.registrationTokenFile is fixed at
|
||||
${registrationTokenPath} and cannot be moved — hive-c0re's Rust
|
||||
side derives this same path independently and has no way to learn
|
||||
an override, so moving it desyncs the two silently instead of
|
||||
loudly.
|
||||
|
||||
Integrating an externally-managed registration token? Deliver it
|
||||
by writing into ${registrationTokenPath} instead of pointing this
|
||||
option elsewhere — see glue-matrix-bao-token.nix, which does
|
||||
exactly that from the swarm secret store.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
# One declaration, two readers. The homeserver knows its own callback
|
||||
|
|
|
|||
|
|
@ -296,6 +296,21 @@ let
|
|||
builtins.elem httpPort ports
|
||||
&& matrixOldPath.containers.hive-matrix.bindMounts ? "/etc/matrix/register.token";
|
||||
}
|
||||
{
|
||||
# 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";
|
||||
ok = lib.any (
|
||||
a: !a.assertion && lib.hasInfix "registrationTokenFile" a.message
|
||||
) matrixOldPath.assertions;
|
||||
}
|
||||
{
|
||||
# Reads the DELIVERY UNIT, not the options: `responderConfigured` gates
|
||||
# whether it exists at all, and the seed path is interpolated into its
|
||||
|
|
|
|||
Loading…
Reference in a new issue