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
相关产品推荐
相关产品推荐

