nucleus/isolation/
usermap.rs1use crate::error::{NucleusError, Result};
2use std::fs;
3use tracing::{debug, info};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct IdMapping {
10 pub container_id: u32,
12 pub host_id: u32,
14 pub count: u32,
16}
17
18impl IdMapping {
19 pub fn new(container_id: u32, host_id: u32, count: u32) -> Self {
21 Self {
22 container_id,
23 host_id,
24 count,
25 }
26 }
27
28 pub fn rootless() -> Self {
30 let uid = nix::unistd::getuid().as_raw();
31 Self::new(0, uid, 1)
32 }
33
34 fn format(&self) -> String {
36 format!("{} {} {}\n", self.container_id, self.host_id, self.count)
37 }
38}
39
40#[derive(Debug, Clone)]
42pub struct UserNamespaceConfig {
43 pub uid_mappings: Vec<IdMapping>,
45 pub gid_mappings: Vec<IdMapping>,
47}
48
49impl UserNamespaceConfig {
50 pub fn rootless() -> Self {
54 let uid = nix::unistd::getuid().as_raw();
55 let gid = nix::unistd::getgid().as_raw();
56
57 Self {
58 uid_mappings: vec![IdMapping::new(0, uid, 1)],
59 gid_mappings: vec![IdMapping::new(0, gid, 1)],
60 }
61 }
62
63 pub fn custom(uid_mappings: Vec<IdMapping>, gid_mappings: Vec<IdMapping>) -> Self {
65 Self {
66 uid_mappings,
67 gid_mappings,
68 }
69 }
70}
71
72pub struct UserNamespaceMapper {
76 config: UserNamespaceConfig,
77}
78
79impl UserNamespaceMapper {
80 pub fn new(config: UserNamespaceConfig) -> Self {
81 Self { config }
82 }
83
84 pub fn setup_mappings(&self) -> Result<()> {
89 info!("Setting up user namespace mappings");
90
91 self.write_setgroups_deny()?;
93
94 self.write_uid_map()?;
96
97 self.write_gid_map()?;
99
100 info!("Successfully configured user namespace mappings");
101 Ok(())
102 }
103
104 fn write_setgroups_deny(&self) -> Result<()> {
108 let path = "/proc/self/setgroups";
109 debug!("Writing 'deny' to {}", path);
110
111 fs::write(path, "deny\n").map_err(|e| {
112 NucleusError::NamespaceError(format!("Failed to write to {}: {}", path, e))
113 })?;
114
115 Ok(())
116 }
117
118 fn write_uid_map(&self) -> Result<()> {
120 let path = "/proc/self/uid_map";
121 let mut content = String::new();
122
123 for mapping in &self.config.uid_mappings {
124 content.push_str(&mapping.format());
125 }
126
127 debug!("Writing UID mappings to {}: {}", path, content.trim());
128
129 fs::write(path, &content).map_err(|e| {
130 NucleusError::NamespaceError(format!("Failed to write UID mappings: {}", e))
131 })?;
132
133 Ok(())
134 }
135
136 fn write_gid_map(&self) -> Result<()> {
138 let path = "/proc/self/gid_map";
139 let mut content = String::new();
140
141 for mapping in &self.config.gid_mappings {
142 content.push_str(&mapping.format());
143 }
144
145 debug!("Writing GID mappings to {}: {}", path, content.trim());
146
147 fs::write(path, &content).map_err(|e| {
148 NucleusError::NamespaceError(format!("Failed to write GID mappings: {}", e))
149 })?;
150
151 Ok(())
152 }
153
154 pub fn config(&self) -> &UserNamespaceConfig {
156 &self.config
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn test_id_mapping_format() {
166 let mapping = IdMapping::new(0, 1000, 1);
167 assert_eq!(mapping.format(), "0 1000 1\n");
168
169 let mapping = IdMapping::new(1000, 2000, 100);
170 assert_eq!(mapping.format(), "1000 2000 100\n");
171 }
172
173 #[test]
174 fn test_id_mapping_rootless() {
175 let mapping = IdMapping::rootless();
176 assert_eq!(mapping.container_id, 0);
177 assert_eq!(mapping.count, 1);
178 }
180
181 #[test]
182 fn test_user_namespace_config_rootless() {
183 let config = UserNamespaceConfig::rootless();
184 assert_eq!(config.uid_mappings.len(), 1);
185 assert_eq!(config.gid_mappings.len(), 1);
186 assert_eq!(config.uid_mappings[0].container_id, 0);
187 assert_eq!(config.gid_mappings[0].container_id, 0);
188 }
189
190 #[test]
191 fn test_user_namespace_config_custom() {
192 let uid_mappings = vec![IdMapping::new(0, 1000, 1), IdMapping::new(1000, 2000, 100)];
193 let gid_mappings = vec![IdMapping::new(0, 1000, 1)];
194
195 let config = UserNamespaceConfig::custom(uid_mappings.clone(), gid_mappings.clone());
196 assert_eq!(config.uid_mappings, uid_mappings);
197 assert_eq!(config.gid_mappings, gid_mappings);
198 }
199
200 }