Skip to main content

sandbox_core/
privilege.rs

1//! Privilege mode configuration for sandbox execution
2
3use crate::capabilities::SystemCapabilities;
4
5/// Determines how the sandbox operates with respect to privileges
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum PrivilegeMode {
8    /// Use only unprivileged mechanisms: user namespaces + seccomp + landlock + setrlimit.
9    /// Does NOT require root. Fails if essential unprivileged features are unavailable.
10    Unprivileged,
11
12    /// Use all available mechanisms including privileged ones: all namespaces + cgroups + chroot + seccomp.
13    /// Requires root. Fails if not running as root.
14    Privileged,
15
16    /// Automatically detect the best available mode.
17    /// Uses privileged mode if running as root, otherwise falls back to unprivileged.
18    #[default]
19    Auto,
20}
21
22impl PrivilegeMode {
23    /// Resolve Auto mode to a concrete mode based on system capabilities
24    pub fn resolve(&self, caps: &SystemCapabilities) -> ResolvedMode {
25        match self {
26            PrivilegeMode::Privileged => ResolvedMode::Privileged,
27            PrivilegeMode::Unprivileged => ResolvedMode::Unprivileged,
28            PrivilegeMode::Auto => {
29                if caps.has_root && caps.has_cgroup_v2 {
30                    ResolvedMode::Privileged
31                } else {
32                    ResolvedMode::Unprivileged
33                }
34            }
35        }
36    }
37}
38
39/// A resolved (non-Auto) privilege mode
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum ResolvedMode {
42    Unprivileged,
43    Privileged,
44}
45
46impl ResolvedMode {
47    pub fn is_privileged(&self) -> bool {
48        matches!(self, ResolvedMode::Privileged)
49    }
50
51    pub fn is_unprivileged(&self) -> bool {
52        matches!(self, ResolvedMode::Unprivileged)
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn default_is_auto() {
62        assert_eq!(PrivilegeMode::default(), PrivilegeMode::Auto);
63    }
64
65    #[test]
66    fn privileged_always_resolves_to_privileged() {
67        let caps = SystemCapabilities {
68            has_root: false,
69            has_user_namespaces: false,
70            has_seccomp: false,
71            has_landlock: false,
72            has_cgroup_v2: false,
73            has_cgroup_delegation: false,
74        };
75        assert_eq!(
76            PrivilegeMode::Privileged.resolve(&caps),
77            ResolvedMode::Privileged
78        );
79    }
80
81    #[test]
82    fn unprivileged_always_resolves_to_unprivileged() {
83        let caps = SystemCapabilities {
84            has_root: true,
85            has_user_namespaces: true,
86            has_seccomp: true,
87            has_landlock: true,
88            has_cgroup_v2: true,
89            has_cgroup_delegation: true,
90        };
91        assert_eq!(
92            PrivilegeMode::Unprivileged.resolve(&caps),
93            ResolvedMode::Unprivileged
94        );
95    }
96
97    #[test]
98    fn auto_resolves_to_privileged_when_root_with_cgroups() {
99        let caps = SystemCapabilities {
100            has_root: true,
101            has_user_namespaces: true,
102            has_seccomp: true,
103            has_landlock: true,
104            has_cgroup_v2: true,
105            has_cgroup_delegation: true,
106        };
107        assert_eq!(PrivilegeMode::Auto.resolve(&caps), ResolvedMode::Privileged);
108    }
109
110    #[test]
111    fn auto_resolves_to_unprivileged_without_root() {
112        let caps = SystemCapabilities {
113            has_root: false,
114            has_user_namespaces: true,
115            has_seccomp: true,
116            has_landlock: true,
117            has_cgroup_v2: true,
118            has_cgroup_delegation: false,
119        };
120        assert_eq!(
121            PrivilegeMode::Auto.resolve(&caps),
122            ResolvedMode::Unprivileged
123        );
124    }
125
126    #[test]
127    fn resolved_mode_helpers() {
128        assert!(ResolvedMode::Privileged.is_privileged());
129        assert!(!ResolvedMode::Privileged.is_unprivileged());
130        assert!(ResolvedMode::Unprivileged.is_unprivileged());
131        assert!(!ResolvedMode::Unprivileged.is_privileged());
132    }
133}