diff --git a/crun.1 b/crun.1 index 67e846ab12..e507dedff1 100644 --- a/crun.1 +++ b/crun.1 @@ -711,6 +711,24 @@ If the \fB\fCidmap\fR option is specified then the mount is ID mapped using the target user namespace. This is an experimental feature and can change at any time without notice. +.PP +The \fB\fCidmap\fR option supports a custom mapping that can be different +than the user namespace used by the container. + +.PP +The mapping can be specified after the \fB\fCidmap\fR option like: +\fB\fCidmap=uids=0-1-10;gids=0-100-10\fR\&. + +.PP +The only two options that are currently supported are \fB\fCuids\fR and \fB\fCgids\fR\&. + +.PP +When a custom mapping is specified, a new user namespace is created +for the idmapped mount. + +.PP +If no option is specified, then the container user namespace is used. + .SH Automatically create user namespace .PP When running as user different than root, an user namespace is diff --git a/crun.1.md b/crun.1.md index f169c31464..c69228b7e2 100644 --- a/crun.1.md +++ b/crun.1.md @@ -553,6 +553,19 @@ If the `idmap` option is specified then the mount is ID mapped using the contain target user namespace. This is an experimental feature and can change at any time without notice. +The `idmap` option supports a custom mapping that can be different +than the user namespace used by the container. + +The mapping can be specified after the `idmap` option like: +`idmap=uids=0-1-10;gids=0-100-10`. + +The only two options that are currently supported are `uids` and `gids`. + +When a custom mapping is specified, a new user namespace is created +for the idmapped mount. + +If no option is specified, then the container user namespace is used. + ## Automatically create user namespace When running as user different than root, an user namespace is diff --git a/src/libcrun/linux.c b/src/libcrun/linux.c index 65a5e4acbb..bedd483b3d 100644 --- a/src/libcrun/linux.c +++ b/src/libcrun/linux.c @@ -430,16 +430,85 @@ get_bind_mount (const char *src, libcrun_error_t *err) return get_and_reset (&open_tree_fd); } +static pid_t +create_userns_for_idmapped_mount (const char *options, libcrun_error_t *err) +{ + cleanup_free char *dup_options = xstrdup (options); + char *option, *saveptr = NULL; + cleanup_pid pid_t pid = -1; + pid_t xchg_pid; + + pid = syscall_clone (CLONE_NEWUSER | SIGCHLD, NULL); + if (UNLIKELY (pid < 0)) + return crun_make_error (err, errno, "clone"); + + if (pid == 0) + { + prctl (PR_SET_PDEATHSIG, SIGKILL); + while (1) + pause (); + _exit (EXIT_SUCCESS); + } + for (option = strtok_r (dup_options, ";", &saveptr); option; option = strtok_r (NULL, ";", &saveptr)) + { + cleanup_free char *ids = xstrdup (option + 5 /* strlen ("uids=") and strlen ("gids=") */); + char proc_file[64]; + char *it; + int ret; + + for (it = ids; *it; it++) + { + if (*it == '-') + *it = ' '; + else if (*it == ',') + *it = '\n'; + } + + if (has_prefix (option, "uids=")) + { + sprintf (proc_file, "/proc/%d/uid_map", pid); + ret = write_file (proc_file, ids, strlen (ids), err); + if (UNLIKELY (ret < 0)) + return ret; + } + else if (has_prefix (option, "gids=")) + { + sprintf (proc_file, "/proc/%d/gid_map", pid); + ret = write_file (proc_file, ids, strlen (ids), err); + if (UNLIKELY (ret < 0)) + return ret; + } + else + return crun_make_error (err, 0, "invalid option `%s` specified", option); + } + + xchg_pid = pid; + pid = -1; + return xchg_pid; +} + static int -get_idmapped_mount (const char *src, pid_t pid, libcrun_error_t *err) +get_idmapped_mount (const char *src, const char *idmap_option, pid_t pid, libcrun_error_t *err) { cleanup_close int open_tree_fd = -1; - cleanup_close int fd = -1; - int ret; - char proc_path[64]; + cleanup_pid pid_t created_pid = -1; struct mount_attr_s attr = { 0, }; + cleanup_close int fd = -1; + const char *options; + char proc_path[64]; + int ret; + + /* If there are options specified, create a new user namespace with the configured mappings. */ + if ((options = strchr (idmap_option, '='))) + { + created_pid = create_userns_for_idmapped_mount (options + 1, err); + if (UNLIKELY (created_pid < 0)) + return created_pid; + + pid = created_pid; + } sprintf (proc_path, "/proc/%d/ns/user", pid); fd = open (proc_path, O_RDONLY); @@ -3350,15 +3419,15 @@ get_fd_map (libcrun_container_t *container) return mount_fds; } -static bool -is_idmapped (runtime_spec_schema_defs_mount *mnt) +static char * +get_idmapped_option (runtime_spec_schema_defs_mount *mnt) { size_t i; for (i = 0; i < mnt->options_len; i++) - if (strcmp (mnt->options[i], "idmap") == 0) - return true; - return false; + if (has_prefix (mnt->options[i], "idmap")) + return mnt->options[i]; + return NULL; } static bool @@ -3393,11 +3462,12 @@ prepare_and_send_mounts (libcrun_container_t *container, pid_t pid, int sync_soc for (i = 0; i < def->mounts_len; i++) { - if (is_idmapped (def->mounts[i])) + const char *idmap_option = get_idmapped_option (def->mounts[i]); + if (idmap_option) { int fd; - fd = get_idmapped_mount (def->mounts[i]->source, pid, err); + fd = get_idmapped_mount (def->mounts[i]->source, idmap_option, pid, err); if (UNLIKELY (fd < 0)) return fd;