[RFC 0/4] per-namespace allowed filesystems list

3 messages, 2 authors, 2012-01-23 · open the first message on its own page

[RFC 0/4] per-namespace allowed filesystems list

From: Glauber Costa <hidden>
Date: 2012-01-23 16:56:08

This patch creates a list of allowed filesystems per-namespace.
The goal is to prevent users inside a container, even root,
to mount filesystems that are not allowed by the main box admin.

My main two motivators to pursue this are:
 1) We want to prevent a certain tailored view of some virtual
    filesystems, for example, by bind-mounting files with userspace
    generated data into /proc. The ability of mounting /proc inside
    the container works against this effort, while disallowing it
    via capabilities would have the effect of disallowing other
    mounts as well.

2) Some filesystems are known not to behave well under a container
   environment. They require changes to work in a safe-way. We can
   whitelist only the filesystems we want.

This works as a whitelist. Only filesystems in the list are allowed
to be mounted. Doing a blacklist would create problems when, say,
a module is loaded. The whitelist is only checked if it is enabled first.
So any setup that was already working, will keep working. And whoever
is not interested in limiting filesystem mount, does not need
to bother about it.

Please let me know what you guys think about it.

Glauber Costa (4):
  move /proc/filesystems inside /proc/self
  per-namespace allowed filesystems list
  show only allowed filesystems in /proc/filesystems
  fslist netlink interface

 fs/Kconfig                     |    9 +++
 fs/Makefile                    |    1 +
 fs/filesystems.c               |  108 ++++++++++++++++++++++++------
 fs/fsnetlink.c                 |  145 ++++++++++++++++++++++++++++++++++++++++
 fs/namespace.c                 |    5 +-
 fs/proc/base.c                 |   64 +++++++++++++++---
 fs/proc/root.c                 |    1 +
 include/linux/fs.h             |   11 +++
 include/linux/fslist_netlink.h |   35 ++++++++++
 include/linux/mnt_namespace.h  |   20 ++++++
 10 files changed, 368 insertions(+), 31 deletions(-)
 create mode 100644 fs/fsnetlink.c
 create mode 100644 include/linux/fslist_netlink.h

-- 
1.7.7.4

[RFC 3/4] show only allowed filesystems in /proc/filesystems

From: Glauber Costa <hidden>
Date: 2012-01-23 16:57:18

Now that a namespace can have a different than default list of
filesystems, only show the allowed ones in /proc/filesystems.

Signed-off-by: Glauber Costa <redacted>
---
 fs/filesystems.c |    4 +++-
 fs/proc/base.c   |   51 +++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 44 insertions(+), 11 deletions(-)
diff --git a/fs/filesystems.c b/fs/filesystems.c
index 118d0d6..b797cda 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -243,11 +243,13 @@ out:
 int filesystems_proc_show(struct seq_file *m, void *v)
 {
 	struct file_system_type * tmp;
+	struct mnt_namespace *ns = m->private;
 
 	read_lock(&file_systems_lock);
 	tmp = file_systems;
 	while (tmp) {
-		seq_printf(m, "%s\t%s\n",
+		if (fs_allowed(tmp, ns))
+			seq_printf(m, "%s\t%s\n",
 			(tmp->fs_flags & FS_REQUIRES_DEV) ? "" : "nodev",
 			tmp->name);
 		tmp = tmp->next;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 2a6e2c7..2a88a47 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -627,6 +627,44 @@ int proc_setattr(struct dentry *dentry, struct iattr *attr)
 	return 0;
 }
 
+struct mnt_namespace *mnt_ns_from_task(struct task_struct *task)
+{
+	struct nsproxy *nsp;
+	struct mnt_namespace *ns = NULL;
+
+
+	rcu_read_lock();
+	nsp = task_nsproxy(task);
+	if (nsp) {
+		ns = nsp->mnt_ns;
+		if (ns)
+			get_mnt_ns(ns);
+	}
+	rcu_read_unlock();
+	return ns;
+}
+
+struct mnt_namespace *mnt_ns_from_inode(struct inode *inode)
+{
+	struct task_struct *task = get_proc_task(inode);
+	struct path root;
+	struct mnt_namespace *ns = NULL;
+
+	if (!task)
+		return NULL;
+
+	ns = mnt_ns_from_task(task);
+
+	if (ns && get_task_root(task, &root) != 0) {
+		put_mnt_ns(ns);
+		ns = NULL;
+	}
+
+	path_put(&root);
+	put_task_struct(task);
+	return ns;
+}
+
 static const struct inode_operations proc_def_inode_operations = {
 	.setattr	= proc_setattr,
 };
@@ -635,21 +673,13 @@ static int mounts_open_common(struct inode *inode, struct file *file,
 			      const struct seq_operations *op)
 {
 	struct task_struct *task = get_proc_task(inode);
-	struct nsproxy *nsp;
 	struct mnt_namespace *ns = NULL;
 	struct path root;
 	struct proc_mounts *p;
 	int ret = -EINVAL;
 
 	if (task) {
-		rcu_read_lock();
-		nsp = task_nsproxy(task);
-		if (nsp) {
-			ns = nsp->mnt_ns;
-			if (ns)
-				get_mnt_ns(ns);
-		}
-		rcu_read_unlock();
+		ns = mnt_ns_from_task(task);
 		if (ns && get_task_root(task, &root) == 0)
 			ret = 0;
 		put_task_struct(task);
@@ -2875,7 +2905,8 @@ static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *de
 
 static int filesystems_proc_open(struct inode *inode, struct file *file)
 {
-	return single_open(file, filesystems_proc_show, NULL);
+	struct mnt_namespace *ns = mnt_ns_from_inode(inode);
+	return single_open(file, filesystems_proc_show, ns);
 }
 
 static const struct file_operations filesystems_proc_fops = {
-- 
1.7.7.4

[RFC 2/4] per-namespace allowed filesystems list

From: Glauber Costa <hidden>
Date: 2012-01-23 16:57:18

This patch creates a list of allowed filesystems per-namespace.
The goal is to prevent users inside a container, even root,
to mount filesystems that are not allowed by the main box admin.

My main two motivators to pursue this are:
 1) We want to prevent a certain tailored view of some virtual
    filesystems, for example, by bind-mounting files with userspace
    generated data into /proc. The ability of mounting /proc inside
    the container works against this effort, while disallowing it
    via capabilities would have the effect of disallowing other
    mounts as well.

2) Some filesystems are known not to behave well under a container
   environment. They require changes to work in a safe-way. We can
   whitelist only the filesystems we want.

This works as a whitelist. Only filesystems in the list are allowed
to be mounted. Doing a blacklist would create problems when, say,
a module is loaded. The whitelist is only checked if it is enabled first.
So any setup that was already working, will keep working. And whoever
is not interested in limiting filesystem mount, does not need
to bother about it.

Signed-off-by: Glauber Costa <redacted>
---
 fs/filesystems.c              |   83 +++++++++++++++++++++++++++++++++++++++++
 fs/namespace.c                |    5 ++-
 include/linux/fs.h            |    9 ++++
 include/linux/mnt_namespace.h |   20 ++++++++++
 4 files changed, 116 insertions(+), 1 deletions(-)
diff --git a/fs/filesystems.c b/fs/filesystems.c
index 458d120..118d0d6 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/mnt_namespace.h>
 #include <asm/uaccess.h>
 
 /*
@@ -218,6 +219,26 @@ int __init get_filesystem_list(char *buf)
 	return len;
 }
 
+static bool fs_allowed(struct file_system_type *fs, struct mnt_namespace *mnt)
+{
+	struct fs_allowed *p;
+	bool ret = true;
+
+	if (!fslist_is_enabled(mnt))
+		goto out;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(p, &mnt->fs_allowed, list)
+		if (p->fstype == fs)
+			goto out_rcu;
+
+	ret = false;
+out_rcu:
+	rcu_read_unlock();
+out:
+	return ret;
+}
+
 #ifdef CONFIG_PROC_FS
 int filesystems_proc_show(struct seq_file *m, void *v)
 {
@@ -265,4 +286,66 @@ struct file_system_type *get_fs_type(const char *name)
 	return fs;
 }
 
+void destroy_filesystems_list(struct mnt_namespace *mnt)
+{
+	struct fs_allowed *fs;
+
+	WARN_ON(!mnt);
+
+	if (!fslist_is_enabled(mnt))
+		return;
+	mutex_lock(&mnt->fs_list_mutex);
+	synchronize_rcu();
+
+	list_for_each_entry(fs, &mnt->fs_allowed, list) {
+		list_del(&fs->list);
+		kfree(fs);
+	}
+	mutex_unlock(&mnt->fs_list_mutex);
+}
+
+void enable_filesystems_list(struct mnt_namespace *mnt)
+{
+	mnt->fs_list_enabled = true;
+}
+
+int add_filesystem_list(const char *name, struct mnt_namespace *mnt)
+{
+	struct file_system_type **fstype;
+	struct fs_allowed *fs;
+
+	if (!fslist_is_enabled(mnt))
+		return -EINVAL;
+
+	fstype = find_filesystem(name, strlen(name));
+	if (!fstype)
+		return -EINVAL;
+
+	if (fs_allowed(*fstype, mnt))
+		return 0;
+
+	fs = kmalloc(sizeof(*fs), GFP_KERNEL);
+	if (!fs)
+		return -ENOMEM;
+
+	fs->fstype = *fstype;
+
+	mutex_lock(&mnt->fs_list_mutex);
+	list_add_rcu(&fs->list, &mnt->fs_allowed);
+	mutex_unlock(&mnt->fs_list_mutex);
+
+	return 0;
+}
+
+struct file_system_type *get_fs_type_ns(const char *name,
+					struct mnt_namespace *mnt)
+{
+	struct file_system_type *fs = get_fs_type(name);
+
+	if (fs && mnt && !fs_allowed(fs, mnt)) {
+		put_filesystem(fs);
+		fs = NULL;
+	}
+	return fs;
+}
 EXPORT_SYMBOL(get_fs_type);
diff --git a/fs/namespace.c b/fs/namespace.c
index cfc6d44..e897985 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1958,7 +1958,8 @@ static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
 struct vfsmount *
 do_kern_mount(const char *fstype, int flags, const char *name, void *data)
 {
-	struct file_system_type *type = get_fs_type(fstype);
+	struct file_system_type *type = get_fs_type_ns(fstype,
+					current->nsproxy->mnt_ns);
 	struct vfsmount *mnt;
 	if (!type)
 		return ERR_PTR(-ENODEV);
@@ -2365,6 +2366,7 @@ static struct mnt_namespace *alloc_mnt_ns(void)
 	INIT_LIST_HEAD(&new_ns->list);
 	init_waitqueue_head(&new_ns->poll);
 	new_ns->event = 0;
+	init_fslist(new_ns);
 	return new_ns;
 }
 
@@ -2745,6 +2747,7 @@ void put_mnt_ns(struct mnt_namespace *ns)
 	br_write_unlock(vfsmount_lock);
 	up_write(&namespace_sem);
 	release_mounts(&umount_list);
+	destroy_filesystems_list(ns);
 	kfree(ns);
 }
 EXPORT_SYMBOL(put_mnt_ns);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3286d74..ab3633a 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2531,6 +2531,15 @@ extern void put_filesystem(struct file_system_type *fs);
 extern struct file_system_type *get_fs_type(const char *name);
 extern int filesystems_proc_show(struct seq_file *m, void *v);
 
+
+struct mnt_namespace;
+extern struct file_system_type *get_fs_type_ns(const char *name,
+					       struct mnt_namespace *mnt);
+extern void enable_filesystems_list(struct mnt_namespace *ns);
+extern void destroy_filesystems_list(struct mnt_namespace *ns);
+extern int add_filesystem_list(const char *name, struct mnt_namespace *ns);
+extern int del_filesystem_list(char *name, struct mnt_namespace *ns);
+
 extern struct super_block *get_super(struct block_device *);
 extern struct super_block *get_active_super(struct block_device *bdev);
 extern struct super_block *user_get_super(dev_t);
diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h
index 2930485..4138fb4 100644
--- a/include/linux/mnt_namespace.h
+++ b/include/linux/mnt_namespace.h
@@ -6,12 +6,20 @@
 #include <linux/seq_file.h>
 #include <linux/wait.h>
 
+struct fs_allowed {
+	struct list_head	list;
+	struct file_system_type *fstype;
+};
+
 struct mnt_namespace {
 	atomic_t		count;
 	struct vfsmount *	root;
 	struct list_head	list;
 	wait_queue_head_t poll;
 	int event;
+	struct list_head	fs_allowed;
+	struct mutex		fs_list_mutex;
+	bool			fs_list_enabled;
 };
 
 struct proc_mounts {
@@ -22,6 +30,18 @@ struct proc_mounts {
 
 struct fs_struct;
 
+static inline bool fslist_is_enabled(struct mnt_namespace *mnt)
+{
+	return mnt->fs_list_enabled;
+}
+
+static inline void init_fslist(struct mnt_namespace *ns)
+{
+	ns->fs_list_enabled = false;
+	INIT_LIST_HEAD(&ns->fs_allowed);
+	mutex_init(&ns->fs_list_mutex);
+}
+
 extern struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt);
 extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
 		struct fs_struct *);
-- 
1.7.7.4
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help