fix: implement 22 security pentest remediation fixes

Server-side session management with SHA-256 hashed tokens and HttpOnly
cookies. Auth middleware gating all RPC/WS/proxy routes with method
allowlist. Login rate limiting (5/60s per IP). CORS restricted to
config origin. Docker registry allowlist. App ID and path validation.
P2P message sanitization (HTML + log injection). Onion address and
known-peer validation. Nginx security headers (CSP, X-Frame-Options,
etc.) and AIUI proxy auth. Systemd hardening (non-root, NoNewPrivileges,
ProtectSystem).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-06 03:26:56 +00:00
parent 6623dbc4ab
commit 6656d2f1d9
12 changed files with 503 additions and 77 deletions

View File

@@ -16,6 +16,7 @@ impl RpcHandler {
.get("id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing package id"))?;
validate_app_id(package_id)?;
let docker_image = params
.get("dockerImage")
@@ -565,6 +566,7 @@ impl RpcHandler {
.get("id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing package id"))?;
validate_app_id(package_id)?;
let preserve_data = params
.get("preserve_data")
.and_then(|v| v.as_bool())
@@ -711,6 +713,7 @@ impl RpcHandler {
/// Get all container names for an app (handles multi-container apps like mempool)
async fn get_containers_for_app(package_id: &str) -> Result<Vec<String>> {
validate_app_id(package_id)?;
let output = tokio::process::Command::new("sudo")
.args(["podman", "ps", "-a", "--format", "{{.Names}}"])
.output()
@@ -759,7 +762,8 @@ async fn get_containers_for_app(package_id: &str) -> Result<Vec<String>> {
Ok(result)
}
/// Get data directories to clean for an app
/// Get data directories to clean for an app.
/// Caller must validate package_id before calling.
fn get_data_dirs_for_app(package_id: &str) -> Vec<String> {
let base = "/var/lib/archipelago";
match package_id {
@@ -781,20 +785,39 @@ fn get_data_dirs_for_app(package_id: &str) -> Vec<String> {
}
}
/// Validate Docker image name format
/// Prevents command injection via malicious image names
/// Trusted Docker registries. Only images from these sources are allowed.
const TRUSTED_REGISTRIES: &[&str] = &[
"docker.io/",
"ghcr.io/",
"localhost/",
];
/// Validate Docker image against trusted registry allowlist.
fn is_valid_docker_image(image: &str) -> bool {
if image.is_empty() || image.len() > 256 {
return false;
}
// Reject shell metacharacters
let dangerous_chars = ['&', '|', ';', '`', '$', '(', ')', '<', '>', '\n', '\r'];
if image.chars().any(|c| dangerous_chars.contains(&c)) {
return false;
}
if !image.chars().any(|c| c.is_alphanumeric()) {
return false;
// Must come from a trusted registry
TRUSTED_REGISTRIES.iter().any(|r| image.starts_with(r))
}
/// Validate that a package/app ID is safe (lowercase alphanumeric + hyphens, 1-64 chars).
fn validate_app_id(id: &str) -> Result<()> {
if id.is_empty() || id.len() > 64 {
anyhow::bail!("Invalid app id: must be 1-64 characters");
}
if image.len() > 256 {
return false;
if !id.bytes().all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-') {
anyhow::bail!("Invalid app id: only lowercase letters, digits, and hyphens allowed");
}
true
if id.starts_with('-') {
anyhow::bail!("Invalid app id: must not start with a hyphen");
}
Ok(())
}
/// Per-app Linux capabilities needed beyond the default cap-drop=ALL.