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
|
|
@ -26,18 +26,25 @@ pub fn account_path(agent: &str, account: &str) -> Result<String, Error> {
|
|||
Ok(format!("{prefix}/matrix/{account}"))
|
||||
}
|
||||
|
||||
/// The path holding `hive`'s matrix registration token.
|
||||
/// The path holding `hive`'s matrix appservice token (`as_token`).
|
||||
///
|
||||
/// Keyed per **hive**, not per agent, like [`crate::queue::agent_client_path`]
|
||||
/// and unlike [`account_path`] above: one homeserver admits one hive's
|
||||
/// accounts, so the token that creates them is the hive's.
|
||||
/// accounts, so the identity that creates them is the hive's.
|
||||
///
|
||||
/// ⚠️ Renamed from `registration-token` along with what it holds: the
|
||||
/// homeserver no longer accepts a shared registration secret at all, so a
|
||||
/// value still stored under the old path would be read by nothing. A hive
|
||||
/// whose store has only the old path falls back to its locally minted
|
||||
/// token — see `glue-matrix-bao-token.nix` — so the rename degrades rather
|
||||
/// than breaks, but the store needs a fresh `put` to take effect again.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::PathSegment`] when `hive` contains anything but `[A-Za-z0-9_-]`,
|
||||
/// which is what keeps one hive's name from addressing another hive's secret.
|
||||
pub fn registration_token_path(hive: &str) -> Result<String, Error> {
|
||||
pub fn appservice_token_path(hive: &str) -> Result<String, Error> {
|
||||
let prefix = principal_prefix(Kind::Hive, hive)?;
|
||||
Ok(format!("{prefix}/matrix/registration-token"))
|
||||
Ok(format!("{prefix}/matrix/appservice-token"))
|
||||
}
|
||||
|
||||
/// What an account's path holds: the token, plus the homeserver it belongs to.
|
||||
|
|
@ -80,28 +87,29 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn the_registration_token_lands_under_the_hive_prefix_the_grant_covers() {
|
||||
fn the_appservice_token_lands_under_the_hive_prefix_the_grant_covers() {
|
||||
// Spelled out for the same reason as above, and with a second job here:
|
||||
// the read policy grants `secret/data/swarm/hives/<hive>/*`, so this
|
||||
// string is what makes the path reachable at all.
|
||||
assert_eq!(
|
||||
registration_token_path("pr1ma").expect("a plain name is legal"),
|
||||
"swarm/hives/pr1ma/matrix/registration-token"
|
||||
appservice_token_path("pr1ma").expect("a plain name is legal"),
|
||||
"swarm/hives/pr1ma/matrix/appservice-token"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_registration_token_is_not_a_top_level_namespace() {
|
||||
// The path it used to hold. `Kind` is a closed set and `matrix` is not
|
||||
// one of its members, so a path with `matrix` as the second segment is
|
||||
// outside every grant — which is how it came to 403 on every read.
|
||||
let p = registration_token_path("pr1ma").expect("legal");
|
||||
fn the_appservice_token_is_not_a_top_level_namespace() {
|
||||
// The path its predecessor used to hold. `Kind` is a closed set and
|
||||
// `matrix` is not one of its members, so a path with `matrix` as the
|
||||
// second segment is outside every grant — which is how it came to 403
|
||||
// on every read.
|
||||
let p = appservice_token_path("pr1ma").expect("legal");
|
||||
assert!(!p.starts_with("swarm/matrix/"), "{p}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_traversal_in_the_hive_name_is_refused() {
|
||||
let e = registration_token_path("../beta").expect_err("a traversal is not");
|
||||
let e = appservice_token_path("../beta").expect_err("a traversal is not");
|
||||
assert!(matches!(e, Error::PathSegment { kind: "hive", .. }), "{e}");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue