You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 08:32:16