Quoting Serge Hallyn (serge.hal...@ubuntu.com): > The lxc configuration file currently supports 'lxc.cap.drop', a list of > capabilities to be dropped (using the bounding set) from the container. > The problem with this is that over time new capabilities are added. So > an older container configuration file may, over time, become insecure. > > Walter has in the past suggested replacing lxc.cap.drop with > lxc.cap.preserve, which would have the inverse sense - any capabilities > in that set would be kept, any others would be dropped. > > Realistically both have the same problem - the sendmail capabilities > bug proved that running code with unexpectedly dropped privilege can be > dangerous. This patch gives the admin a choice: You can use either > lxc.cap.keep or lxc.cap.drop, not both. > > Both continue to be ignored if a user namespace is in use.
Does anyone have any comments on this patch? I still have decide whether, if noone replies, I'll drop it or push it. > Signed-off-by: Serge Hallyn <serge.hal...@ubuntu.com> > --- > doc/lxc.conf.sgml.in | 11 ++++++ > src/lxc/conf.c | 93 > ++++++++++++++++++++++++++++++++++++++++++++++++-- > src/lxc/conf.h | 5 ++- > src/lxc/confile.c | 70 +++++++++++++++++++++++++++++++++++++ > 4 files changed, 176 insertions(+), 3 deletions(-) > > diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in > index 6500e50..7c289a0 100644 > --- a/doc/lxc.conf.sgml.in > +++ b/doc/lxc.conf.sgml.in > @@ -771,6 +771,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA > 02111-1307 USA > </para> > </listitem> > </varlistentry> > + <varlistentry> > + <term> > + <option>lxc.cap.keep</option> > + </term> > + <listitem> > + <para> > + Specify the capability to be kept in the container. All other > + capabilities will be dropped. > + </para> > + </listitem> > + </varlistentry> > </variablelist> > </refsect2> > > diff --git a/src/lxc/conf.c b/src/lxc/conf.c > index c309485..14df7c2 100644 > --- a/src/lxc/conf.c > +++ b/src/lxc/conf.c > @@ -1804,7 +1804,73 @@ static int setup_caps(struct lxc_list *caps) > > } > > - DEBUG("capabilities has been setup"); > + DEBUG("capabilities have been setup"); > + > + return 0; > +} > + > +static int dropcaps_except(struct lxc_list *caps) > +{ > + struct lxc_list *iterator; > + char *keep_entry; > + char *ptr; > + int i, capid; > + int numcaps = lxc_caps_last_cap() + 1; > + INFO("found %d capabilities\n", numcaps); > + > + // caplist[i] is 1 if we keep capability i > + int *caplist = alloca(numcaps * sizeof(int)); > + memset(caplist, 0, numcaps * sizeof(int)); > + > + lxc_list_for_each(iterator, caps) { > + > + keep_entry = iterator->elem; > + > + capid = -1; > + > + for (i = 0; i < sizeof(caps_opt)/sizeof(caps_opt[0]); i++) { > + > + if (strcmp(keep_entry, caps_opt[i].name)) > + continue; > + > + capid = caps_opt[i].value; > + break; > + } > + > + if (capid < 0) { > + /* try to see if it's numeric, so the user may specify > + * capabilities that the running kernel knows about but > + * we don't */ > + capid = strtol(keep_entry, &ptr, 10); > + if (!ptr || *ptr != '\0' || > + capid == LONG_MIN || capid == LONG_MAX) > + /* not a valid number */ > + capid = -1; > + else if (capid > lxc_caps_last_cap()) > + /* we have a number but it's not a valid > + * capability */ > + capid = -1; > + } > + > + if (capid < 0) { > + ERROR("unknown capability %s", keep_entry); > + return -1; > + } > + > + DEBUG("drop capability '%s' (%d)", keep_entry, capid); > + > + caplist[capid] = 1; > + } > + for (i=0; i<numcaps; i++) { > + if (caplist[i]) > + continue; > + if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0)) { > + SYSERROR("failed to remove capability %d", i); > + return -1; > + } > + } > + > + DEBUG("capabilities have been setup"); > > return 0; > } > @@ -2140,6 +2206,7 @@ struct lxc_conf *lxc_conf_init(void) > lxc_list_init(&new->network); > lxc_list_init(&new->mount_list); > lxc_list_init(&new->caps); > + lxc_list_init(&new->keepcaps); > lxc_list_init(&new->id_map); > for (i=0; i<NUM_LXC_HOOKS; i++) > lxc_list_init(&new->hooks[i]); > @@ -2884,7 +2951,16 @@ int lxc_setup(const char *name, struct lxc_conf > *lxc_conf) > } > > if (lxc_list_empty(&lxc_conf->id_map)) { > - if (setup_caps(&lxc_conf->caps)) { > + if (!lxc_list_empty(&lxc_conf->keepcaps)) { > + if (!lxc_list_empty(&lxc_conf->caps)) { > + ERROR("Simultaneously requested dropping and > keeping caps"); > + return -1; > + } > + if (dropcaps_except(&lxc_conf->keepcaps)) { > + ERROR("failed to keep requested caps\n"); > + return -1; > + } > + } else if (setup_caps(&lxc_conf->caps)) { > ERROR("failed to drop capabilities"); > return -1; > } > @@ -3070,6 +3146,18 @@ int lxc_clear_config_caps(struct lxc_conf *c) > return 0; > } > > +int lxc_clear_config_keepcaps(struct lxc_conf *c) > +{ > + struct lxc_list *it,*next; > + > + lxc_list_for_each_safe(it, &c->keepcaps, next) { > + lxc_list_del(it); > + free(it->elem); > + free(it); > + } > + return 0; > +} > + > int lxc_clear_cgroups(struct lxc_conf *c, const char *key) > { > struct lxc_list *it,*next; > @@ -3169,6 +3257,7 @@ void lxc_conf_free(struct lxc_conf *conf) > #endif > lxc_seccomp_free(conf); > lxc_clear_config_caps(conf); > + lxc_clear_config_keepcaps(conf); > lxc_clear_cgroups(conf, "lxc.cgroup"); > lxc_clear_hooks(conf, "lxc.hook"); > lxc_clear_mount_entries(conf); > diff --git a/src/lxc/conf.h b/src/lxc/conf.h > index 2fd3ab1..5ca30ec 100644 > --- a/src/lxc/conf.h > +++ b/src/lxc/conf.h > @@ -232,7 +232,8 @@ struct lxc_rootfs { > * @network : network configuration > * @utsname : container utsname > * @fstab : path to a fstab file format > - * @caps : list of the capabilities > + * @caps : list of the capabilities to drop > + * @keepcaps : list of the capabilities to keep > * @tty_info : tty data > * @console : console data > * @ttydir : directory (under /dev) in which to create console and ttys > @@ -265,6 +266,7 @@ struct lxc_conf { > int num_savednics; > struct lxc_list mount_list; > struct lxc_list caps; > + struct lxc_list keepcaps; > struct lxc_tty_info tty_info; > struct lxc_console console; > struct lxc_rootfs rootfs; > @@ -315,6 +317,7 @@ extern void lxc_delete_tty(struct lxc_tty_info *tty_info); > extern int lxc_clear_config_network(struct lxc_conf *c); > extern int lxc_clear_nic(struct lxc_conf *c, const char *key); > extern int lxc_clear_config_caps(struct lxc_conf *c); > +extern int lxc_clear_config_keepcaps(struct lxc_conf *c); > extern int lxc_clear_cgroups(struct lxc_conf *c, const char *key); > extern int lxc_clear_mount_entries(struct lxc_conf *c); > extern int lxc_clear_hooks(struct lxc_conf *c, const char *key); > diff --git a/src/lxc/confile.c b/src/lxc/confile.c > index a7db117..a4006bc 100644 > --- a/src/lxc/confile.c > +++ b/src/lxc/confile.c > @@ -85,6 +85,7 @@ static int config_network_script(const char *, const char > *, struct lxc_conf *); > static int config_network_ipv6(const char *, const char *, struct lxc_conf > *); > static int config_network_ipv6_gateway(const char *, const char *, struct > lxc_conf *); > static int config_cap_drop(const char *, const char *, struct lxc_conf *); > +static int config_cap_keep(const char *, const char *, struct lxc_conf *); > static int config_console(const char *, const char *, struct lxc_conf *); > static int config_seccomp(const char *, const char *, struct lxc_conf *); > static int config_includefile(const char *, const char *, struct lxc_conf *); > @@ -136,6 +137,7 @@ static struct lxc_config_t config[] = { > /* config_network_nic must come after all other 'lxc.network.*' entries > */ > { "lxc.network.", config_network_nic }, > { "lxc.cap.drop", config_cap_drop }, > + { "lxc.cap.keep", config_cap_keep }, > { "lxc.console", config_console }, > { "lxc.seccomp", config_seccomp }, > { "lxc.include", config_includefile }, > @@ -1264,6 +1266,52 @@ static int config_mount(const char *key, const char > *value, > return 0; > } > > +static int config_cap_keep(const char *key, const char *value, > + struct lxc_conf *lxc_conf) > +{ > + char *keepcaps, *keepptr, *sptr, *token; > + struct lxc_list *keeplist; > + int ret = -1; > + > + if (!strlen(value)) > + return -1; > + > + keepcaps = strdup(value); > + if (!keepcaps) { > + SYSERROR("failed to dup '%s'", value); > + return -1; > + } > + > + /* in case several capability keep is specified in a single line > + * split these caps in a single element for the list */ > + for (keepptr = keepcaps;;keepptr = NULL) { > + token = strtok_r(keepptr, " \t", &sptr); > + if (!token) { > + ret = 0; > + break; > + } > + > + keeplist = malloc(sizeof(*keeplist)); > + if (!keeplist) { > + SYSERROR("failed to allocate keepcap list"); > + break; > + } > + > + keeplist->elem = strdup(token); > + if (!keeplist->elem) { > + SYSERROR("failed to dup '%s'", token); > + free(keeplist); > + break; > + } > + > + lxc_list_add_tail(&lxc_conf->keepcaps, keeplist); > + } > + > + free(keepcaps); > + > + return ret; > +} > + > static int config_cap_drop(const char *key, const char *value, > struct lxc_conf *lxc_conf) > { > @@ -1630,6 +1678,22 @@ static int lxc_get_item_cap_drop(struct lxc_conf *c, > char *retv, int inlen) > return fulllen; > } > > +static int lxc_get_item_cap_keep(struct lxc_conf *c, char *retv, int inlen) > +{ > + int len, fulllen = 0; > + struct lxc_list *it; > + > + if (!retv) > + inlen = 0; > + else > + memset(retv, 0, inlen); > + > + lxc_list_for_each(it, &c->keepcaps) { > + strprint(retv, inlen, "%s\n", (char *)it->elem); > + } > + return fulllen; > +} > + > static int lxc_get_mount_entries(struct lxc_conf *c, char *retv, int inlen) > { > int len, fulllen = 0; > @@ -1810,6 +1874,8 @@ int lxc_get_config_item(struct lxc_conf *c, const char > *key, char *retv, > v = c->rootfs.pivot; > else if (strcmp(key, "lxc.cap.drop") == 0) > return lxc_get_item_cap_drop(c, retv, inlen); > + else if (strcmp(key, "lxc.cap.keep") == 0) > + return lxc_get_item_cap_keep(c, retv, inlen); > else if (strncmp(key, "lxc.hook", 8) == 0) > return lxc_get_item_hooks(c, retv, inlen, key); > else if (strcmp(key, "lxc.network") == 0) > @@ -1833,6 +1899,8 @@ int lxc_clear_config_item(struct lxc_conf *c, const > char *key) > return lxc_clear_nic(c, key + 12); > else if (strcmp(key, "lxc.cap.drop") == 0) > return lxc_clear_config_caps(c); > + else if (strcmp(key, "lxc.cap.keep") == 0) > + return lxc_clear_config_keepcaps(c); > else if (strncmp(key, "lxc.cgroup", 10) == 0) > return lxc_clear_cgroups(c, key); > else if (strcmp(key, "lxc.mount.entries") == 0) > @@ -1945,6 +2013,8 @@ void write_config(FILE *fout, struct lxc_conf *c) > } > lxc_list_for_each(it, &c->caps) > fprintf(fout, "lxc.cap.drop = %s\n", (char *)it->elem); > + lxc_list_for_each(it, &c->keepcaps) > + fprintf(fout, "lxc.cap.keep = %s\n", (char *)it->elem); > for (i=0; i<NUM_LXC_HOOKS; i++) { > lxc_list_for_each(it, &c->hooks[i]) > fprintf(fout, "lxc.hook.%s = %s\n", > -- > 1.7.9.5 > > > ------------------------------------------------------------------------------ > This SF.net email is sponsored by Windows: > > Build for Windows Store. > > http://p.sf.net/sfu/windows-dev2dev > _______________________________________________ > Lxc-devel mailing list > Lxc-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/lxc-devel ------------------------------------------------------------------------------ This SF.net email is sponsored by Windows: Build for Windows Store. http://p.sf.net/sfu/windows-dev2dev _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel