fix(security): bind seq into mesh signatures (v2 preimage), guard DID slice, cfg-gate dev password
- mesh: verify_signature accepts a v2 preimage (t,v,ts,seq) alongside
legacy v1 (t,v,ts); signed_with_seq() is the v2 sender path, not yet
wired — senders stay v1 until the fleet verifies v2 (receivers
hard-drop bad sigs, so flipping send-side first would break
mixed-fleet alerts). Tests: v2 verify, v2 seq-tamper rejection,
v1 sign-then-set-seq compat.
- mesh listener: malformed radio-supplied DID shorter than the
'did🔑' prefix can no longer panic advert_name (slice -> .get()).
- auth: the pre-setup password123 dev login and the constant itself are
now #[cfg(debug_assertions)] — no release binary carries the bypass,
whatever its runtime config says.
- orchestrator: canned host-facts under #[cfg(test)] — awaiting real
subprocesses under tokio's paused test clock deadlocks against
auto-advanced timers (the old blocking detection only worked by never
yielding).
- drop two now-unused std::process::Command imports left by 4c75bb3d.
Tests: mesh 110/110 (incl. 2 new), api 68/68, container 159/159,
archipelago-container check clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
291f2d7186
commit
2c8c99fd28
@@ -32,7 +32,6 @@ use async_trait::async_trait;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
@@ -2725,19 +2724,34 @@ impl ProdContainerOrchestrator {
|
||||
}
|
||||
|
||||
async fn detect_host_facts(&self) -> HostFacts {
|
||||
let host_ip = Self::detect_host_ip()
|
||||
.await
|
||||
.unwrap_or_else(|| "127.0.0.1".to_string());
|
||||
let host_mdns = Self::detect_host_mdns().await;
|
||||
let disk_gb = self.disk_gb().await;
|
||||
HostFacts {
|
||||
host_ip,
|
||||
host_mdns,
|
||||
disk_gb,
|
||||
// Cheap default; resolve_dynamic_env fills the real node name on
|
||||
// demand (it costs a podman call) only for manifests that use
|
||||
// {{BITCOIN_HOST}}, rather than every app on every reconcile.
|
||||
bitcoin_host: "bitcoin-knots".to_string(),
|
||||
// Unit tests run under tokio's paused clock; awaiting a real
|
||||
// subprocess there deadlocks against auto-advanced timers (the old
|
||||
// BLOCKING detection only worked by never yielding). Canned facts.
|
||||
#[cfg(test)]
|
||||
{
|
||||
return HostFacts {
|
||||
host_ip: "127.0.0.1".to_string(),
|
||||
host_mdns: "test.local".to_string(),
|
||||
disk_gb: self.test_disk_gb.unwrap_or(1000),
|
||||
bitcoin_host: "bitcoin-knots".to_string(),
|
||||
};
|
||||
}
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
let host_ip = Self::detect_host_ip()
|
||||
.await
|
||||
.unwrap_or_else(|| "127.0.0.1".to_string());
|
||||
let host_mdns = Self::detect_host_mdns().await;
|
||||
let disk_gb = self.disk_gb().await;
|
||||
HostFacts {
|
||||
host_ip,
|
||||
host_mdns,
|
||||
disk_gb,
|
||||
// Cheap default; resolve_dynamic_env fills the real node name on
|
||||
// demand (it costs a podman call) only for manifests that use
|
||||
// {{BITCOIN_HOST}}, rather than every app on every reconcile.
|
||||
bitcoin_host: "bitcoin-knots".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2745,10 +2759,15 @@ impl ProdContainerOrchestrator {
|
||||
/// `bitcoin-core`) for the `{{BITCOIN_HOST}}` derived-env placeholder.
|
||||
/// Defaults to `bitcoin-knots` when none is running (B12).
|
||||
async fn bitcoin_host(&self) -> String {
|
||||
// No real podman under the tests' paused clock (see detect_host_facts).
|
||||
#[cfg(test)]
|
||||
if let Some(host) = &self.test_bitcoin_host {
|
||||
return host.clone();
|
||||
{
|
||||
return self
|
||||
.test_bitcoin_host
|
||||
.clone()
|
||||
.unwrap_or_else(|| "bitcoin-knots".to_string());
|
||||
}
|
||||
#[allow(unreachable_code)]
|
||||
// Mirrors api::rpc::package::dependencies (the legacy install path);
|
||||
// both Bitcoin node variants are reachable on archy-net by name.
|
||||
const BITCOIN_NAMES: &[&str] = &["bitcoin-knots", "bitcoin-core", "bitcoin"];
|
||||
@@ -2839,11 +2858,15 @@ impl ProdContainerOrchestrator {
|
||||
}
|
||||
|
||||
async fn disk_gb(&self) -> u64 {
|
||||
// No real df under the tests' paused clock (see detect_host_facts).
|
||||
#[cfg(test)]
|
||||
if let Some(disk_gb) = self.test_disk_gb {
|
||||
return disk_gb;
|
||||
{
|
||||
return self.test_disk_gb.unwrap_or(1000);
|
||||
}
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
Self::detect_disk_gb().await
|
||||
}
|
||||
Self::detect_disk_gb().await
|
||||
}
|
||||
|
||||
/// Ensure app-specific secrets exist *before* env resolution. The Bitcoin
|
||||
|
||||
Reference in New Issue
Block a user