hive-c0re/matrix.rs: trim UIAA prose to docs pointer (#715 batch 1)
This commit is contained in:
parent
017786564a
commit
91e50d843c
1 changed files with 23 additions and 51 deletions
|
|
@ -1,35 +1,11 @@
|
||||||
//! Optional matrix-tuwunel wiring. When the `hive-matrix` nixos-container
|
//! Optional matrix-tuwunel wiring: shared registration token (host) +
|
||||||
//! is present and running, hive-c0re ensures:
|
//! per-agent UIAA registration → `<agent-state>/matrix-token`. No-op
|
||||||
|
//! when the `hive-matrix` container isn't running, so operators who
|
||||||
|
//! haven't flipped `hyperhive.matrix.enable = true` pay nothing.
|
||||||
//!
|
//!
|
||||||
//! 1. A shared `registration_token` exists at
|
//! See `docs/matrix.md::Provisioning flow (registration token)` for
|
||||||
//! `/var/lib/hyperhive/matrix-register-token` (mode 0600, generated
|
//! the full UIAA round-trip, token-file shape, and host/container
|
||||||
//! once on first boot). The hive-matrix module bind-mounts that file
|
//! bind-mount layout.
|
||||||
//! read-only into the tuwunel container so tuwunel can resolve its
|
|
||||||
//! `registration_token_file` setting against it.
|
|
||||||
//! 2. Every agent (and the manager) has a matrix account on the local
|
|
||||||
//! homeserver with an `access_token` written to
|
|
||||||
//! `<agent-state>/matrix-token`. Idempotent: skips registration when
|
|
||||||
//! the token file already exists.
|
|
||||||
//!
|
|
||||||
//! Agents never see the registration token — only their own `access_token`.
|
|
||||||
//! Account provisioning rides the matrix-spec UIAA flow:
|
|
||||||
//!
|
|
||||||
//! ```text
|
|
||||||
//! POST /_matrix/client/v3/register
|
|
||||||
//! {"username": "<agent>", "password": "<random>"}
|
|
||||||
//! → 401 {"flows":[{"stages":["m.login.registration_token"]}],
|
|
||||||
//! "session": "<id>", ...}
|
|
||||||
//!
|
|
||||||
//! POST /_matrix/client/v3/register
|
|
||||||
//! {"username": "<agent>", "password": "<random>",
|
|
||||||
//! "auth": {"type": "m.login.registration_token",
|
|
||||||
//! "token": "<reg_token>", "session": "<id>"}}
|
|
||||||
//! → 200 {"user_id": "@agent:server", "access_token": "<at>", ...}
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! No-op when the `hive-matrix` container isn't running (detected via
|
|
||||||
//! `nixos-container list`), so operators who haven't flipped
|
|
||||||
//! `hyperhive.matrix.enable = true` pay nothing.
|
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
|
@ -123,15 +99,12 @@ pub fn ensure_register_token() -> Result<String> {
|
||||||
}
|
}
|
||||||
std::fs::write(path, format!("{token}\n"))
|
std::fs::write(path, format!("{token}\n"))
|
||||||
.with_context(|| format!("write registration token to {}", path.display()))?;
|
.with_context(|| format!("write registration token to {}", path.display()))?;
|
||||||
// Mode 0640: tuwunel inside the hive-matrix container runs as a
|
// Mode 0640 (group-readable for tuwunel via the host-side
|
||||||
// non-root user; the file gets `chown :tuwunel` via the activation
|
// activation script's chown). 0600 blocks tuwunel; 0644
|
||||||
// script in `nix/modules/hive-matrix.nix` so the tuwunel group
|
// world-reads the token. The activation script reconciles
|
||||||
// gains read. 0600 would block tuwunel with `Permission denied
|
// chown + chmod on every boot, so this write is best-effort.
|
||||||
// (os error 13)` (#644); 0644 would world-read the token (mara
|
// See `docs/matrix.md::Provisioning flow (registration token)`
|
||||||
// veto: footgun). 0640 with a pinned group is the sweet spot.
|
// for the host/container permission split.
|
||||||
// The host-side activation script also chowns + chmods on every
|
|
||||||
// boot, so this is best-effort: hive-c0re may write the file
|
|
||||||
// before the chown lands, but the next activation reconciles it.
|
|
||||||
let _ = std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o640));
|
let _ = std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o640));
|
||||||
tracing::info!(path = %path.display(), "matrix: generated registration token");
|
tracing::info!(path = %path.display(), "matrix: generated registration token");
|
||||||
Ok(token)
|
Ok(token)
|
||||||
|
|
@ -172,9 +145,9 @@ async fn register_post(
|
||||||
/// Generate a throwaway random password for matrix UIAA registration.
|
/// Generate a throwaway random password for matrix UIAA registration.
|
||||||
/// `PASSWORD_BYTES` raw bytes ⇒ 64-char hex string. Agents authenticate
|
/// `PASSWORD_BYTES` raw bytes ⇒ 64-char hex string. Agents authenticate
|
||||||
/// by `access_token` so the password is protocol overhead we never
|
/// by `access_token` so the password is protocol overhead we never
|
||||||
/// persist; the operator path in `hivectl` (#663) lets the caller
|
/// persist; the operator path in `hivectl` lets the caller supply a
|
||||||
/// supply a real password instead so they can log into a matrix web
|
/// real password instead so they can log into a matrix web client
|
||||||
/// client (`m.login.password`).
|
/// (`m.login.password`).
|
||||||
pub fn random_password() -> Result<String> {
|
pub fn random_password() -> Result<String> {
|
||||||
random_hex(PASSWORD_BYTES)
|
random_hex(PASSWORD_BYTES)
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +161,7 @@ pub fn random_password() -> Result<String> {
|
||||||
///
|
///
|
||||||
/// Caller picks the password: agents use [`random_password`] (throwaway
|
/// Caller picks the password: agents use [`random_password`] (throwaway
|
||||||
/// — they auth by `access_token`), operators on the `hivectl` path
|
/// — they auth by `access_token`), operators on the `hivectl` path
|
||||||
/// supply their own so they can log into matrix web clients (#663).
|
/// supply their own so they can log into matrix web clients.
|
||||||
async fn register_user(
|
async fn register_user(
|
||||||
client: &reqwest::Client,
|
client: &reqwest::Client,
|
||||||
agent: &str,
|
agent: &str,
|
||||||
|
|
@ -250,8 +223,7 @@ fn extract_access_token(body: &serde_json::Value) -> Result<String> {
|
||||||
/// To force re-registration, delete the token file.
|
/// To force re-registration, delete the token file.
|
||||||
///
|
///
|
||||||
/// `client` is shared across the sweep so we build one reqwest
|
/// `client` is shared across the sweep so we build one reqwest
|
||||||
/// connection pool for all agents rather than one per call (argus
|
/// connection pool for all agents rather than one per call.
|
||||||
/// nit on #565 — bounded but wasteful).
|
|
||||||
pub async fn ensure_user_for(
|
pub async fn ensure_user_for(
|
||||||
client: &reqwest::Client,
|
client: &reqwest::Client,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|
@ -285,11 +257,11 @@ pub async fn ensure_user_for(
|
||||||
/// for storing it. Used by `hivectl matrix create-user` for human
|
/// for storing it. Used by `hivectl matrix create-user` for human
|
||||||
/// (non-agent) accounts so we don't create stray
|
/// (non-agent) accounts so we don't create stray
|
||||||
/// `/var/lib/hyperhive/agents/<name>/` directories for users that
|
/// `/var/lib/hyperhive/agents/<name>/` directories for users that
|
||||||
/// aren't agents (#662). For operator accounts the caller passes a
|
/// aren't agents. For operator accounts the caller passes a real
|
||||||
/// real password so the operator can `m.login.password` into matrix
|
/// password so the operator can `m.login.password` into matrix web
|
||||||
/// web clients afterwards (#663); for headless agent re-provisioning
|
/// clients afterwards; for headless agent re-provisioning the caller
|
||||||
/// the caller can pass [`random_password`] to keep the existing
|
/// can pass [`random_password`] to keep the existing throwaway
|
||||||
/// throwaway behaviour.
|
/// behaviour.
|
||||||
///
|
///
|
||||||
/// **Not idempotent** (unlike [`forge::provision_user_token`]): the
|
/// **Not idempotent** (unlike [`forge::provision_user_token`]): the
|
||||||
/// matrix UIAA `/register` endpoint returns `M_USER_IN_USE` (HTTP 400)
|
/// matrix UIAA `/register` endpoint returns `M_USER_IN_USE` (HTTP 400)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue