使用CLONE_NEWUSER克隆进程后,如何切换至root?UID非0是否正常?
关于CLONE_NEWUSER克隆后UID为65534的问题及切换root的方法
问题描述
- 使用
CLONE_NEWUSER标志克隆进程后,子进程UID显示为65534而非0,这是否属于预期行为? - 克隆后是否可以将当前用户切换至root?如何操作?
代码示例
let mut stack = [0u8; 1024 * 1024]; let flags = CloneFlags::empty() .union(CloneFlags::CLONE_NEWUSER) .union(CloneFlags::CLONE_NEWNET) .union(CloneFlags::CLONE_NEWPID) .union(CloneFlags::CLONE_NEWNS); let pid = unsafe { nix::sched::clone( Box::new(|| match run_container(cxt.clone(), container.clone()) { Ok(()) => 0, Err(e) => { tracing::error!("Failed to run container: {e}"); -1 } }), &mut stack, flags, // The SIGCHLD signal is required for wait/waitpid; // otherwise, ECHILD will be reported. Some(libc::SIGCHLD), )? }; ... fn run_container(cxt: cfg::Context, container: Container) -> ChariotResult<()> { tracing::debug!("Run sandbox <{}> in <{}> as <{}>.", container.name, getpid(), getuid()); ... }
调试日志
... 2024-10-15T14:55:47.267206Z DEBUG chariot::cmd::runc: Run sandbox <busybox> in <1> as <65534>. ...
解答
1. UID显示为65534是预期行为
当使用CLONE_NEWUSER创建新的用户命名空间时,子进程在新命名空间内的UID/GID映射默认未配置。此时内核会将未映射的外部UID自动映射到新命名空间内的65534(即nobody用户的UID),这是完全符合设计的预期行为。
2. 可以切换至root,需配置UID/GID映射
要在新用户命名空间内获得root权限,必须手动配置UID和GID映射,将外部用户的UID映射到新命名空间内的0(root)。具体操作如下:
步骤:在子进程初始化时添加映射配置
在run_container函数的最开始(执行任何权限操作前)添加映射配置代码,注意只有新命名空间的PID 1进程有权限修改这些文件:
use nix::unistd::{Uid, Gid}; use std::fs::File; use std::io::Write; fn run_container(cxt: cfg::Context, container: Container) -> ChariotResult<()> { // 1. 禁用setgroups(部分系统要求此步骤) let setgroups_path = "/proc/self/setgroups"; if let Ok(mut f) = File::create(setgroups_path) { writeln!(f, "deny")?; } // 2. 配置UID映射:将外部当前用户UID映射到新命名空间的root(0) let uid_map_path = "/proc/self/uid_map"; let current_uid = Uid::current().as_raw(); let mut uid_file = File::create(uid_map_path)?; writeln!(uid_file, "0 {} 1", current_uid)?; // 3. 配置GID映射,逻辑同UID let gid_map_path = "/proc/self/gid_map"; let current_gid = Gid::current().as_raw(); let mut gid_file = File::create(gid_map_path)?; writeln!(gid_file, "0 {} 1", current_gid)?; // 后续执行原有逻辑 tracing::debug!("Run sandbox <{}> in <{}> as <{}>.", container.name, getpid(), getuid()); ... }
配置说明
uid_map格式:内部UID 外部UID 映射范围,这里0 {} 1表示将外部当前用户的UID映射为新命名空间内的root(UID 0),映射范围为1个UID。gid_map配置逻辑与UID完全一致,确保GID也映射到新命名空间的root(GID 0)。setgroups设置为deny:部分Linux发行版要求先禁用setgroups才能成功写入GID映射,避免权限错误。
验证效果
配置完成后,再次调用getuid()会返回0,此时子进程在新用户命名空间内拥有完整的root权限,可以执行挂载、修改网络命名空间等需要root权限的操作。
额外注意事项
- 仅新用户命名空间的初始化进程(PID 1)有权限修改
uid_map、gid_map和setgroups文件,其他进程无法修改。 - 如果父进程是普通用户,需确保系统允许非root用户创建用户命名空间:检查
/proc/sys/kernel/unprivileged_userns_clone的值,若为0则需改为1(临时生效:echo 1 > /proc/sys/kernel/unprivileged_userns_clone,永久生效需修改/etc/sysctl.conf)。
内容的提问来源于stack exchange,提问作者Klaus Ma
相关产品推荐
相关产品推荐

