[lxc-devel] [PATCH 4/4] Support for automatic mounting of filesystems

Christian Seiler christian at iwakd.de
Wed Sep 11 23:44:44 UTC 2013


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 <christian at 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





More information about the lxc-devel mailing list