use crate::error::{NucleusError, Result, StateTransition};
use crate::isolation::state::NamespaceState;
use crate::isolation::usermap::{UserNamespaceConfig, UserNamespaceMapper};
use nix::mount::MsFlags;
use nix::sched::{unshare, CloneFlags};
use nix::unistd::sethostname;
use tracing::{debug, info};
#[derive(Debug, Clone)]
pub struct NamespaceConfig {
pub pid: bool,
pub mnt: bool,
pub net: bool,
pub uts: bool,
pub ipc: bool,
pub cgroup: bool,
pub user: bool,
pub time: bool,
}
impl NamespaceConfig {
pub fn all() -> Self {
Self {
pid: true,
mnt: true,
net: true,
uts: true,
ipc: true,
cgroup: true,
user: false, time: false, }
}
pub fn minimal() -> Self {
Self {
pid: true,
mnt: true,
net: true,
uts: true,
ipc: true,
cgroup: true,
user: false,
time: false,
}
}
pub fn with_cgroup_namespace(mut self, enabled: bool) -> Self {
self.cgroup = enabled;
self
}
pub fn with_time_namespace(mut self, enabled: bool) -> Self {
self.time = enabled;
self
}
fn to_clone_flags(&self) -> CloneFlags {
let mut flags = CloneFlags::empty();
if self.pid {
flags |= CloneFlags::CLONE_NEWPID;
}
if self.mnt {
flags |= CloneFlags::CLONE_NEWNS;
}
if self.net {
flags |= CloneFlags::CLONE_NEWNET;
}
if self.uts {
flags |= CloneFlags::CLONE_NEWUTS;
}
if self.ipc {
flags |= CloneFlags::CLONE_NEWIPC;
}
if self.cgroup {
flags |= CloneFlags::CLONE_NEWCGROUP;
}
if self.user {
flags |= CloneFlags::CLONE_NEWUSER;
}
if self.time {
flags |= CloneFlags::from_bits_retain(libc::CLONE_NEWTIME);
}
flags
}
}
impl Default for NamespaceConfig {
fn default() -> Self {
Self::all()
}
}
pub struct NamespaceManager {
config: NamespaceConfig,
state: NamespaceState,
user_mapper: Option<UserNamespaceMapper>,
}
impl NamespaceManager {
pub fn new(config: NamespaceConfig) -> Self {
Self {
config,
state: NamespaceState::Uninitialized,
user_mapper: None,
}
}
pub fn with_user_mapping(mut self, user_config: UserNamespaceConfig) -> Self {
self.user_mapper = Some(UserNamespaceMapper::new(user_config));
self
}
pub fn unshare_namespaces(&mut self) -> Result<()> {
if self.state != NamespaceState::Uninitialized {
debug!("Namespaces already created, skipping");
return Ok(());
}
info!("Creating namespaces: {:?}", self.config);
let flags = self.config.to_clone_flags();
unshare(flags).map_err(|e| {
NucleusError::NamespaceError(format!("Failed to unshare namespaces: {}", e))
})?;
if self.config.mnt {
nix::mount::mount(
None::<&str>,
"/",
None::<&str>,
MsFlags::MS_REC | MsFlags::MS_PRIVATE,
None::<&str>,
)
.map_err(|e| {
NucleusError::NamespaceError(format!(
"Failed to set mount propagation to private: {}",
e
))
})?;
}
if self.config.user {
if let Some(mapper) = &self.user_mapper {
info!("Setting up user namespace UID/GID mappings");
mapper.setup_mappings()?;
} else {
debug!("User namespace enabled but no mapper configured");
}
}
self.state = self.state.transition(NamespaceState::Unshared)?;
info!("Successfully created namespaces");
Ok(())
}
pub fn is_unshared(&self) -> bool {
self.state != NamespaceState::Uninitialized
}
pub fn enter(&mut self) -> Result<()> {
self.state = self.state.transition(NamespaceState::Entered)?;
debug!("Namespace state: {:?}", self.state);
Ok(())
}
pub fn state(&self) -> NamespaceState {
self.state
}
pub fn config(&self) -> &NamespaceConfig {
&self.config
}
pub fn set_hostname(&self, hostname: &str) -> Result<()> {
if !self.config.uts {
debug!("UTS namespace not enabled, skipping hostname setting");
return Ok(());
}
info!("Setting hostname to: {}", hostname);
sethostname(hostname)
.map_err(|e| NucleusError::NamespaceError(format!("Failed to set hostname: {}", e)))?;
info!("Successfully set hostname to: {}", hostname);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_namespace_config_all() {
let config = NamespaceConfig::all();
assert!(config.pid);
assert!(config.mnt);
assert!(config.net);
assert!(config.uts);
assert!(config.ipc);
assert!(config.cgroup);
assert!(!config.user); assert!(!config.time);
}
#[test]
fn test_namespace_config_minimal() {
let config = NamespaceConfig::minimal();
assert!(config.pid);
assert!(config.mnt);
assert!(config.net);
assert!(config.uts); assert!(config.ipc); assert!(config.cgroup);
assert!(!config.user);
assert!(!config.time);
}
#[test]
fn test_namespace_manager_initial_state() {
let mgr = NamespaceManager::new(NamespaceConfig::minimal());
assert!(!mgr.is_unshared());
}
}