user_namespaces(7) Miscellaneous Information Manual user_namespaces(7) user_namespaces - namespaces(7). ( credentials(7)) ( keyrings(7)) ( capabilities(7)). . 0 . -- ("")-- . unshare(2) clone(2) CLONE_NEWUSER. ( 3.11) 32 . unshare(2) clone(2) EUSERS. . fork(2) clone(2) CLONE_NEWUSER . setns(2) CAP_SYS_ADMIN . clone(2) unshare(2) CLONE_NEWUSER ( clone(2)) ( unshare(2)) . NS_GET_PARENT ioctl(2) ioctl_nsfs(2). /proc/sys/fs/suid_dumpable. proc . PR_SET_DUMPABLE prctl(2) . prctl(2) proc(5) . clone(2) CLONE_NEWUSER . unshare(2) setns(2) . ( clone(2)) ( unshare(2) setns(2)) ( 0 ). execve(2) ( capabilities(7)). 0 . . clone(2) unshare(2) CLONE_NEWUSER setns(2) "securebits" ( capabilities(7)) ( ) ( clone(2)) ( unshare(2) setns(2)). setns(2) "securebits" setns(2) . : o . . set-user-ID . clone(2) unshare(2) setns(2) . o ( ) . o "" . . . NS_GET_OWNER_UID ioctl(2) ioctl_nsfs(2). ( ) . ( ) () ( ). ( ) ( CAP_SYS_TIME) ( CAP_SYS_MODULE) ( CAP_MKNOD). . CAP_SYS_ADMIN : o /proc ( 3.8) o /sys ( 3.8) o devpts ( 3.9) o tmpfs(5) ( 3.9) o ramfs ( 3.9) o mqueue ( 3.9) o bpf ( 4.4) o overlayfs ( 5.11) CAP_SYS_ADMIN cgroup ( 4.6) cgroup 2 cgroup 1 ( cgroup "none,name="). CAP_SYS_ADMIN PID ( 3.8) /proc. CAP_SYS_ADMIN . 3.8 CAP_SYS_ADMIN . . . CLONE_NEWUSER CLONE_NEW* clone(2) unshare(2) (clone(2)) (unshare(2)) . . ( ) clone(2) unshare(2) . ( .) . (sethostname(2)) UTS. UTS (CAP_SYS_ADMIN) . NS_GET_USERNS ioctl(2) ioctl_nsfs(2). : uid_map gid_map ( ) . /proc/pid/uid_map /proc/pid/gid_map ( 3.5) pid. ( ) . uid_map gid_map " " " ". uid_map pid uid_map ( ). uid_map . uid_map 1--1 . ( .) . . . : (1) pid. (2) . uid_map pid : () : uid_map. () : pid. uid_map ( /proc/self/uid_map) . (3) . ( )-- getuid(2) getgid(2) stat(2)-- ( ) . . stat(2) . . uid_map (gid_map ) : $ cat /proc/$$/uid_map; 0 0 4294967295 0 0 ( ) 32 . 4294967295 ( -1 32 ) . : (uid_t) -1 ( setreuid(2)) " ". (uid_t) -1 . : uid_map gid_map uid_map . uid_map EPERM. gid_map. uid_map (gid_map) : o 0. o . o . 4.14 ( ) 5 . 4.16 340 . ( lseek(2) pwrite(2) ). o ( ) . ( 3.8) 1 2 . 3.9 . o . EINVAL. /proc/pid/uid_map (/proc/pid/gid_map) : o CAP_SETUID (CAP_SETGID) pid. o pid pid. o ( ) . o /proc/pid/uid_map UID 0 : () CAP_SETFCAP () CAP_SETFCAP . 5.12. UID 0 CAP_SETFCAP ( capabilities(7)) : (1) ( UID 0 UID 0 ) UID 0 . (2) CAP_SETFCAP ( ). o : () CAP_SETUID (CAP_SETGID) . o : ( ) . () : o uid_map (gid_map) ( ) ( ) . o . o gid_map setgroups(2) "deny" /proc/pid/setgroups ( ) gid_map. EPERM. : projid_map . ( setquota(8) quotactl(2).) /proc/pid/projid_map ( Linux 3.7). /proc/pid/projid_map uid_map write(2) EINVAL. /proc/pid/projid_map : o pid pid. o . write(2) EPERM. uid_map . gid_map . uid_map gid_map . setuid(2) setfsuid(2) setreuid(2) setresuid(2). setgid(2) setfsgid(2) setregid(2) setresgid(2) setgroups(2). "deny" /proc/pid/setgroups /proc/pid/gid_map setgroups(2) /proc/pid/gid_map CAP_SETGID . /proc/pid/setgroups /proc/pid/setgroups "allow" pid setgroups(2) "deny" setgroups(2) . /proc/pid/setgroups ( ) setgroups(2) /proc/pid/gid_map . ( CAP_SYS_ADMIN ) "allow" "deny" /proc/pid/gid_map. "deny" setgroups(2). /proc/pid/setgroups setgroups(2) /proc/pid/gid_map . setgroups(2) setgroups(2) setgroups(2) setgroups(2). "allow". /proc/pid/gid_map ( setgroups(2) ) setgroups(2) "deny" /proc/pid/setgroups ( EPERM). /proc/pid/setgroups . setgroups "deny" setgroups(2) ( "allow" ) . ( EPERM.) . /proc/pid/setgroups Linux 3.19 . "rwx---rwx". "group" "other". setgroups(2) . ( CAP_SETGID) setgroups(2). . . /proc/pid/setgroups setgroups(2). ( ) . getuid(2) . ( ) ( ) 65534. /proc/sys/kernel/overflowuid /proc/sys/kernel/overflowgid proc(5). (getuid(2) getgid(2) ) UNIX stat(2) waitid(2) "ctl" IPC_STAT V IPC /proc/pid/status /proc/sysvipc/* si_uid siginfo_t ( sigaction(2)) ( acct(5)) POSIX ( mq_notify(3)). . uid_map gid_map 4294967295 (-1 ). (UID GID) . System V IPC. . : CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_FSETID. : o o . CAP_FOWNER : ( ). set-user-ID set-group-ID set-user-ID (set-group-ID) () () . set-user-ID (set-group-ID) : () . ( set-user-ID set-group-ID MS_NOSUID mount(2).) UNIX ( SCM_CREDENTIALS unix(7)) . . Linux set-user-ID-root. . " " 0 . CONFIG_USER_NS. . . Linux 3.8 . Linux 3.9 (Plan 9 (9P) Andrew File System (AFS) Ceph CIFS CODA NFS OCFS2). Linux 3.12 XFS. . . usage() . . : $ uname -rs; # Need Linux 3.8 or later Linux 3.8.0 $ id -u; # Running as unprivileged user 1000 $ id -g; 1000 (-U) (-m) PID (-p) (-M) (-G) 1000 0 : $ ./userns_child_exec -p -m -U -M '0 1000 1' -G '0 1000 1' bash; PID 1 PID : bash$ echo $$; 1 /proc PID PID: bash$ mount -t proc proc /proc; bash$ ps ax; PID TTY STAT TIME COMMAND 1 pts/3 S 0:00 bash 22 pts/3 R+ 0:00 ps ax 0 : bash$ cat /proc/$$/status | egrep '^[UG]id'; Uid: 0 0 0 0 Gid: 0 0 0 0 bash$ cat /proc/$$/status | egrep '^Cap(Prm|Inh|Eff)'; CapInh: 0000000000000000 CapPrm: 0000001fffffffff CapEff: 0000001fffffffff /* userns_child_exec.c Licensed under GNU General Public License v2 or later Create a child process that executes a shell command in new namespace(s); allow UID and GID mappings to be specified when creating a user namespace. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include struct child_args { char **argv; /* Command to be executed by child, with args */ int pipe_fd[2]; /* Pipe used to synchronize parent and child */ }; static int verbose; static void usage(char *pname) { fprintf(stderr, "Usage: %s [options] cmd [arg...]\n\n", pname); fprintf(stderr, "Create a child process that executes a shell " "command in a new user namespace,\n" "and possibly also other new namespace(s).\n\n"); fprintf(stderr, "Options can be:\n\n"); #define fpe(str) fprintf(stderr, " %s", str); fpe("-i New IPC namespace\n"); fpe("-m New mount namespace\n"); fpe("-n New network namespace\n"); fpe("-p New PID namespace\n"); fpe("-u New UTS namespace\n"); fpe("-U New user namespace\n"); fpe("-M uid_map Specify UID map for user namespace\n"); fpe("-G gid_map Specify GID map for user namespace\n"); fpe("-z Map user's UID and GID to 0 in user namespace\n"); fpe(" (equivalent to: -M '0 1' -G '0 1')\n"); fpe("-v Display verbose messages\n"); fpe("\n"); fpe("If -z, -M, or -G is specified, -U is required.\n"); fpe("It is not permitted to specify both -z and either -M or -G.\n"); fpe("\n"); fpe("Map strings for -M and -G consist of records of the form:\n"); fpe("\n"); fpe(" ID-inside-ns ID-outside-ns size\n"); fpe("\n"); fpe("A map string can contain multiple records, separated" " by commas;\n"); fpe("the commas are replaced by newlines before writing" " to map files.\n"); exit(EXIT_FAILURE); } /* Update the mapping file 'map_file', with the value provided in 'mapping', a string that defines a UID or GID mapping. A UID or GID mapping consists of one or more newline-delimited records of the form: ID_inside-ns ID-outside-ns size Requiring the user to supply a string that contains newlines is of course inconvenient for command-line use. Thus, we permit the use of commas to delimit records in this string, and replace them with newlines before writing the string to the file. */ static void update_map(char *mapping, char *map_file) { int fd; size_t map_len; /* Length of 'mapping' */ /* Replace commas in mapping string with newlines. */ map_len = strlen(mapping); for (size_t j = 0; j < map_len; j++) if (mapping[j] == ',') mapping[j] = '\n'; fd = open(map_file, O_RDWR); if (fd == -1) { fprintf(stderr, "ERROR: open %s: %s\n", map_file, strerror(errno)); exit(EXIT_FAILURE); } if (write(fd, mapping, map_len) != map_len) { fprintf(stderr, "ERROR: write %s: %s\n", map_file, strerror(errno)); exit(EXIT_FAILURE); } close(fd); } /* Linux 3.19 made a change in the handling of setgroups(2) and the 'gid_map' file to address a security issue. The issue allowed *unprivileged* users to employ user namespaces in order to drop groups. The upshot of the 3.19 changes is that in order to update the 'gid_maps' file, use of the setgroups() system call in this user namespace must first be disabled by writing "deny" to one of the /proc/PID/setgroups files for this namespace. That is the purpose of the following function. */ static void proc_setgroups_write(pid_t child_pid, char *str) { char setgroups_path[PATH_MAX]; int fd; snprintf(setgroups_path, PATH_MAX, "/proc/%jd/setgroups", (intmax_t) child_pid); fd = open(setgroups_path, O_RDWR); if (fd == -1) { /* We may be on a system that doesn't support /proc/PID/setgroups. In that case, the file won't exist, and the system won't impose the restrictions that Linux 3.19 added. That's fine: we don't need to do anything in order to permit 'gid_map' to be updated. However, if the error from open() was something other than the ENOENT error that is expected for that case, let the user know. */ if (errno != ENOENT) fprintf(stderr, "ERROR: open %s: %s\n", setgroups_path, strerror(errno)); return; } if (write(fd, str, strlen(str)) == -1) fprintf(stderr, "ERROR: write %s: %s\n", setgroups_path, strerror(errno)); close(fd); } static int /* Start function for cloned child */ childFunc(void *arg) { struct child_args *args = arg; char ch; /* Wait until the parent has updated the UID and GID mappings. See the comment in main(). We wait for end of file on a pipe that will be closed by the parent process once it has updated the mappings. */ close(args->pipe_fd[1]); /* Close our descriptor for the write end of the pipe so that we see EOF when parent closes its descriptor. */ if (read(args->pipe_fd[0], &ch, 1) != 0) { fprintf(stderr, "Failure in child: read from pipe returned != 0\n"); exit(EXIT_FAILURE); } close(args->pipe_fd[0]); /* Execute a shell command. */ printf("About to exec %s\n", args->argv[0]); execvp(args->argv[0], args->argv); err(EXIT_FAILURE, "execvp"); } #define STACK_SIZE (1024 * 1024) static char child_stack[STACK_SIZE]; /* Space for child's stack */ int main(int argc, char *argv[]) { int flags, opt, map_zero; pid_t child_pid; struct child_args args; char *uid_map, *gid_map; const int MAP_BUF_SIZE = 100; char map_buf[MAP_BUF_SIZE]; char map_path[PATH_MAX]; /* Parse command-line options. The initial '+' character in the final getopt() argument prevents GNU-style permutation of command-line options. That's useful, since sometimes the 'command' to be executed by this program itself has command-line options. We don't want getopt() to treat those as options to this program. */ flags = 0; verbose = 0; gid_map = NULL; uid_map = NULL; map_zero = 0; while ((opt = getopt(argc, argv, "+imnpuUM:G:zv")) != -1) { switch (opt) { case 'i': flags |= CLONE_NEWIPC; break; case 'm': flags |= CLONE_NEWNS; break; case 'n': flags |= CLONE_NEWNET; break; case 'p': flags |= CLONE_NEWPID; break; case 'u': flags |= CLONE_NEWUTS; break; case 'v': verbose = 1; break; case 'z': map_zero = 1; break; case 'M': uid_map = optarg; break; case 'G': gid_map = optarg; break; case 'U': flags |= CLONE_NEWUSER; break; default: usage(argv[0]); } } /* -M or -G without -U is nonsensical */ if (((uid_map != NULL || gid_map != NULL || map_zero) && !(flags & CLONE_NEWUSER)) || (map_zero && (uid_map != NULL || gid_map != NULL))) usage(argv[0]); args.argv = &argv[optind]; /* We use a pipe to synchronize the parent and child, in order to ensure that the parent sets the UID and GID maps before the child calls execve(). This ensures that the child maintains its capabilities during the execve() in the common case where we want to map the child's effective user ID to 0 in the new user namespace. Without this synchronization, the child would lose its capabilities if it performed an execve() with nonzero user IDs (see the capabilities(7) man page for details of the transformation of a process's capabilities during execve()). */ if (pipe(args.pipe_fd) == -1) err(EXIT_FAILURE, "pipe"); /* Create the child in new namespace(s). */ child_pid = clone(childFunc, child_stack + STACK_SIZE, flags | SIGCHLD, &args); if (child_pid == -1) err(EXIT_FAILURE, "clone"); /* Parent falls through to here. */ if (verbose) printf("%s: PID of child created by clone() is %jd\n", argv[0], (intmax_t) child_pid); /* Update the UID and GID maps in the child. */ if (uid_map != NULL || map_zero) { snprintf(map_path, PATH_MAX, "/proc/%jd/uid_map", (intmax_t) child_pid); if (map_zero) { snprintf(map_buf, MAP_BUF_SIZE, "0 %jd 1", (intmax_t) getuid()); uid_map = map_buf; } update_map(uid_map, map_path); } if (gid_map != NULL || map_zero) { proc_setgroups_write(child_pid, "deny"); snprintf(map_path, PATH_MAX, "/proc/%jd/gid_map", (intmax_t) child_pid); if (map_zero) { snprintf(map_buf, MAP_BUF_SIZE, "0 %ld 1", (intmax_t) getgid()); gid_map = map_buf; } update_map(gid_map, map_path); } /* Close the write end of the pipe, to signal to the child that we have updated the UID and GID maps. */ close(args.pipe_fd[1]); if (waitpid(child_pid, NULL, 0) == -1) /* Wait for child */ err(EXIT_FAILURE, "waitpid"); if (verbose) printf("%s: terminating\n", argv[0]); exit(EXIT_SUCCESS); } newgidmap(1) newuidmap(1) clone(2) ptrace(2) setns(2) unshare(2) proc(5), subgid(5) subuid(5) capabilities(7) cgroup_namespaces(7) credentials(7) namespaces(7) pid_namespaces(7) Documentation/admin-guide/namespaces/resource-control.rst. 3 . . : . 6.18 8 2026 user_namespaces(7)