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 {
52 Self {
53 pid: true,
54 mnt: true,
55 net: true,
56 uts: true,
57 ipc: true,
58 cgroup: true,
59 user: false,
60 time: false,
61 }
62 }
63
64 pub fn with_cgroup_namespace(mut self, enabled: bool) -> Self {
66 self.cgroup = enabled;
67 self
68 }
69
70 pub fn with_time_namespace(mut self, enabled: bool) -> Self {
72 self.time = enabled;
73 self
74 }
75
76 fn to_clone_flags(&self) -> CloneFlags {
78 let mut flags = CloneFlags::empty();
79
80 if self.pid {
81 flags |= CloneFlags::CLONE_NEWPID;
82 }
83 if self.mnt {
84 flags |= CloneFlags::CLONE_NEWNS;
85 }
86 if self.net {
87 flags |= CloneFlags::CLONE_NEWNET;
88 }
89 if self.uts {
90 flags |= CloneFlags::CLONE_NEWUTS;
91 }
92 if self.ipc {
93 flags |= CloneFlags::CLONE_NEWIPC;
94 }
95 if self.cgroup {
96 flags |= CloneFlags::CLONE_NEWCGROUP;
97 }
98 if self.user {
99 flags |= CloneFlags::CLONE_NEWUSER;
100 }
101 if self.time {
102 flags |= CloneFlags::from_bits_retain(libc::CLONE_NEWTIME);
103 }
104
105 flags
106 }
107}
108
109impl Default for NamespaceConfig {
110 fn default() -> Self {
111 Self::all()
112 }
113}
114
115pub struct NamespaceManager {
120 config: NamespaceConfig,
121 state: NamespaceState,
122 user_mapper: Option<UserNamespaceMapper>,
123}
124
125impl NamespaceManager {
126 pub fn new(config: NamespaceConfig) -> Self {
127 Self {
128 config,
129 state: NamespaceState::Uninitialized,
130 user_mapper: None,
131 }
132 }
133
134 pub fn with_user_mapping(mut self, user_config: UserNamespaceConfig) -> Self {
136 self.user_mapper = Some(UserNamespaceMapper::new(user_config));
137 self
138 }
139
140 pub fn unshare_namespaces(&mut self) -> Result<()> {
145 if self.state != NamespaceState::Uninitialized {
146 debug!("Namespaces already created, skipping");
147 return Ok(());
148 }
149
150 info!("Creating namespaces: {:?}", self.config);
151
152 let flags = self.config.to_clone_flags();
153
154 unshare(flags).map_err(|e| {
155 NucleusError::NamespaceError(format!("Failed to unshare namespaces: {}", e))
156 })?;
157
158 if self.config.mnt {
160 nix::mount::mount(
161 None::<&str>,
162 "/",
163 None::<&str>,
164 MsFlags::MS_REC | MsFlags::MS_PRIVATE,
165 None::<&str>,
166 )
167 .map_err(|e| {
168 NucleusError::NamespaceError(format!(
169 "Failed to set mount propagation to private: {}",
170 e
171 ))
172 })?;
173 }
174
175 if self.config.user {
178 if let Some(mapper) = &self.user_mapper {
179 info!("Setting up user namespace UID/GID mappings");
180 mapper.setup_mappings()?;
181 } else {
182 debug!("User namespace enabled but no mapper configured");
183 }
184 }
185
186 self.state = self.state.transition(NamespaceState::Unshared)?;
188 info!("Successfully created namespaces");
189
190 Ok(())
191 }
192
193 pub fn is_unshared(&self) -> bool {
195 self.state != NamespaceState::Uninitialized
196 }
197
198 pub fn enter(&mut self) -> Result<()> {
202 self.state = self.state.transition(NamespaceState::Entered)?;
203 debug!("Namespace state: {:?}", self.state);
204 Ok(())
205 }
206
207 pub fn state(&self) -> NamespaceState {
209 self.state
210 }
211
212 pub fn config(&self) -> &NamespaceConfig {
214 &self.config
215 }
216
217 pub fn set_hostname(&self, hostname: &str) -> Result<()> {
221 if !self.config.uts {
222 debug!("UTS namespace not enabled, skipping hostname setting");
223 return Ok(());
224 }
225
226 info!("Setting hostname to: {}", hostname);
227
228 sethostname(hostname)
229 .map_err(|e| NucleusError::NamespaceError(format!("Failed to set hostname: {}", e)))?;
230
231 info!("Successfully set hostname to: {}", hostname);
232
233 Ok(())
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_namespace_config_all() {
243 let config = NamespaceConfig::all();
244 assert!(config.pid);
245 assert!(config.mnt);
246 assert!(config.net);
247 assert!(config.uts);
248 assert!(config.ipc);
249 assert!(config.cgroup);
250 assert!(!config.user); assert!(!config.time);
252 }
253
254 #[test]
255 fn test_namespace_config_minimal() {
256 let config = NamespaceConfig::minimal();
257 assert!(config.pid);
258 assert!(config.mnt);
259 assert!(config.net);
260 assert!(config.uts); assert!(config.ipc); assert!(config.cgroup);
263 assert!(!config.user);
264 assert!(!config.time);
265 }
266
267 #[test]
268 fn test_namespace_manager_initial_state() {
269 let mgr = NamespaceManager::new(NamespaceConfig::minimal());
270 assert!(!mgr.is_unshared());
271 }
272
273 }