From 72b87dfaa98d59782c2f8993a448329c182646bd Mon Sep 17 00:00:00 2001 From: Nahuel Gonzalez <87866711+cryptonahue@users.noreply.github.com> Date: Sun, 15 Mar 2026 09:30:29 -0300 Subject: [PATCH] chromium no-sandbox root Chromium refuses to launch without --no-sandbox when the process is running as UID 0. This causes the browser hand to fail immediately with 'Chromium exited before printing DevTools URL' on any server-based OpenFang installation that runs as root (the default install). Added is_running_as_root() which reads /proc/self/status on Linux to detect UID 0 without requiring a libc dependency, with a fallback to the HOME env var for other Unix systems. When root is detected, --no-sandbox is appended to the Chromium launch args automatically. --- crates/openfang-runtime/src/browser.rs | 42 ++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/crates/openfang-runtime/src/browser.rs b/crates/openfang-runtime/src/browser.rs index 4bb0f2e7..a1607410 100644 --- a/crates/openfang-runtime/src/browser.rs +++ b/crates/openfang-runtime/src/browser.rs @@ -258,6 +258,12 @@ impl BrowserSession { args.insert(0, "--headless=new".to_string()); args.push("--disable-gpu".to_string()); } + // Chromium refuses to run as root without --no-sandbox. Detect this + // without adding a libc dependency by reading the effective UID from + // /proc/self/status (Linux) or falling back to the HOME env var. + if is_running_as_root() { + args.push("--no-sandbox".to_string()); + } let mut cmd = tokio::process::Command::new(&chrome_path); cmd.args(&args); @@ -1154,6 +1160,36 @@ const EXTRACT_CONTENT_JS: &str = r#"(() => { return JSON.stringify({title, url, content}); })()"#; +// ── Root detection ───────────────────────────────────────────────────────── + +/// Returns true if the current process is running as root (UID 0). +/// +/// On Linux, reads `/proc/self/status` to get the effective UID without +/// requiring a `libc` dependency. Falls back to checking the `HOME` env var +/// on systems where `/proc` is not available. +fn is_running_as_root() -> bool { + #[cfg(unix)] + { + // Primary: read effective UID from /proc/self/status (Linux) + if let Ok(status) = std::fs::read_to_string("/proc/self/status") { + for line in status.lines() { + if let Some(rest) = line.strip_prefix("Uid:") { + // Format: "Uid: " + if let Some(euid_str) = rest.split_whitespace().nth(1) { + return euid_str == "0"; + } + } + } + } + // Fallback: HOME=/root is a reliable indicator on most Unix systems + std::env::var("HOME").map(|h| h == "/root").unwrap_or(false) + } + #[cfg(not(unix))] + { + false + } +} + // ── Tests ────────────────────────────────────────────────────────────────── #[cfg(test)] @@ -1290,6 +1326,12 @@ mod tests { assert!(mgr.sessions.is_empty()); } + #[test] + fn test_is_running_as_root_returns_bool() { + // Just verify it doesn't panic and returns a bool. + let _ = is_running_as_root(); + } + #[test] fn test_chromium_candidates_not_empty() { let paths = chromium_candidates();