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

User Namespace中UID设置成功但GID仍为65534,如何解决?

解决User Namespace中GID映射失败的问题

我尝试在User Namespace中设置UID和GID映射,代码成功将命名空间内的UID设置为0,但GID始终保持为65534(nobody用户组)。

原代码

#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/sched.h>
#include <sched.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/utsname.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/capability.h>

#define STACK_SIZE 0x10000

int x(void *arg) {
  sleep(3);
  system("id");
  return 0;
}

int main() {
  char uid_map[64] = {0};
  char gid_map[64] = {0};

  char* stack = malloc(STACK_SIZE);

  int pid = clone(x, stack + STACK_SIZE, CLONE_NEWUSER | SIGCHLD, NULL);
  if(pid == -1) return -1;

  snprintf(uid_map, 64, "/proc/%d/uid_map", pid);
  snprintf(gid_map, 64, "/proc/%d/gid_map", pid);
  int uid_fd = open(uid_map, O_RDWR|O_CREAT);
  int gid_fd = open(gid_map, O_RDWR|O_CREAT);

  printf("%d %d\n", uid_fd, gid_fd);

  write(uid_fd, "0 1000 1\n", 9);
  write(gid_fd, "0 1000 1\n", 9);

  waitpid(pid, NULL, 0);

  return 0;
}

原运行输出

3 4
uid=0(root) gid=65534(nobody) groups=65534(nobody)

问题原因

GID映射失败是内核的安全限制导致:非特权用户在写入gid_map之前,必须先禁用setgroups功能。默认情况下,新创建的User Namespace中setgroups处于启用状态,此时直接写入gid_map会被内核拒绝。

解决方法

在写入gid_map之前,先打开/proc/[pid]/setgroups文件并写入deny,禁用该功能后再进行GID映射。同时建议添加错误检查,避免忽略系统调用的失败信息。

修改后的代码

#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/sched.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/capability.h>

#define STACK_SIZE 0x10000

int x(void *arg) {
    sleep(3);
    system("id");
    return 0;
}

int main() {
    char uid_map[64] = {0};
    char gid_map[64] = {0};
    char setgroups_path[64] = {0};

    char* stack = malloc(STACK_SIZE);
    if (!stack) {
        perror("malloc failed");
        return -1;
    }

    int pid = clone(x, stack + STACK_SIZE, CLONE_NEWUSER | SIGCHLD, NULL);
    if (pid == -1) {
        perror("clone failed");
        free(stack);
        return -1;
    }

    snprintf(uid_map, 64, "/proc/%d/uid_map", pid);
    snprintf(gid_map, 64, "/proc/%d/gid_map", pid);
    snprintf(setgroups_path, 64, "/proc/%d/setgroups", pid);

    // 先禁用setgroups,才能写入gid_map
    int setgroups_fd = open(setgroups_path, O_WRONLY);
    if (setgroups_fd != -1) {
        write(setgroups_fd, "deny", 4);
        close(setgroups_fd);
    } else {
        // 部分内核版本可能无setgroups文件,可忽略该错误
        perror("open setgroups failed");
    }

    int uid_fd = open(uid_map, O_WRONLY);
    int gid_fd = open(gid_map, O_WRONLY);

    if (uid_fd == -1 || gid_fd == -1) {
        perror("open map files failed");
        close(uid_fd);
        close(gid_fd);
        free(stack);
        return -1;
    }

    ssize_t uid_ret = write(uid_fd, "0 1000 1\n", 9);
    ssize_t gid_ret = write(gid_fd, "0 1000 1\n", 9);

    if (uid_ret != 9 || gid_ret != 9) {
        perror("write map failed");
    }

    close(uid_fd);
    close(gid_fd);
    waitpid(pid, NULL, 0);
    free(stack);

    return 0;
}

关键改动说明

  • 新增/proc/[pid]/setgroups处理:写入deny禁用该功能,解除内核对GID映射的限制
  • 添加内存分配、系统调用的错误检查,便于排查问题
  • 将open模式改为O_WRONLY,映射文件由clone自动生成,原代码的O_CREAT是多余操作

修改后运行输出

uid=0(root) gid=0(root) groups=0(root)

内容的提问来源于stack exchange,提问作者BitFriends

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 14:15:40