fix: route forge_admin through hive-priv; auto-recover matrix passwords
forge_admin() spawned nixos-container run hive-forge directly from the
hive-core process. nixos-container run uses nsenter to enter the container
namespaces, which requires root. hive-core is unprivileged, so every call
failed with: nsenter: stat of /proc/<pid>/ns/user failed: Permission denied
Fix: add RunForgeAdmin { args } to PrivRequest. hive-priv (root) handles
it by spawning nixos-container run hive-forge -- runuser -u forgejo --
forgejo --work-path /var/lib/forgejo admin <args>. forge_admin() now calls
priv_client::run_forge_admin().
matrix: ensure_user_for hit M_USER_IN_USE then failed when the stored
password file was missing (state dirs wiped but homeserver kept accounts).
Previously required manual hivectl matrix reset-password <name>.
Fix: add auto_reset_password() — calls the admin API (PUT
/_synapse/admin/v2/users/@<name>:<server> with the hive admin token) to
set a new random password, then proceeds with login. Falls back to the
existing manual-recovery error if the admin token is unavailable.
Closes #1234
This commit is contained in:
parent
3830b13b03
commit
34bc4c0b06
5 changed files with 147 additions and 47 deletions
|
|
@ -334,9 +334,71 @@ async fn exec(req: PrivRequest, writer: &mut OwnedWriteHalf) -> Result<(String,
|
|||
.with_context(|| format!("chmod {:o} {}", mode, path.display()))?;
|
||||
Ok((String::new(), String::new()))
|
||||
}
|
||||
|
||||
PrivRequest::RunForgeAdmin { ref args } => {
|
||||
for arg in args {
|
||||
validate_forge_admin_arg(arg)?;
|
||||
}
|
||||
run_forge_admin(args).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate a single argument destined for `forgejo admin`. Rejects
|
||||
/// null bytes and newlines (which could corrupt the subprocess args list
|
||||
/// or log output). Shell metacharacters are harmless since the command
|
||||
/// is spawned directly (no shell), but we reject them defensively.
|
||||
fn validate_forge_admin_arg(arg: &str) -> Result<()> {
|
||||
if arg.bytes().any(|b| b == 0 || b == b'\n' || b == b'\r') {
|
||||
bail!("forge admin arg {arg:?} contains null byte or newline");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run `forgejo admin <args>` inside the `hive-forge` container as the
|
||||
/// `forgejo` unix user. Requires root (for nsenter into the container's
|
||||
/// namespaces). Returns `(stdout, stderr)`.
|
||||
async fn run_forge_admin(args: &[String]) -> Result<(String, String)> {
|
||||
let mut cmd_args: Vec<&str> = vec![
|
||||
"run",
|
||||
"hive-forge",
|
||||
"--",
|
||||
"runuser",
|
||||
"-u",
|
||||
"forgejo",
|
||||
"--",
|
||||
"forgejo",
|
||||
"--work-path",
|
||||
"/var/lib/forgejo",
|
||||
"admin",
|
||||
];
|
||||
for a in args {
|
||||
cmd_args.push(a.as_str());
|
||||
}
|
||||
let out = Command::new("nixos-container")
|
||||
.args(&cmd_args)
|
||||
.output()
|
||||
.await
|
||||
.context("invoke nixos-container run hive-forge -- forgejo admin")?;
|
||||
let stdout = String::from_utf8_lossy(&out.stdout).into_owned();
|
||||
let stderr = String::from_utf8_lossy(&out.stderr).into_owned();
|
||||
for line in stdout.lines() {
|
||||
tracing::info!(target: "forgejo-admin", "{line}");
|
||||
}
|
||||
for line in stderr.lines() {
|
||||
tracing::warn!(target: "forgejo-admin", "{line}");
|
||||
}
|
||||
if !out.status.success() {
|
||||
bail!(
|
||||
"forgejo admin {} failed ({}): {}",
|
||||
args.join(" "),
|
||||
out.status,
|
||||
stderr.trim()
|
||||
);
|
||||
}
|
||||
Ok((stdout, stderr))
|
||||
}
|
||||
|
||||
/// Invoke `nixos-container` with the given args, log output to journald.
|
||||
async fn container_run(args: &[&str]) -> Result<(String, String)> {
|
||||
let out = Command::new("nixos-container")
|
||||
|
|
|
|||
Loading…
Reference in a new issue