[lxc-devel] [lxc/master] confile: add maskedpaths and readonlypaths to protect unsafe places

tanyifeng on Github lxc-bot at linuxcontainers.org
Wed Dec 13 08:17:00 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 350 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20171213/fccfdce3/attachment.bin>
-------------- next part --------------
From 00d48b0c230480aad999f2fb64a5290d126c430c Mon Sep 17 00:00:00 2001
From: Yifeng Tan <tanyifeng1 at huawei.com>
Date: Tue, 12 Dec 2017 02:29:36 +0800
Subject: [PATCH] confile: add maskedpaths and readonlypaths to protect unsafe
 places

Signed-off-by: Yifeng Tan <tanyifeng1 at huawei.com>
---
 doc/lxc.container.conf.sgml.in |  24 ++++++++
 src/lxc/conf.c                 | 127 +++++++++++++++++++++++++++++++++++++++++
 src/lxc/conf.h                 |   6 ++
 src/lxc/confile.c              | 104 +++++++++++++++++++++++++++++++++
 src/tests/parse_config_file.c  |  18 +++++-
 5 files changed, 277 insertions(+), 2 deletions(-)

diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
index 7a6dd4841..eebd15c66 100644
--- a/doc/lxc.container.conf.sgml.in
+++ b/doc/lxc.container.conf.sgml.in
@@ -1200,6 +1200,30 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term>
+            <option>lxc.rootfs.ropath</option>
+          </term>
+          <listitem>
+            <para>
+              Specifies paths within the container's rootfs to remount as read-only
+              so that these files prevent any writes.
+            </para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>
+            <option>lxc.rootfs.maskedpath</option>
+          </term>
+          <listitem>
+            <para>
+              specifies paths within the container's rootfs to mask over with a bind
+              mount pointing to /dev/null as to prevent reads of the file.
+            </para>
+          </listitem>
+        </varlistentry>
+
       </variablelist>
     </refsect2>
 
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 6a452064b..f1c628a3b 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -2339,6 +2339,68 @@ static int parse_resource(const char *res) {
 	return -1;
 }
 
+// maskPath masks the top of the specified path inside a container to avoid
+// security issues from processes reading information from non-namespace aware
+// mounts ( proc/kcore ).
+static bool mask_path(const char *path)
+{
+	int ret, savederrno;
+
+	if (!path)
+		return true;
+
+	ret = mount("/dev/null", path, "", MS_BIND, "");
+	if (ret < 0 && errno != ENOENT) {
+		savederrno = errno;
+		if (savederrno == ENOTDIR) {
+			ret = mount("tmpfs", path, "tmpfs", MS_RDONLY, "");
+			if (ret < 0) {
+				savederrno = errno;
+				goto error;
+			}
+			return true;
+		}
+		goto error;
+	}
+
+	return true;
+
+error:
+	SYSERROR("Failed to mask path \"%s\": %s", path, strerror(savederrno));
+	return false;
+}
+
+// readonly_path will make a path read only.
+static bool readonly_path(const char *path)
+{
+
+	int ret, savederrno;
+
+	if (!path)
+		return true;
+
+	ret = mount(path, path, "", MS_BIND | MS_REC, "");
+	if (ret < 0) {
+		savederrno = errno;
+		if (savederrno == ENOENT)
+			return true;
+
+		goto error;
+	}
+
+	ret = mount(path, path, "", MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, "");
+	if (ret < 0) {
+		savederrno = errno;
+		goto error;
+	}
+
+	return true;
+
+error:
+	SYSERROR("unable to mount \"%s\" to readonly: %s", path, strerror(savederrno));
+	return false;
+}
+
 int setup_resource_limits(struct lxc_list *limits, pid_t pid) {
 	struct lxc_list *it;
 	struct lxc_limit *lim;
@@ -2453,6 +2515,8 @@ struct lxc_conf *lxc_conf_init(void)
 		lxc_list_init(&new->hooks[i]);
 	lxc_list_init(&new->groups);
 	lxc_list_init(&new->state_clients);
+	lxc_list_init(&new->rootfs.maskedpaths);
+	lxc_list_init(&new->rootfs.ropaths);
 	new->lsm_aa_profile = NULL;
 	new->lsm_se_context = NULL;
 	new->tmp_umount_proc = 0;
@@ -3012,6 +3076,30 @@ void lxc_execute_bind_init(struct lxc_conf *conf)
 	INFO("lxc.init.static bound into container at %s", path);
 }
 
+static int setup_rootfs_maskedpaths(struct lxc_list *maskedpaths)
+{
+	struct lxc_list *it;
+
+	lxc_list_for_each(it, maskedpaths) {
+		if (!mask_path((char *)it->elem))
+			return -1;
+	}
+
+	return 0;
+}
+
+static int setup_rootfs_ropaths(struct lxc_list *ropaths)
+{
+	struct lxc_list *it;
+
+	lxc_list_for_each(it, ropaths) {
+		if (!readonly_path((char *)it->elem))
+			return -1;
+	}
+
+	return 0;
+}
+
 /*
  * This does the work of remounting / if it is shared, calling the
  * container pre-mount hooks, and mounting the rootfs.
@@ -3192,6 +3280,18 @@ int lxc_setup(struct lxc_handler *handler)
 	if (ret < 0)
 		return -1;
 
+	if (!lxc_list_empty(&lxc_conf->rootfs.ropaths)) {
+		ret = setup_rootfs_ropaths(&lxc_conf->rootfs.ropaths);
+		if (ret < 0)
+			return -1;
+	}
+
+	if (!lxc_list_empty(&lxc_conf->rootfs.maskedpaths)) {
+		ret = setup_rootfs_maskedpaths(&lxc_conf->rootfs.maskedpaths);
+		if (ret < 0)
+			return -1;
+	}
+
 	if (setup_personality(lxc_conf->personality)) {
 		ERROR("failed to setup personality");
 		return -1;
@@ -3408,6 +3508,31 @@ int lxc_clear_environment(struct lxc_conf *c)
 	return 0;
 }
 
+
+int lxc_clear_rootfs_maskedpaths(struct lxc_conf *c)
+{
+	struct lxc_list *it,*next;
+
+	lxc_list_for_each_safe(it, &c->rootfs.maskedpaths, next) {
+		lxc_list_del(it);
+		free(it->elem);
+		free(it);
+	}
+	return 0;
+}
+
+int lxc_clear_rootfs_ropaths(struct lxc_conf *c)
+{
+	struct lxc_list *it,*next;
+
+	lxc_list_for_each_safe(it, &c->rootfs.ropaths, next) {
+		lxc_list_del(it);
+		free(it->elem);
+		free(it);
+	}
+	return 0;
+}
+
 int lxc_clear_mount_entries(struct lxc_conf *c)
 {
 	struct lxc_list *it,*next;
@@ -3522,6 +3647,8 @@ void lxc_conf_free(struct lxc_conf *conf)
 	lxc_clear_includes(conf);
 	lxc_clear_aliens(conf);
 	lxc_clear_environment(conf);
+	lxc_clear_rootfs_maskedpaths(conf);
+	lxc_clear_rootfs_ropaths(conf);
 	lxc_clear_limits(conf, "lxc.prlimit");
 	lxc_clear_sysctls(conf, "lxc.sysctl");
 	free(conf->cgroup_meta.dir);
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index e60a6151f..38126e633 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -185,12 +185,16 @@ struct lxc_console {
  * @mount      : where it is mounted
  * @options    : mount options
  * @bev_type   : optional backing store type
+ * @maskedpaths: A list of paths to be msked over inside the container
+ * @ropaths    : A list of paths to be remounted with readonly inside the container
  */
 struct lxc_rootfs {
 	char *path;
 	char *mount;
 	char *options;
 	char *bdev_type;
+	struct lxc_list maskedpaths;
+	struct lxc_list ropaths;
 };
 
 /*
@@ -415,6 +419,8 @@ extern int lxc_clear_environment(struct lxc_conf *c);
 extern int lxc_clear_limits(struct lxc_conf *c, const char *key);
 extern int lxc_delete_autodev(struct lxc_handler *handler);
 extern void lxc_clear_includes(struct lxc_conf *conf);
+extern int lxc_clear_rootfs_maskedpaths(struct lxc_conf *c);
+extern int lxc_clear_rootfs_ropaths(struct lxc_conf *c);
 extern int do_rootfs_setup(struct lxc_conf *conf, const char *name,
 			   const char *lxcpath);
 extern int lxc_setup(struct lxc_handler *handler);
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 584f93630..3caa52d80 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -132,6 +132,8 @@ lxc_config_define(rootfs_backend);
 lxc_config_define(rootfs_mount);
 lxc_config_define(rootfs_options);
 lxc_config_define(rootfs_path);
+lxc_config_define(rootfs_maskedpath);
+lxc_config_define(rootfs_ropath);
 lxc_config_define(seccomp_profile);
 lxc_config_define(selinux_context);
 lxc_config_define(signal_halt);
@@ -231,6 +233,8 @@ static struct lxc_config_t config[] = {
 	{ "lxc.rootfs.mount",              false,                  set_config_rootfs_mount,                get_config_rootfs_mount,                clr_config_rootfs_mount,              },
 	{ "lxc.rootfs.options",            false,                  set_config_rootfs_options,              get_config_rootfs_options,              clr_config_rootfs_options,            },
 	{ "lxc.rootfs.path",               false,                  set_config_rootfs_path,                 get_config_rootfs_path,                 clr_config_rootfs_path,               },
+	{ "lxc.rootfs.maskedpath",         false,                  set_config_rootfs_maskedpath,           get_config_rootfs_maskedpath,           clr_config_rootfs_maskedpath,         },
+	{ "lxc.rootfs.ropath",             false,                  set_config_rootfs_ropath,               get_config_rootfs_ropath,               clr_config_rootfs_ropath,             },
 	{ "lxc.seccomp.profile",           false,                  set_config_seccomp_profile,             get_config_seccomp_profile,             clr_config_seccomp_profile,           },
 	{ "lxc.selinux.context",           false,                  set_config_selinux_context,             get_config_selinux_context,             clr_config_selinux_context,           },
 	{ "lxc.signal.halt",               false,                  set_config_signal_halt,                 get_config_signal_halt,                 clr_config_signal_halt,               },
@@ -1125,6 +1129,58 @@ static int set_config_environment(const char *key, const char *value,
 	return -1;
 }
 
+static int set_config_rootfs_maskedpath(const char *key, const char *value,
+				  struct lxc_conf *lxc_conf, void *data)
+{
+	struct lxc_list *list_item = NULL;
+
+	if (lxc_config_value_empty(value))
+		return clr_config_rootfs_maskedpath(key, lxc_conf, data);
+
+	list_item = malloc(sizeof(*list_item));
+	if (!list_item)
+		goto on_error;
+
+	list_item->elem = strdup(value);
+
+	if (!list_item->elem)
+		goto on_error;
+
+	lxc_list_add_tail(&lxc_conf->rootfs.maskedpaths, list_item);
+
+	return 0;
+
+on_error:
+	free(list_item);
+	return -1;
+}
+
+static int set_config_rootfs_ropath(const char *key, const char *value,
+				  struct lxc_conf *lxc_conf, void *data)
+{
+	struct lxc_list *list_item = NULL;
+
+	if (lxc_config_value_empty(value))
+		return clr_config_rootfs_ropath(key, lxc_conf, data);
+
+	list_item = malloc(sizeof(*list_item));
+	if (!list_item)
+		goto on_error;
+
+	list_item->elem = strdup(value);
+
+	if (!list_item->elem)
+		goto on_error;
+
+	lxc_list_add_tail(&lxc_conf->rootfs.ropaths, list_item);
+
+	return 0;
+
+on_error:
+	free(list_item);
+	return -1;
+}
+
 static int set_config_tty_max(const char *key, const char *value,
 			      struct lxc_conf *lxc_conf, void *data)
 {
@@ -3278,6 +3334,42 @@ static int get_config_environment(const char *key, char *retv, int inlen,
 	return fulllen;
 }
 
+static int get_config_rootfs_maskedpath(const char *key, char *retv, int inlen,
+				  struct lxc_conf *c, void *data)
+{
+	int len, fulllen = 0;
+	struct lxc_list *it;
+
+	if (!retv)
+		inlen = 0;
+	else
+		memset(retv, 0, inlen);
+
+	lxc_list_for_each(it, &c->rootfs.maskedpaths) {
+		strprint(retv, inlen, "%s\n", (char *)it->elem);
+	}
+
+	return fulllen;
+}
+
+static int get_config_rootfs_ropath(const char *key, char *retv, int inlen,
+				  struct lxc_conf *c, void *data)
+{
+	int len, fulllen = 0;
+	struct lxc_list *it;
+
+	if (!retv)
+		inlen = 0;
+	else
+		memset(retv, 0, inlen);
+
+	lxc_list_for_each(it, &c->rootfs.ropaths) {
+		strprint(retv, inlen, "%s\n", (char *)it->elem);
+	}
+
+	return fulllen;
+}
+
 static int get_config_execute_cmd(const char *key, char *retv, int inlen,
 			       struct lxc_conf *c, void *data)
 {
@@ -3584,6 +3676,18 @@ static inline int clr_config_rootfs_backend(const char *key, struct lxc_conf *c,
 	return 0;
 }
 
+static inline int clr_config_rootfs_maskedpath(const char *key, struct lxc_conf *c,
+						void *data)
+{
+	return lxc_clear_rootfs_maskedpaths(c);
+}
+
+static inline int clr_config_rootfs_ropath(const char *key, struct lxc_conf *c,
+						void *data)
+{
+	return lxc_clear_rootfs_ropaths(c);
+}
+
 static inline int clr_config_uts_name(const char *key, struct lxc_conf *c,
 				     void *data)
 {
diff --git a/src/tests/parse_config_file.c b/src/tests/parse_config_file.c
index 556d6d034..6ca97986b 100644
--- a/src/tests/parse_config_file.c
+++ b/src/tests/parse_config_file.c
@@ -478,7 +478,7 @@ int main(int argc, char *argv[])
 	}
 
 	if (!c->get_config_item(c, "lxc.id_map", retval, sizeof(retval))) {
-		lxc_error("%s\n", "failed to get config item \"lxc.cgroup\"");
+		lxc_error("%s\n", "failed to get config item \"lxc.id_map\"");
 		return -1;
 	}
 
@@ -506,7 +506,7 @@ int main(int argc, char *argv[])
 	}
 
 	if (!c->get_config_item(c, "lxc.idmap", retval, sizeof(retval))) {
-		lxc_error("%s\n", "failed to get config item \"lxc.cgroup\"");
+		lxc_error("%s\n", "failed to get config item \"lxc.idmap\"");
 		return -1;
 	}
 
@@ -615,6 +615,20 @@ int main(int argc, char *argv[])
 		goto non_test_error;
 	}
 
+	/* lxc.rootfs.ropath */
+	if (set_get_compare_clear_save_load(c, "lxc.rootfs.ropath",
+					    "/proc/fs", tmpf, false) < 0) {
+		lxc_error("%s\n", "lxc.rootfs.ropath");
+		goto non_test_error;
+	}
+
+	/* lxc.rootfs.mskedpath */
+	if (set_get_compare_clear_save_load(c, "lxc.rootfs.maskedpath",
+					    "/proc/kcore", tmpf, false) < 0) {
+		lxc_error("%s\n", "lxc.rootfs.maskedpath");
+		goto non_test_error;
+	}
+
 	/* REMOVE IN LXC 3.0
 	   legacy lxc.utsname key
 	 */


More information about the lxc-devel mailing list