This patch adds the lxc.mount.auto configuration option that allows the user to specify that certain standard filesystems should be automatically pre-mounted when the container is started.
Currently, four things are implemented: - /proc (mounted read-write) - /sys (mounted read-only) - /sys/fs/cgroup (special logic, see mailing list discussions) - /proc/sysrq-trigger (see below) /proc/sysrq-trigger may be used from within a container to trigger a forced host reboot (echo b > /proc/sysrq-trigger) or do other things that a container shouldn't be able to do. The logic here is to bind-mount /dev/null over /proc/sysrq-trigger, so that that cannot happen. This obviously only protects fully if CAP_SYS_ADMIN is not available inside the container (otherwise that bind-mount could be removed). Signed-off-by: Christian Seiler <christ...@iwakd.de> --- src/lxc/conf.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/lxc/conf.h | 14 ++++++- src/lxc/confile.c | 63 ++++++++++++++++++++++++++++-- src/lxc/start.c | 2 +- 4 files changed, 183 insertions(+), 7 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 5f9ae87..364e571 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -72,6 +72,7 @@ #include "lxc.h" /* for lxc_cgroup_set() */ #include "caps.h" /* for lxc_caps_last_cap() */ #include "bdev.h" +#include "cgroup.h" #if HAVE_APPARMOR #include <apparmor.h> @@ -707,6 +708,89 @@ int pin_rootfs(const char *rootfs) return fd; } +static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct cgroup_process_info *cgroup_info) +{ + char *path = NULL; + char *dev_null = NULL; + int r; + + dev_null = lxc_append_paths(conf->rootfs.mount, "/dev/null"); + if (!dev_null) { + SYSERROR("memory allocation error"); + goto cleanup; + } + + if (flags & LXC_AUTO_PROC) { + path = lxc_append_paths(conf->rootfs.mount, "/proc"); + if (!path) { + SYSERROR("memory allocation error trying to automatically mount /proc"); + goto cleanup; + } + + r = mount("proc", path, "proc", MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL); + if (r < 0) { + SYSERROR("error mounting /proc"); + goto cleanup; + } + + free(path); + path = NULL; + } + + if (flags & LXC_AUTO_PROC_SYSRQ) { + path = lxc_append_paths(conf->rootfs.mount, "/proc/sysrq-trigger"); + if (!path) { + SYSERROR("memory allocation error trying to automatically mount /proc"); + goto cleanup; + } + + /* safety measure, mount /dev/null over /proc/sysrq-trigger, + * otherwise, a container may trigger a host reboot or such + */ + r = mount(dev_null, path, NULL, MS_BIND, NULL); + if (r < 0) + WARN("error mounting /dev/null over /proc/sysrq-trigger: %s", strerror(errno)); + + free(path); + path = NULL; + } + + if (flags & LXC_AUTO_SYS) { + path = lxc_append_paths(conf->rootfs.mount, "/sys"); + if (!path) { + SYSERROR("memory allocation error trying to automatically mount /sys"); + goto cleanup; + } + + r = mount("sysfs", path, "sysfs", MS_RDONLY, NULL); + if (r < 0) { + SYSERROR("error mounting /sys"); + goto cleanup; + } + + free(path); + path = NULL; + } + + if (flags & LXC_AUTO_CGROUP) { + r = lxc_setup_mount_cgroup(conf->rootfs.mount, cgroup_info); + if (r < 0) { + SYSERROR("error mounting /sys/fs/cgroup"); + goto cleanup; + } + } + + free(dev_null); + free(path); + + return 0; + +cleanup: + free(dev_null); + free(path); + return -1; +} + static int mount_rootfs(const char *rootfs, const char *target) { char absrootfs[MAXPATHLEN]; @@ -2878,7 +2962,7 @@ int uid_shift_ttys(int pid, struct lxc_conf *conf) return 0; } -int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath) +int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath, struct cgroup_process_info *cgroup_info) { #if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */ int mounted; @@ -2911,6 +2995,14 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath) } } + /* do automatic mounts (mainly /proc and /sys), but exclude + * those that need to wait until other stuff has finished + */ + if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & ~LXC_AUTO_CGROUP & ~LXC_AUTO_PROC_SYSRQ, cgroup_info) < 0) { + ERROR("failed to setup the automatic mounts for '%s'", name); + return -1; + } + if (setup_mount(&lxc_conf->rootfs, lxc_conf->fstab, name)) { ERROR("failed to setup the mounts for '%s'", name); return -1; @@ -2921,6 +3013,15 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath) return -1; } + /* now mount only cgroup, if wanted; + * before, /sys could not have been mounted + * (is either mounted automatically or via fstab entries) + */ + if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & LXC_AUTO_CGROUP, cgroup_info) < 0) { + ERROR("failed to setup the automatic mounts for '%s'", name); + return -1; + } + if (run_lxc_hooks(name, "mount", lxc_conf, lxcpath, NULL)) { ERROR("failed to run mount hooks for container '%s'.", name); return -1; @@ -2937,6 +3038,14 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath) } } + /* over-mount /proc/sysrq-trigger with /dev/null now, if wanted; + * before /dev/null did not necessarily exist + */ + if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & LXC_AUTO_PROC_SYSRQ, cgroup_info) < 0) { + ERROR("failed to setup the automatic mounts for '%s'", name); + return -1; + } + if (!lxc_conf->is_execute && setup_console(&lxc_conf->rootfs, &lxc_conf->console, lxc_conf->ttydir)) { ERROR("failed to setup the console for '%s'", name); return -1; diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 5febf12..d99bdfe 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -222,6 +222,16 @@ struct lxc_rootfs { }; /* + * Automatic mounts for LXC to perform inside the container + */ +enum { + LXC_AUTO_PROC = 0x01, /* /proc */ + LXC_AUTO_SYS = 0x02, /* /sys*/ + LXC_AUTO_CGROUP = 0x04, /* /sys/fs/cgroup */ + LXC_AUTO_PROC_SYSRQ = 0x08, /* /proc/sysrq-trigger over-bind-mounted with /dev/null */ +}; + +/* * Defines the global container configuration * @rootfs : root directory to run the container * @pivotdir : pivotdir path, if not set default will be used @@ -265,6 +275,7 @@ struct lxc_conf { struct lxc_list network; struct saved_nic *saved_nics; int num_savednics; + int auto_mounts; struct lxc_list mount_list; struct lxc_list caps; struct lxc_list keepcaps; @@ -336,8 +347,9 @@ extern int uid_shift_ttys(int pid, struct lxc_conf *conf); * Configure the container from inside */ +struct cgroup_process_info; extern int lxc_setup(const char *name, struct lxc_conf *lxc_conf, - const char *lxcpath); + const char *lxcpath, struct cgroup_process_info *cgroup_info); extern void lxc_rename_phys_nics_on_shutdown(struct lxc_conf *conf); #endif diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 7904db4..b378c3a 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1235,11 +1235,60 @@ static int config_fstab(const char *key, const char *value, return config_path_item(key, value, lxc_conf, &lxc_conf->fstab); } +static int config_mount_auto(const char *key, const char *value, + struct lxc_conf *lxc_conf) +{ + char *autos, *autoptr, *sptr, *token; + static struct { const char *token; int flag; } allowed_auto_mounts[] = { + { "proc", LXC_AUTO_PROC }, + { "sysrq", LXC_AUTO_PROC_SYSRQ }, + { "sys", LXC_AUTO_SYS }, + { "cgroup", LXC_AUTO_CGROUP }, + { NULL, 0 } + }; + int i; + int ret = -1; + + if (!strlen(value)) + return -1; + + autos = strdup(value); + if (!autos) { + SYSERROR("failed to dup '%s'", value); + return -1; + } + + for (autoptr = autos; ; autoptr = NULL) { + token = strtok_r(autoptr, " \t", &sptr); + if (!token) { + ret = 0; + break; + } + + for (i = 0; allowed_auto_mounts[i].token; i++) { + if (!strcmp(allowed_auto_mounts[i].token, token)) + break; + } + + if (!allowed_auto_mounts[i].token) { + ERROR("Invalid filesystem to automount: %s", token); + break; + } + + lxc_conf->auto_mounts |= allowed_auto_mounts[i].flag; + } + + free(autos); + + return ret; +} + static int config_mount(const char *key, const char *value, struct lxc_conf *lxc_conf) { char *fstab_token = "lxc.mount"; char *token = "lxc.mount.entry"; + char *auto_token = "lxc.mount.auto"; char *subkey; char *mntelem; struct lxc_list *mntlist; @@ -1247,12 +1296,18 @@ static int config_mount(const char *key, const char *value, subkey = strstr(key, token); if (!subkey) { - subkey = strstr(key, fstab_token); + subkey = strstr(key, auto_token); - if (!subkey) - return -1; + if (!subkey) { + subkey = strstr(key, fstab_token); + + if (!subkey) + return -1; + + return config_fstab(key, value, lxc_conf); + } - return config_fstab(key, value, lxc_conf); + return config_mount_auto(key, value, lxc_conf); } if (!strlen(subkey)) diff --git a/src/lxc/start.c b/src/lxc/start.c index 56a2e6b..48a06cf 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -525,7 +525,7 @@ static int do_start(void *data) #endif /* Setup the container, ip, names, utsname, ... */ - if (lxc_setup(handler->name, handler->conf, handler->lxcpath)) { + if (lxc_setup(handler->name, handler->conf, handler->lxcpath, handler->cgroup)) { ERROR("failed to setup the container"); goto out_warn_father; } -- 1.7.10.4 ------------------------------------------------------------------------------ How ServiceNow helps IT people transform IT departments: 1. Consolidate legacy IT systems to a single system of record for IT 2. Standardize and globalize service processes across IT 3. Implement zero-touch automation to replace manual, redundant tasks http://pubads.g.doubleclick.net/gampad/clk?id=51271111&iu=/4140/ostg.clktrk _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel