feat: Phase 4 backend hardening — container reliability + security audit
Container Management (CONT-01 through CONT-06): - Fix needs_archy_net: add lnd, nbxplorer to archy-net list - Add StartupTier dependency ordering to health monitor (DB→Core→Dependent→App→UI) - Add exponential backoff (10s/30s/90s) with 1hr stability reset - Add get_health_check_args() with health checks for 20+ apps - Add get_memory_limit() with per-app limits (128m-4g vs blanket 2g) - Create docs/network-topology.md - Fix fedimint containers on both nodes (moved to archy-net) Security Audit (SEC-01 through SEC-06): - Add sanitize_error_message() — strips internal paths from RPC errors - Add validate_identity_id() — blocks path traversal on identity operations - Add validate_did() — blocks path traversal on federation operations - Add message size limits: node-send-message (1MB), dwn.write-message (10MB) - Add rate limits for federation endpoints (join: 5/60s, invite: 10/300s) - Configure journald (500MB max, 7 day retention) on both nodes - Add /etc/logrotate.d/archipelago for backend + crowdsec logs - Verify all 4 nginx security headers on both nodes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
f9a47a2602
commit
6335ea17ee
@@ -226,8 +226,9 @@ impl RpcHandler {
|
||||
let needs_archy_net = matches!(
|
||||
package_id,
|
||||
"bitcoin-knots" | "bitcoin" | "bitcoin-core"
|
||||
| "lnd"
|
||||
| "mempool" | "mempool-web" | "mempool-api" | "mempool-electrs" | "electrs" | "mysql-mempool" | "archy-mempool-db" | "archy-mempool-web"
|
||||
| "btcpay-server" | "btcpayserver" | "archy-btcpay-db"
|
||||
| "btcpay-server" | "btcpayserver" | "archy-btcpay-db" | "archy-nbxplorer" | "nbxplorer"
|
||||
| "fedimint" | "fedimint-gateway"
|
||||
);
|
||||
|
||||
@@ -329,12 +330,18 @@ printtoconsole=1\n";
|
||||
run_args.push(env);
|
||||
}
|
||||
|
||||
// Security: Resource limits (from manifest)
|
||||
let memory_limit = if package_id == "ollama" { "4g" } else { "2g" };
|
||||
// Resource limits: per-app memory and CPU
|
||||
let memory_limit = get_memory_limit(package_id);
|
||||
let mem_arg = format!("--memory={}", memory_limit);
|
||||
run_args.push(&mem_arg);
|
||||
run_args.push("--cpus=2");
|
||||
|
||||
// Health check definitions
|
||||
let health_args = get_health_check_args(package_id);
|
||||
for arg in &health_args {
|
||||
run_args.push(arg);
|
||||
}
|
||||
|
||||
// Finally, the image
|
||||
run_args.push(docker_image);
|
||||
|
||||
@@ -1289,6 +1296,148 @@ fn is_readonly_compatible(app_id: &str) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
/// Get container health check arguments for podman run.
|
||||
/// Returns (health-cmd, interval, retries) args to append to run_args.
|
||||
fn get_health_check_args(app_id: &str) -> Vec<String> {
|
||||
let (cmd, interval, retries) = match app_id {
|
||||
"bitcoin" | "bitcoin-core" | "bitcoin-knots" => (
|
||||
"bitcoin-cli -rpcuser=archipelago -rpcpassword=archipelago123 getblockchaininfo || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"lnd" => (
|
||||
"lncli getinfo || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"btcpay-server" | "btcpayserver" => (
|
||||
"curl -sf http://localhost:49392/ || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"mempool-api" => (
|
||||
"curl -sf http://localhost:8999/api/v1/backend-info || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"mempool" | "mempool-web" | "archy-mempool-web" => (
|
||||
"curl -sf http://localhost:8080/ || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"mempool-electrs" | "electrs" => (
|
||||
"curl -sf http://localhost:50001/ || exit 1",
|
||||
"60s", "3",
|
||||
),
|
||||
"nextcloud" => (
|
||||
"curl -sf http://localhost:80/status.php || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"homeassistant" | "home-assistant" => (
|
||||
"curl -sf http://localhost:8123/api/ || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"grafana" => (
|
||||
"curl -sf http://localhost:3000/api/health || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"jellyfin" => (
|
||||
"curl -sf http://localhost:8096/health || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"vaultwarden" => (
|
||||
"curl -sf http://localhost:80/alive || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"uptime-kuma" => (
|
||||
"curl -sf http://localhost:3001/ || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"filebrowser" => (
|
||||
"curl -sf http://localhost:80/health || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"searxng" => (
|
||||
"curl -sf http://localhost:8080/ || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"photoprism" => (
|
||||
"curl -sf http://localhost:2342/api/v1/status || exit 1",
|
||||
"60s", "3",
|
||||
),
|
||||
"immich_server" | "immich" => (
|
||||
"curl -sf http://localhost:2283/api/server/ping || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"dwn" => (
|
||||
"curl -sf http://localhost:3000/health || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"portainer" => (
|
||||
"curl -sf http://localhost:9000/api/status || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"ollama" => (
|
||||
"curl -sf http://localhost:11434/ || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"fedimint" => (
|
||||
"curl -sf http://localhost:8174/health || exit 1",
|
||||
"60s", "3",
|
||||
),
|
||||
"nostr-rs-relay" | "nostr-relay" => (
|
||||
"curl -sf http://localhost:8080/ || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
"nginx-proxy-manager" => (
|
||||
"curl -sf http://localhost:81/api/ || exit 1",
|
||||
"30s", "3",
|
||||
),
|
||||
_ => return vec![],
|
||||
};
|
||||
|
||||
vec![
|
||||
format!("--health-cmd={}", cmd),
|
||||
format!("--health-interval={}", interval),
|
||||
format!("--health-retries={}", retries),
|
||||
"--health-start-period=60s".to_string(),
|
||||
]
|
||||
}
|
||||
|
||||
/// Get per-app memory limit.
|
||||
fn get_memory_limit(app_id: &str) -> &'static str {
|
||||
match app_id {
|
||||
// Heavy apps
|
||||
"bitcoin" | "bitcoin-core" | "bitcoin-knots" => "2g",
|
||||
"onlyoffice" | "onlyoffice-documentserver" => "2g",
|
||||
"ollama" => "4g",
|
||||
// Medium apps
|
||||
"lnd" => "512m",
|
||||
"mempool-electrs" | "electrs" => "1g",
|
||||
"nextcloud" => "1g",
|
||||
"immich_server" | "immich" => "1g",
|
||||
"btcpay-server" | "btcpayserver" => "1g",
|
||||
"homeassistant" | "home-assistant" => "512m",
|
||||
"fedimint" => "512m",
|
||||
"fedimint-gateway" => "512m",
|
||||
"photoprism" => "1g",
|
||||
// Light apps
|
||||
"mempool-api" => "512m",
|
||||
"mempool" | "mempool-web" | "archy-mempool-web" => "256m",
|
||||
"grafana" => "256m",
|
||||
"jellyfin" => "1g",
|
||||
"vaultwarden" => "256m",
|
||||
"uptime-kuma" => "256m",
|
||||
"filebrowser" => "256m",
|
||||
"searxng" => "512m",
|
||||
"dwn" => "256m",
|
||||
"portainer" => "256m",
|
||||
"nostr-rs-relay" | "nostr-relay" => "256m",
|
||||
"nginx-proxy-manager" => "256m",
|
||||
// Databases
|
||||
"archy-btcpay-db" | "archy-mempool-db" | "mysql-mempool" => "512m",
|
||||
"immich_postgres" | "penpot-postgres" => "256m",
|
||||
"immich_redis" | "penpot-valkey" => "128m",
|
||||
// Default
|
||||
_ => "512m",
|
||||
}
|
||||
}
|
||||
|
||||
/// Get app-specific configuration
|
||||
/// Returns: (ports, volumes, env_vars, custom_command, custom_args)
|
||||
fn get_app_config(
|
||||
|
||||
Reference in New Issue
Block a user