matrix: swarm-controller is the only minter
Every hive is in a swarm and every swarm runs matrix, so every swarm has a swarm-controller, and since #4810 its hive_sender pass mints each hive's @hive-<hive>: sender token into the store every five minutes. The two other minters of that token go: - swarm-matrix-ctl mint: the systemd.services.swarm-matrix-ctl unit in the hive-matrix container, Command::Mint and src/mint.rs. The binary, its appservice render/publish verbs, ctlPackage, ctlActive and the ctl cert role stay. bao-matrix-reader's checks on the deleted unit are removed; the leaf-identity and no-token-in-env checks now look at swarm-matrix-appservice-publish, which runs under the same identity. - the hive-side mint ladder in hive-c0re's ensure_hive_user (register/appservice-login/password-login with the local as_token), with read_appservice_token, paths::matrix_appservice_token and the helpers only it used. ensure_hive_user now takes the store's token, keeps the file when the store has none or can't be reached, and fails otherwise. - hivectl matrix sync-admin: the verb, HostRequest::MatrixSyncAdmin and handle_matrix_sync_admin. The periodic MatrixSweep (ensure_all) is unchanged apart from no longer reading the local as_token. This removes the double-mint race #4810's review flagged: two minters logging in on one pinned device could leave a dead token in the store until the next pass. Closes #4813 Closes #4814
This commit is contained in:
parent
91e47732a6
commit
ddb7d7196d
22 changed files with 187 additions and 1162 deletions
|
|
@ -7,20 +7,14 @@
|
|||
//! identity plumbing to add one action, so the next thing that has to run in
|
||||
//! here is a verb below, not a new crate.
|
||||
//!
|
||||
//! [`mint`] publishes a hive's appservice sender token to the swarm's secret
|
||||
//! store, once. [`appservice`] mints the **swarm's** own appservice
|
||||
//! registration and publishes its token for `swarm-controller`.
|
||||
//!
|
||||
//! It lives in the container because the appservice `as_token` that authorises
|
||||
//! the mint is *already* there — the registration tuwunel loads is bind-mounted
|
||||
//! in — so no second holder of that secret is created.
|
||||
//! [`appservice`] mints the **swarm's** own appservice registration and
|
||||
//! publishes its token for `swarm-controller`.
|
||||
//!
|
||||
//! 🩸 **A secret is a path, never a value.** The only identifier any verb here
|
||||
//! logs is the store path; see `swarm_matrix_client`'s module doc for the same rule
|
||||
//! applied to error messages.
|
||||
|
||||
mod appservice;
|
||||
mod mint;
|
||||
mod registration;
|
||||
|
||||
use anyhow::Result;
|
||||
|
|
@ -38,13 +32,6 @@ struct Cli {
|
|||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum Command {
|
||||
/// Publish the appservice sender account's access token to the swarm
|
||||
/// secret store, once.
|
||||
///
|
||||
/// Configured entirely by the `MATRIX_MINT_*` environment the unit sets —
|
||||
/// no flags, because a systemd `Environment=` block is what a nix module
|
||||
/// can render and a command line full of paths is not.
|
||||
Mint,
|
||||
/// The swarm's own appservice registration, whose sender is the
|
||||
/// homeserver's admin account. Configured by `MATRIX_APPSERVICE_*`.
|
||||
#[command(subcommand)]
|
||||
|
|
@ -71,7 +58,6 @@ async fn main() -> Result<()> {
|
|||
.init();
|
||||
|
||||
match Cli::parse().command {
|
||||
Command::Mint => mint::run().await,
|
||||
Command::Appservice(Appservice::Render) => appservice::render(),
|
||||
Command::Appservice(Appservice::Publish) => appservice::publish().await,
|
||||
}
|
||||
|
|
@ -87,17 +73,8 @@ mod tests {
|
|||
Cli::command().debug_assert();
|
||||
}
|
||||
|
||||
/// The unit's `ExecStart` names a verb, so a rename of it is a deploy-time
|
||||
/// failure with no local signal. This is that signal.
|
||||
#[test]
|
||||
fn mint_is_spelled_the_way_the_unit_invokes_it() {
|
||||
let cli = Cli::try_parse_from(["swarm-matrix-ctl", "mint"]).expect("`mint` is a verb");
|
||||
assert!(matches!(cli.command, Command::Mint));
|
||||
}
|
||||
|
||||
/// The control: without it the case above passes on a parser that accepts
|
||||
/// anything.
|
||||
/// The two units name these verbs, same reason as the test above.
|
||||
/// The two units name these verbs in `ExecStart`, so a rename of one is a
|
||||
/// deploy-time failure with no local signal. This is that signal.
|
||||
#[test]
|
||||
fn the_appservice_verbs_are_spelled_the_way_the_units_invoke_them() {
|
||||
let cli =
|
||||
|
|
@ -122,9 +99,9 @@ mod tests {
|
|||
.expect_err("only declared verbs are accepted");
|
||||
}
|
||||
|
||||
/// A bare invocation must not silently do something. `mint` writes a
|
||||
/// credential, so "no verb" defaulting to it would make a typo in the unit
|
||||
/// mint rather than fail.
|
||||
/// A bare invocation must not silently do something. `appservice publish`
|
||||
/// writes a credential, so "no verb" defaulting to it would make a typo in
|
||||
/// the unit write rather than fail.
|
||||
#[test]
|
||||
fn no_verb_at_all_is_refused() {
|
||||
Cli::try_parse_from(["swarm-matrix-ctl"]).expect_err("a verb is required");
|
||||
|
|
|
|||
Loading…
Reference in a new issue