On Fri, Jul 19, 2013 at 02:26:50PM +0000, Serge Hallyn wrote: > From: Serge Hallyn <serge.hal...@ubuntu.com> > > 1. lxcapi_create: don't try to unshare and mount for dir backed containers > > It's unnecessary, and breaks unprivileged lxc-create (since unpriv users > cannot yet unshare(CLONE_NEWNS)). > > 2. api_create: chown rootfs > > chown rootfs to the host uid to which container root will be mapped > > 3. create: run template in a mapped user ns > > 4. use (setuid-root) newxidmap to set id_map if we are not root > > This is needed to be able to set userns mappings as an unprivileged > user, for unprivileged lxc-start. > > Signed-off-by: Serge Hallyn <serge.hal...@ubuntu.com> > --- > src/lxc/conf.c | 107 +++++++++++++++++++++++++++----- > src/lxc/conf.h | 4 ++ > src/lxc/lxccontainer.c | 162 > +++++++++++++++++++++++++++++++++++++++++++++---- > 3 files changed, 244 insertions(+), 29 deletions(-) > > diff --git a/src/lxc/conf.c b/src/lxc/conf.c > index 46320dd..2e202ec 100644 > --- a/src/lxc/conf.c > +++ b/src/lxc/conf.c > @@ -2547,6 +2547,11 @@ static int write_id_mapping(enum idtype idtype, pid_t > pid, const char *buf, > return ret < 0 ? ret : closeret; > } > > +int do_call_newxid(char *str) > +{ > + return system(str); > +} > +
Hmm, what? :) Is that meant to evolve into something more than an alias of system()? > int lxc_map_ids(struct lxc_list *idmap, pid_t pid) > { > struct lxc_list *iterator; > @@ -2554,31 +2559,49 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid) > int ret = 0; > enum idtype type; > char *buf = NULL, *pos; > + int am_root = (getuid() == 0); > > for(type = ID_TYPE_UID; type <= ID_TYPE_GID; type++) { > int left, fill; > - > - pos = buf; > - lxc_list_for_each(iterator, idmap) { > - /* The kernel only takes <= 4k for writes to > /proc/<nr>/[ug]id_map */ > - if (!buf) > - buf = pos = malloc(4096); > + int had_entry = 0; > + if (!buf) { > + buf = pos = malloc(4096); > if (!buf) > return -ENOMEM; > + } > + pos = buf; > + if (!am_root) > + pos += sprintf(buf, "/usr/bin/new%cidmap %d ", May be worth having autoconf figure out the paths for those as they very well may be moved to /bin. > + type == ID_TYPE_UID ? 'u' : 'g', > + pid); > > + lxc_list_for_each(iterator, idmap) { > + /* The kernel only takes <= 4k for writes to > /proc/<nr>/[ug]id_map */ > map = iterator->elem; > - if (map->idtype == type) { > - left = 4096 - (pos - buf); > - fill = snprintf(pos, left, "%lu %lu %lu\n", > - map->nsid, map->hostid, map->range); > - if (fill <= 0 || fill >= left) > - SYSERROR("snprintf failed, too many > mappings"); > - pos += fill; > - } > + if (map->idtype != type) > + continue; > + > + had_entry = 1; > + left = 4096 - (pos - buf); > + fill = snprintf(pos, left, " %lu %lu %lu", map->nsid, > + map->hostid, map->range); > + if (fill <= 0 || fill >= left) > + SYSERROR("snprintf failed, too many mappings"); > + pos += fill; > } > - if (pos == buf) // no mappings were found > + if (!had_entry) > continue; > - ret = write_id_mapping(type, pid, buf, pos-buf); > + left = 4096 - (pos - buf); > + fill = snprintf(pos, left, "\n"); > + if (fill <= 0 || fill >= left) > + SYSERROR("snprintf failed, too many mappings"); > + pos += fill; > + > + if (am_root) > + ret = write_id_mapping(type, pid, buf, pos-buf); > + else > + ret = do_call_newxid(buf); > + > if (ret) > break; > } > @@ -2588,6 +2611,58 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid) > return ret; > } > > +/* > + * return the host uid to which the container root is mapped, or -1 on > + * error > + */ > +int get_mapped_rootid(struct lxc_conf *conf) > +{ > + struct lxc_list *it; > + struct id_map *map; > + > + lxc_list_for_each(it, &conf->id_map) { > + map = it->elem; > + if (map->idtype != ID_TYPE_UID) > + continue; > + if (map->nsid != 0) > + continue; > + return map->hostid; > + } > + return -1; > +} > + > +bool hostid_is_mapped(int id, struct lxc_conf *conf) > +{ > + struct lxc_list *it; > + struct id_map *map; > + lxc_list_for_each(it, &conf->id_map) { > + map = it->elem; > + if (map->idtype != ID_TYPE_UID) > + continue; > + if (id >= map->hostid && id < map->hostid + map->range) > + return true; > + } > + return false; > +} > + > +int find_unmapped_nsuid(struct lxc_conf *conf) > +{ > + struct lxc_list *it; > + struct id_map *map; > + uid_t freeid = 0; > +again: > + lxc_list_for_each(it, &conf->id_map) { > + map = it->elem; > + if (map->idtype != ID_TYPE_UID) > + continue; > + if (freeid >= map->nsid && freeid < map->nsid + map->range) { > + freeid = map->nsid + map->range; > + goto again; > + } > + } > + return freeid; > +} > + > int lxc_find_gateway_addresses(struct lxc_handler *handler) > { > struct lxc_list *network = &handler->conf->network; > diff --git a/src/lxc/conf.h b/src/lxc/conf.h > index ed3240d..065b1df 100644 > --- a/src/lxc/conf.h > +++ b/src/lxc/conf.h > @@ -331,4 +331,8 @@ extern int lxc_setup(const char *name, struct lxc_conf > *lxc_conf, > const char *lxcpath); > > extern void lxc_rename_phys_nics_on_shutdown(struct lxc_conf *conf); > + > +extern int get_mapped_rootid(struct lxc_conf *conf); > +extern int find_unmapped_nsuid(struct lxc_conf *conf); > +extern bool hostid_is_mapped(int id, struct lxc_conf *conf); > #endif > diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c > index b0695bc..f9ce7d3 100644 > --- a/src/lxc/lxccontainer.c > +++ b/src/lxc/lxccontainer.c > @@ -678,6 +678,49 @@ static const char *lxcapi_get_config_path(struct > lxc_container *c); > static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, > const char *v); > > /* > + * chown_mapped: for an unprivileged user with uid X to chown a dir > + * to subuid Y, he needs to run chown as root in a userns where > + * nsid 0 is mapped to hostuid Y, and nsid Y is mapped to hostuid > + * X. That way, the container root is privileged with respect to > + * hostuid X, allowing him to do the chown. > + */ > +static int chown_mapped(int nsrootid, char *path) > +{ > + if (nsrootid < 0) > + return nsrootid; > + pid_t pid = fork(); > + if (pid < 0) { > + SYSERROR("Failed forking"); > + return -1; > + } > + if (!pid) { > + int hostuid = geteuid(), ret; > + char map1[100], map2[100]; > + char *args[] = {"usernsexec", "-m", map1, "-m", map2, "--", > "/bin/chown", > + "0", path, NULL}; Same thing as above, we should avoid hardcoding paths to binaries that we aren't shipping ourselves, though admitedly this one should be safe... > + > + // "b:0:nsrootid:1" > + ret = snprintf(map1, 100, "b:0:%d:1", nsrootid); > + if (ret < 0 || ret >= 100) { > + ERROR("Error uid printing map string"); > + return -1; > + } > + > + // "b:hostuid:hostuid:1" > + ret = snprintf(map2, 100, "b:%d:%d:1", hostuid, hostuid); > + if (ret < 0 || ret >= 100) { > + ERROR("Error uid printing map string"); > + return -1; > + } > + > + ret = execvp("usernsexec", args); > + SYSERROR("Failed executing usernsexec"); > + exit(1); > + } > + return wait_for_pid(pid); > +} > + > +/* > * do_bdev_create: thin wrapper around bdev_create(). Like bdev_create(), > * it returns a mounted bdev on success, NULL on error. > */ > @@ -701,6 +744,25 @@ static struct bdev *do_bdev_create(struct lxc_container > *c, const char *type, > if (!bdev) > return NULL; > lxcapi_set_config_item(c, "lxc.rootfs", bdev->src); > + > + /* if we are not root, chown the rootfs dir to root in the > + * target uidmap */ > + > + if (geteuid() != 0) { > + int rootid; > + if ((rootid = get_mapped_rootid(c->lxc_conf)) <= 0) { > + ERROR("No mapping for container root"); > + bdev_put(bdev); > + return NULL; > + } > + ret = chown_mapped(rootid, bdev->dest); > + if (ret < 0) { > + ERROR("Error chowning %s to %d\n", bdev->dest, rootid); > + bdev_put(bdev); > + return NULL; > + } > + } > + > return bdev; > } > > @@ -772,6 +834,7 @@ static bool create_run_template(struct lxc_container *c, > char *tpath, bool quiet > int i; > int ret, len, nargs = 0; > char **newargv; > + struct lxc_conf *conf = c->lxc_conf; > > if (quiet) { > close(0); > @@ -781,10 +844,6 @@ static bool create_run_template(struct lxc_container *c, > char *tpath, bool quiet > open("/dev/null", O_RDWR); > open("/dev/null", O_RDWR); > } > - if (unshare(CLONE_NEWNS) < 0) { > - ERROR("error unsharing mounts"); > - exit(1); > - } > > src = c->lxc_conf->rootfs.path; > /* > @@ -801,9 +860,19 @@ static bool create_run_template(struct lxc_container *c, > char *tpath, bool quiet > exit(1); > } > > - if (bdev->ops->mount(bdev) < 0) { > - ERROR("Error mounting rootfs"); > - exit(1); > + if (strcmp(bdev->type, "dir") != 0) { > + if (unshare(CLONE_NEWNS) < 0) { > + ERROR("error unsharing mounts"); > + exit(1); > + } > + if (bdev->ops->mount(bdev) < 0) { > + ERROR("Error mounting rootfs"); > + exit(1); > + } > + } else { // TODO come up with a better way here! > + if (bdev->dest) > + free(bdev->dest); > + bdev->dest = strdup(bdev->src); > } > > /* > @@ -813,6 +882,7 @@ static bool create_run_template(struct lxc_container *c, > char *tpath, bool quiet > if (argv) > for (nargs = 0; argv[nargs]; nargs++) ; > nargs += 4; // template, path, rootfs and name args > + > newargv = malloc(nargs * sizeof(*newargv)); > if (!newargv) > exit(1); > @@ -856,6 +926,66 @@ static bool create_run_template(struct lxc_container *c, > char *tpath, bool quiet > exit(1); > newargv[nargs - 1] = NULL; > > + /* > + * If we're running the template in a mapped userns, then > + * we prepend the template command with: > + * usernsexec <-m map1> ... <-m mapn> -- > + */ > + if (geteuid() != 0 && !lxc_list_empty(&conf->id_map)) { > + int n2args = 1; > + char **n2 = malloc(n2args * sizeof(*n2)); > + struct lxc_list *it; > + struct id_map *map; > + > + newargv[0] = tpath; > + tpath = "/usr/bin/usernsexec"; Same thing. > + n2[0] = "usernsexec"; > + lxc_list_for_each(it, &conf->id_map) { > + map = it->elem; > + n2args += 2; > + n2 = realloc(n2, n2args * sizeof(*n2)); > + if (!n2) > + exit(1); > + n2[n2args-2] = "-m"; > + n2[n2args-1] = malloc(200); > + if (!n2[n2args-1]) > + exit(1); > + ret = snprintf(n2[n2args-1], 200, > "%c:%lu:%lu:%lu", > + map->idtype == ID_TYPE_UID ? 'u' : 'g', > + map->nsid, map->hostid, map->range); > + if (ret < 0 || ret >= 200) > + exit(1); > + } > + bool hostid_mapped = hostid_is_mapped(geteuid(), conf); > + int extraargs = hostid_mapped ? 1 : 3; > + n2 = realloc(n2, (nargs + n2args + extraargs) * > sizeof(*n2)); > + if (!n2) > + exit(1); > + if (!hostid_mapped) { > + int free_id = find_unmapped_nsuid(conf); > + n2[n2args++] = "-m"; > + if (free_id < 0) { > + ERROR("Could not find free uid to map"); > + exit(1); > + } > + n2[n2args++] = malloc(200); > + if (!n2[n2args-1]) { > + SYSERROR("out of memory"); > + exit(1); > + } > + ret = snprintf(n2[n2args-1], 200, "u:%d:%d:1", > + free_id, geteuid()); > + if (ret < 0 || ret >= 200) { > + ERROR("string too long"); > + exit(1); > + } > + } > + n2[n2args++] = "--"; > + for (i = 0; i < nargs; i++) > + n2[i + n2args] = newargv[i]; > + free(newargv); > + newargv = n2; > + } > /* execute */ > execv(tpath, newargv); > SYSERROR("failed to execute template %s", tpath); > @@ -1949,15 +2079,21 @@ static int clone_update_rootfs(struct lxc_container > *c0, > if (pid > 0) > return wait_for_pid(pid); > > - if (unshare(CLONE_NEWNS) < 0) { > - ERROR("error unsharing mounts"); > - exit(1); > - } > bdev = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, > NULL); > if (!bdev) > exit(1); > - if (bdev->ops->mount(bdev) < 0) > - exit(1); > + if (strcmp(bdev->type, "dir") != 0) { > + if (unshare(CLONE_NEWNS) < 0) { > + ERROR("error unsharing mounts"); > + exit(1); > + } > + if (bdev->ops->mount(bdev) < 0) > + exit(1); > + } else { // TODO come up with a better way > + if (bdev->dest) > + free(bdev->dest); > + bdev->dest = strdup(bdev->src); > + } > > if (!lxc_list_empty(&conf->hooks[LXCHOOK_CLONE])) { > /* Start of environment variable setup for hooks */ > -- > 1.8.3.2 > > > ------------------------------------------------------------------------------ > See everything from the browser to the database with AppDynamics > Get end-to-end visibility with application monitoring from AppDynamics > Isolate bottlenecks and diagnose root cause in seconds. > Start your free trial of AppDynamics Pro today! > http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk > _______________________________________________ > Lxc-devel mailing list > Lxc-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/lxc-devel -- Stéphane Graber Ubuntu developer http://www.ubuntu.com
signature.asc
Description: Digital signature
------------------------------------------------------------------------------ See everything from the browser to the database with AppDynamics Get end-to-end visibility with application monitoring from AppDynamics Isolate bottlenecks and diagnose root cause in seconds. Start your free trial of AppDynamics Pro today! http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk
_______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel