use std::path::Path;
#[derive(Debug, Clone)]
pub struct SystemCapabilities {
pub has_root: bool,
pub has_user_namespaces: bool,
pub has_seccomp: bool,
pub has_landlock: bool,
pub has_cgroup_v2: bool,
pub has_cgroup_delegation: bool,
}
impl SystemCapabilities {
pub fn detect() -> Self {
Self {
has_root: detect_root(),
has_user_namespaces: detect_user_namespaces(),
has_seccomp: detect_seccomp(),
has_landlock: detect_landlock(),
has_cgroup_v2: detect_cgroup_v2(),
has_cgroup_delegation: detect_cgroup_delegation(),
}
}
pub fn can_sandbox_unprivileged(&self) -> bool {
self.has_seccomp
}
pub fn can_sandbox_privileged(&self) -> bool {
self.has_root && self.has_cgroup_v2
}
pub fn summary(&self) -> String {
let mut lines = Vec::new();
let check = |available: bool| if available { "[ok]" } else { "[--]" };
lines.push(format!("{} Root privileges", check(self.has_root)));
lines.push(format!(
"{} User namespaces",
check(self.has_user_namespaces)
));
lines.push(format!("{} Seccomp BPF", check(self.has_seccomp)));
lines.push(format!("{} Landlock LSM", check(self.has_landlock)));
lines.push(format!("{} Cgroup v2", check(self.has_cgroup_v2)));
lines.push(format!(
"{} Cgroup delegation",
check(self.has_cgroup_delegation)
));
lines.join("\n")
}
}
fn detect_root() -> bool {
unsafe { libc::geteuid() == 0 }
}
fn detect_user_namespaces() -> bool {
if let Ok(content) = std::fs::read_to_string("/proc/sys/kernel/unprivileged_userns_clone")
&& content.trim() == "0"
{
return false;
}
if let Ok(content) = std::fs::read_to_string("/proc/sys/user/max_user_namespaces")
&& let Ok(max) = content.trim().parse::<u64>()
{
return max > 0;
}
true
}
fn detect_seccomp() -> bool {
let ret = unsafe { libc::prctl(libc::PR_GET_SECCOMP, 0, 0, 0, 0) };
ret >= 0
}
fn detect_landlock() -> bool {
let ret = unsafe {
libc::syscall(
libc::SYS_landlock_create_ruleset,
std::ptr::null::<libc::c_void>(),
0usize,
1u32, )
};
if ret >= 0 {
return true;
}
let errno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
errno != libc::ENOSYS
}
fn detect_cgroup_v2() -> bool {
Path::new("/sys/fs/cgroup/cgroup.controllers").exists()
}
fn detect_cgroup_delegation() -> bool {
let uid = unsafe { libc::geteuid() };
if uid == 0 {
return true; }
let user_slice = format!("/sys/fs/cgroup/user.slice/user-{}.slice", uid);
let path = Path::new(&user_slice);
if !path.exists() {
return false;
}
let test_path = path.join("sandbox-test-probe");
match std::fs::create_dir(&test_path) {
Ok(()) => {
let _ = std::fs::remove_dir(&test_path);
true
}
Err(_) => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detect_returns_valid_capabilities() {
let caps = SystemCapabilities::detect();
let _ = caps.has_root;
let _ = caps.has_seccomp;
let _ = caps.has_user_namespaces;
let _ = caps.has_landlock;
let _ = caps.has_cgroup_v2;
let _ = caps.has_cgroup_delegation;
}
#[test]
fn summary_produces_output() {
let caps = SystemCapabilities::detect();
let summary = caps.summary();
assert!(!summary.is_empty());
assert!(summary.contains("Root privileges"));
assert!(summary.contains("Seccomp BPF"));
}
#[test]
fn seccomp_detection_works() {
let has = detect_seccomp();
let _ = has;
}
#[test]
fn root_detection_matches_euid() {
let detected = detect_root();
let actual = unsafe { libc::geteuid() == 0 };
assert_eq!(detected, actual);
}
}