fix(cli): pass worker proxy via env, not argv (credential leak) (#370)

obscura serve --workers N --proxy ... spawned each worker with the proxy
as a --proxy CLI flag, so the plaintext credential was visible to any
local user via ps / /proc/<pid>/cmdline (issue #366). scrape already
avoids this by passing the proxy in OBSCURA_PROXY, which is only readable
by the owner.

Pass the proxy to serve workers via OBSCURA_PROXY too, and have the serve
command read that env as a fallback when no --proxy flag is given. The
worker argv no longer contains the credential.

Fixes #366.
This commit is contained in:
SGavrl authored and GitHub committed 2026-07-09 10:52:06 +02:00
1 parent 6788996b0b
commit f5d7e11692
1 file changed
+10 -2
+10 -2
View File
@@ -329,7 +329,11 @@ async fn main() -> anyhow::Result<()> {
match args.command {
Some(Command::Serve { port, host, proxy, user_agent, workers, allow_file_access, storage_dir, quiet: _ }) => {
let proxy = merge_proxy(global_proxy.clone(), proxy);
// Fall back to OBSCURA_PROXY so a proxy can be supplied without
// putting credentials on the command line. The multi-worker load
// balancer passes the proxy to each worker this way (issue #366).
let proxy = merge_proxy(global_proxy.clone(), proxy)
.or_else(|| std::env::var("OBSCURA_PROXY").ok().filter(|s| !s.is_empty()));
print_banner(port);
if let Some(ref dir) = storage_dir {
tracing::info!("Storage dir: {}", dir.display());
@@ -423,7 +427,11 @@ async fn run_multi_worker_serve(
let mut cmd = std::process::Command::new(&exe);
cmd.arg("serve").arg("--port").arg(worker_port.to_string());
if let Some(ref p) = proxy {
cmd.arg("--proxy").arg(p);
// Pass the proxy (which may embed credentials) via the environment,
// not argv. A --proxy flag is visible in `ps`/`/proc/<pid>/cmdline`
// to any local user; OBSCURA_PROXY is only readable by the owner
// (issue #366). The worker's serve path reads this env as a fallback.
cmd.env("OBSCURA_PROXY", p);
}
if let Some(ref ua) = user_agent {
cmd.arg("--user-agent").arg(ua);