nucleus/isolation/
namespaces.rs1use crate::error::{NucleusError, Result, StateTransition};
2use crate::isolation::state::NamespaceState;
3use crate::isolation::usermap::{UserNamespaceConfig, UserNamespaceMapper};
4use nix::mount::MsFlags;
5use nix::sched::{unshare, CloneFlags};
6use nix::unistd::sethostname;
7use tracing::{debug, info};
8
9#[derive(Debug, Clone)]
13pub struct NamespaceConfig {
14 pub pid: bool,
16 pub mnt: bool,
18 pub net: bool,
20 pub uts: bool,
22 pub ipc: bool,
24 pub cgroup: bool,
26 pub user: bool,
28 pub time: bool,
30}
31
32impl NamespaceConfig {
33 pub fn all() -> Self {
35 Self {
36 pid: true,
37 mnt: true,
38 net: true,
39 uts: true,
40 ipc: true,
41 cgroup: true,
42 user: false, time: false, }
45 }
46
47 pub fn minimal() -> Self {
49 Self {
50 pid: true,
51 mnt: true,
52 net: true,
53 uts: false,
54 ipc: false,
55 cgroup: true,
56 user: false,
57 time: false,
58 }
59 }
60
61 pub fn with_cgroup_namespace(mut self, enabled: bool) -> Self {
63 self.cgroup = enabled;
64 self
65 }
66
67 pub fn with_time_namespace(mut self, enabled: bool) -> Self {
69 self.time = enabled;
70 self
71 }
72
73 fn to_clone_flags(&self) -> CloneFlags {
75 let mut flags = CloneFlags::empty();
76
77 if self.pid {
78 flags |= CloneFlags::CLONE_NEWPID;
79 }
80 if self.mnt {
81 flags |= CloneFlags::CLONE_NEWNS;
82 }
83 if self.net {
84 flags |= CloneFlags::CLONE_NEWNET;
85 }
86 if self.uts {
87 flags |= CloneFlags::CLONE_NEWUTS;
88 }
89 if self.ipc {
90 flags |= CloneFlags::CLONE_NEWIPC;
91 }
92 if self.cgroup {
93 flags |= CloneFlags::CLONE_NEWCGROUP;
94 }
95 if self.user {
96 flags |= CloneFlags::CLONE_NEWUSER;
97 }
98 if self.time {
99 flags |= CloneFlags::from_bits_retain(libc::CLONE_NEWTIME);
100 }
101
102 flags
103 }
104}
105
106impl Default for NamespaceConfig {
107 fn default() -> Self {
108 Self::all()
109 }
110}
111
112pub struct NamespaceManager {
117 config: NamespaceConfig,
118 state: NamespaceState,
119 user_mapper: Option<UserNamespaceMapper>,
120}
121
122impl NamespaceManager {
123 pub fn new(config: NamespaceConfig) -> Self {
124 Self {
125 config,
126 state: NamespaceState::Uninitialized,
127 user_mapper: None,
128 }
129 }
130
131 pub fn with_user_mapping(mut self, user_config: UserNamespaceConfig) -> Self {
133 self.user_mapper = Some(UserNamespaceMapper::new(user_config));
134 self
135 }
136
137 pub fn unshare_namespaces(&mut self) -> Result<()> {
142 if self.state != NamespaceState::Uninitialized {
143 debug!("Namespaces already created, skipping");
144 return Ok(());
145 }
146
147 info!("Creating namespaces: {:?}", self.config);
148
149 let flags = self.config.to_clone_flags();
150
151 unshare(flags).map_err(|e| {
152 NucleusError::NamespaceError(format!("Failed to unshare namespaces: {}", e))
153 })?;
154
155 if self.config.mnt {
157 nix::mount::mount(
158 None::<&str>,
159 "/",
160 None::<&str>,
161 MsFlags::MS_REC | MsFlags::MS_PRIVATE,
162 None::<&str>,
163 )
164 .map_err(|e| {
165 NucleusError::NamespaceError(format!(
166 "Failed to set mount propagation to private: {}",
167 e
168 ))
169 })?;
170 }
171
172 if self.config.user {
175 if let Some(mapper) = &self.user_mapper {
176 info!("Setting up user namespace UID/GID mappings");
177 mapper.setup_mappings()?;
178 } else {
179 debug!("User namespace enabled but no mapper configured");
180 }
181 }
182
183 self.state = self.state.transition(NamespaceState::Unshared)?;
185 info!("Successfully created namespaces");
186
187 Ok(())
188 }
189
190 pub fn is_unshared(&self) -> bool {
192 self.state != NamespaceState::Uninitialized
193 }
194
195 pub fn enter(&mut self) -> Result<()> {
199 self.state = self.state.transition(NamespaceState::Entered)?;
200 debug!("Namespace state: {:?}", self.state);
201 Ok(())
202 }
203
204 pub fn state(&self) -> NamespaceState {
206 self.state
207 }
208
209 pub fn config(&self) -> &NamespaceConfig {
211 &self.config
212 }
213
214 pub fn set_hostname(&self, hostname: &str) -> Result<()> {
218 if !self.config.uts {
219 debug!("UTS namespace not enabled, skipping hostname setting");
220 return Ok(());
221 }
222
223 info!("Setting hostname to: {}", hostname);
224
225 sethostname(hostname)
226 .map_err(|e| NucleusError::NamespaceError(format!("Failed to set hostname: {}", e)))?;
227
228 info!("Successfully set hostname to: {}", hostname);
229
230 Ok(())
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 #[test]
239 fn test_namespace_config_all() {
240 let config = NamespaceConfig::all();
241 assert!(config.pid);
242 assert!(config.mnt);
243 assert!(config.net);
244 assert!(config.uts);
245 assert!(config.ipc);
246 assert!(config.cgroup);
247 assert!(!config.user); assert!(!config.time);
249 }
250
251 #[test]
252 fn test_namespace_config_minimal() {
253 let config = NamespaceConfig::minimal();
254 assert!(config.pid);
255 assert!(config.mnt);
256 assert!(config.net);
257 assert!(!config.uts);
258 assert!(!config.ipc);
259 assert!(config.cgroup);
260 assert!(!config.user);
261 assert!(!config.time);
262 }
263
264 #[test]
265 fn test_namespace_manager_initial_state() {
266 let mgr = NamespaceManager::new(NamespaceConfig::minimal());
267 assert!(!mgr.is_unshared());
268 }
269
270 }