diff --git a/src/app.rs b/src/app.rs index a0197ce..9546912 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1449,6 +1449,10 @@ impl App { let provider = PlatformStatsProvider; let mut previous_stats: HashMap = HashMap::new(); + // Warn once if stat collection ever fails so a permission/sandbox + // regression (e.g. Landlock denying /sys) is visible at the + // default log level instead of being silently swallowed. + let mut warned_collect_failure = false; loop { if should_stop.load(Ordering::Relaxed) { @@ -1477,7 +1481,15 @@ impl App { } } Err(e) => { - debug!("Failed to collect interface stats: {}", e); + if !warned_collect_failure { + warn!( + "Failed to collect interface stats: {} (interface panel will be empty; on Linux this is often a sandbox/permission issue reading /sys/class/net)", + e + ); + warned_collect_failure = true; + } else { + debug!("Failed to collect interface stats: {}", e); + } } } diff --git a/src/network/platform/linux/sandbox/landlock.rs b/src/network/platform/linux/sandbox/landlock.rs index 6dc764d..1da0347 100644 --- a/src/network/platform/linux/sandbox/landlock.rs +++ b/src/network/platform/linux/sandbox/landlock.rs @@ -182,6 +182,20 @@ pub fn apply_landlock(config: &SandboxConfig) -> Result { log::warn!("Could not add /proc rule: {}", e); } + // Add rules for sysfs (read-only). The interface-stats poller enumerates + // interfaces via read_dir("/sys/class/net") and then reads each + // /sys/class/net//statistics/* counter. Those per-interface entries + // are symlinks into /sys/devices/.../net/, and Landlock evaluates the + // *resolved* path, so both subtrees need an allow-rule — without them the + // reads fail with EACCES and the Interfaces panel shows + // "No interface stats available". sysfs is not process-sensitive the way + // /proc is, and this is read-only, so granting the two subtrees is fine. + for sysfs_path in ["/sys/class/net", "/sys/devices"] { + if let Err(e) = add_path_rule(&mut ruleset_created, sysfs_path, read_access) { + log::warn!("Could not add {} rule: {}", sysfs_path, e); + } + } + // Add rules for read-only paths (e.g., GeoIP databases) for path in &config.read_paths { if path.exists()