is it possible to map in supplemental groups in a userns when the user lacks setgid/etc... capabilities in the parent ns ? it doesn't seem like it's currently possible, but is there a reason to not enable it ? basically i have a build tool that i want to isolate a bit, but it requires access to some of my supplemental groups. if i map just my effective uid/gid, the build will fail when it tries to use the chown/chgrp commands (gets back EINVAL). my scenario boils down to: - normal unprivileged user (uid=8282) - member of multiple groups (gid=100, getgroups={100,16,250,...}) - create a new userns (to get access to other ns like mount/pid) but still have access to existing groups where i'm root - use various features that require caps (new pidns/mntns/etc...) - create another userns and map back non-root users/groups i.e. i switch from 8282 to 0, do what i need, then switch back to 8282. i've attached a simple test program to show the issue. it can map the current uid/gid fine, but fails to do so with a supplemental group. my reading of kernel/user_namespace.c shows that it's not possible: (1) if gid_map has any entries, map_write bails early with EPERM (2) if gid_map is empty, then writing a single entry (e.g. "0 100 1") works, but then a 2nd write runs into (1) (3) if gid_map is empty, then writing multiple entries (e.g. "0 100 1\n250 250 1\n") fails in new_idmap_permitted -- the first check is skipped (since new_map->nr_extents is 2), and the user does not have caps in the parent ns i'm aware UID_GID_MAP_MAX_EXTENTS is low, so it's not even possible if i had the caps to map all the existing supplemental groups, which is why i only want to map the few critical groups. but assuming this use case is one we want to support, maybe it makes sense to add a knob to map all of the user's supplemental groups ? in the mean time, a "quick" fix might be to change new_idmap_permitted to walk all the extents, and if all the ranges are set to 1, check the supplemental groups in addition to the current egid ? -mike
/* To compile: * gcc test.c * To run: * ./a.out [1|2|3|4|5] * ./a.out -1 "string to write to gid_map" */ #define _GNU_SOURCE #include <assert.h> #include <err.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> int main(int argc, char *argv[]) { int ret; FILE *fp; int uid = getuid(); int gid = getgid(); int nuid = 0; int ngid = 0; int suppgid; gid_t *groups; size_t i, gsize; int group_mode; switch (argc) { case 0: case 1: group_mode = 1; case 2: group_mode = atoi(argv[1]); case 3: group_mode = -1; } /* Find one supplemental group */ gsize = getgroups(0, NULL); assert(gsize != 0); groups = malloc(sizeof(*groups) * gsize); ret = getgroups(gsize, groups); assert(ret == gsize); for (i = 0; i < gsize; ++i) if (groups[i] != gid) { suppgid = groups[i]; break; } if (i == gsize) errx(1, "could not find a supplemental group to test"); free(groups); /* Create new userns */ assert(unshare(CLONE_NEWUSER) == 0); /* Map the current uid */ fp = fopen("/proc/self/uid_map", "we"); assert(fp); setbuf(fp, NULL); fprintf(fp, "%i %i 1", nuid, uid); fclose(fp); /* Disable setgroups() */ fp = fopen("/proc/self/setgroups", "we"); assert(fp); setbuf(fp, NULL); fputs("deny", fp); fclose(fp); /* Map the various gids */ fp = fopen("/proc/self/gid_map", "we"); assert(fp); setbuf(fp, NULL); switch (group_mode) { case 1: /* This works */ fprintf(fp, "%i %i 1\n", ngid, gid); break; case 2: /* This fails */ fprintf(fp, "%i %i 1\n", ngid, gid); fprintf(fp, "%i %i 1\n", suppgid, suppgid); break; case 3: /* This fails */ fprintf(fp, "%i %i 1\n%i %i 1\n", ngid, gid, suppgid, suppgid); break; case 4: /* This fails */ fprintf(fp, "%i %i 1\n", suppgid, suppgid); break; case 5: /* This fails */ fprintf(fp, "0 0 10000\n"); break; case -1: fprintf(fp, argv[2]); break; } fclose(fp); /* Validate */ printf("uid:%i gid:%i\n", getuid(), getgid()); gsize = getgroups(0, NULL); assert(gsize != 0); groups = malloc(sizeof(*groups) * gsize); ret = getgroups(gsize, groups); assert(ret == gsize); printf("groups: "); for (i = 0; i < gsize; ++i) printf("%i ", groups[i]); printf("\n"); free(groups); }
Attachment:
signature.asc
Description: Digital signature
_______________________________________________ Containers mailing list Containers@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linuxfoundation.org/mailman/listinfo/containers