swarm-matrix-ctl: mint the swarm's own appservice registration
The swarm gets an appservice identity of its own, separate from each hive's `hyperhive` registration. `swarm-matrix-ctl appservice render` mints its tokens inside the matrix container when they are absent and renders the registration tuwunel loads; `appservice publish` writes its as_token to `swarm/controller/swarm-controller/matrix/appservice-token`, the one kind no hive's policy grants. The homeserver calls move out of swarm-matrix-ctl into swarm-matrix-client, with a `whoami`, so swarm-controller can mint agents' accounts through the same pinned device id instead of a copy of them.
This commit is contained in:
parent
cb176c7be7
commit
89aff8d613
10 changed files with 523 additions and 23 deletions
|
|
@ -93,6 +93,28 @@ pub fn appservice_token_path(hive: &str) -> Result<String, Error> {
|
|||
Ok(format!("{prefix}/matrix/appservice-token"))
|
||||
}
|
||||
|
||||
/// The name segment of the controller's own subtree, and the cert-auth role it
|
||||
/// logs in under (`swarm-controller`'s `store::CERT_ROLE`).
|
||||
const CONTROLLER: &str = "swarm-controller";
|
||||
|
||||
/// The path holding the **swarm's** appservice token: the one registration
|
||||
/// on the swarm's homeserver that is not a hive's, whose sender is promoted
|
||||
/// to homeserver admin at boot.
|
||||
///
|
||||
/// The matrix container mints it and publishes it here; `swarm-controller`
|
||||
/// reads it to create agents' accounts. Under [`Kind::Controller`] because
|
||||
/// that is the one kind [`crate::policy::render`] grants no hive: every hive
|
||||
/// reads `agents/*`, its own `hives/<hive>/*` and `services/*`, so under any
|
||||
/// of those this admin credential would be readable by every hive.
|
||||
///
|
||||
/// # Errors
|
||||
/// Never in practice: the name segment is a constant. The `Result` is
|
||||
/// [`principal_prefix`]'s.
|
||||
pub fn swarm_appservice_token_path() -> Result<String, Error> {
|
||||
let prefix = principal_prefix(Kind::Controller, CONTROLLER)?;
|
||||
Ok(format!("{prefix}/matrix/appservice-token"))
|
||||
}
|
||||
|
||||
/// What an account's path holds: the token, plus the homeserver it belongs to.
|
||||
///
|
||||
/// The homeserver rides with the token rather than on the queue notice that
|
||||
|
|
@ -215,6 +237,39 @@ mod tests {
|
|||
assert!(matches!(e, Error::PathSegment { kind: "hive", .. }), "{e}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_swarm_appservice_token_lands_under_the_controller() {
|
||||
assert_eq!(
|
||||
swarm_appservice_token_path().expect("a constant segment"),
|
||||
"swarm/controller/swarm-controller/matrix/appservice-token"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_hive_policy_reaches_the_swarm_appservice_token() {
|
||||
// 🩸 The swarm's sender is a homeserver admin, so its token must not
|
||||
// be under any stanza a hive's policy renders. Checked against the
|
||||
// rendered document rather than against the prefix, because the
|
||||
// document is what the store enforces.
|
||||
let doc = crate::policy::render("pr1ma").expect("a plain name is legal");
|
||||
// Every stanza is `path "secret/data/<prefix>*" { … }`.
|
||||
let granted: Vec<&str> = doc
|
||||
.lines()
|
||||
.filter_map(|l| l.strip_prefix("path \"secret/data/"))
|
||||
.filter_map(|p| p.strip_suffix("*\" {"))
|
||||
.collect();
|
||||
let reads = |path: &str| granted.iter().any(|g| path.starts_with(g));
|
||||
let path = swarm_appservice_token_path().expect("a constant segment");
|
||||
assert!(!reads(&path), "{path} is readable under {granted:?}");
|
||||
// The control: an agent's account path IS under a hive stanza, so the
|
||||
// assertion above can fail at all.
|
||||
let agent = account_path("atlas", "main").expect("legal");
|
||||
assert!(
|
||||
reads(&agent),
|
||||
"{agent} should be readable under {granted:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_segment_cannot_escape_its_own_directory() {
|
||||
// Each of these is a *different* way to address another agent's tree,
|
||||
|
|
|
|||
Loading…
Reference in a new issue