Skip to contentSkip to conversation

Namespace contrained helper execution

RFC PATCH In review Ian Kent 6 participants 27 messages Nov 25, 2014 – Dec 3, 2014

Subsystems (4)
lore
Share & tools
lore

Ian Kent

author
#0 lore raw

cover Namespace contrained helper execution

Hi all,

Some time ago an attempt was made to update call_usermodehelper() to execute within it's namespace.

Comments at the time were basically that the approach didn't go nearly far enough to constrain the process.

This series attempts to remedy that by taking care to create an appropriate namespace environment then switch to it and setup fs_struct for path walking prior to the user mode helper thread runner calling do_execve().

Please review and comment on the patch series.
Ian

---

Benjamin Coddington (1):
KEYS: exec request-key within the requesting task's namespace

Ian Kent (3):
vfs - fs/namespaces.c: break out mntns_setfs() from mntns_install() nsproxy - make create_new_namespaces() non-static
kmod - add call_usermodehelper_ns() helper

 fs/namespace.c              |   41 ++++++++++++++++++++++++++++-----------
 include/linux/kmod.h        |   17 ++++++++++++++++
 include/linux/mount.h       |    1 +
 include/linux/nsproxy.h     |    3 +++
 kernel/kmod.c               |   39 +++++++++++++++++++++++++++++++++++++
 kernel/nsproxy.c            |    2 +-
 security/keys/request_key.c |   45 +++++++++++++++++++++++++++++++++++++------
 7 files changed, 129 insertions(+), 19 deletions(-)

--
Signature

author
#1 lore raw

RFC PATCH 1/4 vfs - fs/namespaces.c: break out mntns_setfs() from mntns_install()

Some users of kmod.c's usermodehelper need to setup their fs_struct root and pwd based on the callers namespaces after the kernel thread runner has created a new process but before do_execve() is called.

Break out the fs_struct portion of mntns_install so it can be used for this purpose.

Cc Al Viro <[email protected]>
Cc J. Bruce Fields <[email protected]>
Cc David Howells <[email protected]>
Cc Trond Myklebust <[email protected]>
Cc Stanislav Kinsbursky <[email protected]>
Cc Oleg Nesterov <[email protected]>
Cc Eric W. Biederman <[email protected]>
---
 fs/namespace.c        |   41 +++++++++++++++++++++++++++++------------
 include/linux/mount.h |    1 +
 2 files changed, 30 insertions(+), 12 deletions(-)
fs/namespace.c +29 -12
index 5b66b2b..3cdbb9e 100644
@@ -3165,11 +3165,38 @@ static void mntns_put(void *ns)
 	put_mnt_ns(ns);
 }
 
+/*
+ * Set fs root and pwd for a subsequent call to do_execve().
+ *
+ * This assumes that an nsproxy has been created within the
+ * namespace context that is the target for the exec so that
+ * mnt_ns is already set. Additionally, since the nsproxy is
+ * created within the requesting namespace context the security
+ * checks of mntns_install() aren't required.
+ */
+void mntns_setfs(struct mnt_namespace *mnt_ns)
+{
+	struct fs_struct *fs = current->fs;
+	struct path root;
+
+	/* Find the root */
+	root.mnt    = &mnt_ns->root->mnt;
+	root.dentry = mnt_ns->root->mnt.mnt_root;
+	path_get(&root);
+	while(d_mountpoint(root.dentry) && follow_down_one(&root))
+		;
+
+	/* Update the pwd and root */
+	set_fs_pwd(fs, &root);
+	set_fs_root(fs, &root);
+
+	path_put(&root);
+}
+
 static int mntns_install(struct nsproxy *nsproxy, void *ns)
 {
 	struct fs_struct *fs = current->fs;
 	struct mnt_namespace *mnt_ns = ns;
-	struct path root;
 
 	if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) ||
 	    !ns_capable(current_user_ns(), CAP_SYS_CHROOT) ||
@@ -3183,18 +3210,8 @@ static int mntns_install(struct nsproxy *nsproxy, void *ns)
 	put_mnt_ns(nsproxy->mnt_ns);
 	nsproxy->mnt_ns = mnt_ns;
 
-	/* Find the root */
-	root.mnt    = &mnt_ns->root->mnt;
-	root.dentry = mnt_ns->root->mnt.mnt_root;
-	path_get(&root);
-	while(d_mountpoint(root.dentry) && follow_down_one(&root))
-		;
-
-	/* Update the pwd and root */
-	set_fs_pwd(fs, &root);
-	set_fs_root(fs, &root);
+	mntns_setfs(mnt_ns);
 
-	path_put(&root);
 	return 0;
 }
 
include/linux/mount.h +1 -0
index c2c561d..a9f6548 100644
@@ -80,6 +80,7 @@ extern void mntput(struct vfsmount *mnt);
 extern struct vfsmount *mntget(struct vfsmount *mnt);
 extern struct vfsmount *mnt_clone_internal(struct path *path);
 extern int __mnt_is_readonly(struct vfsmount *mnt);
+void mntns_setfs(struct mnt_namespace *mnt_ns);
 
 struct path;
 extern struct vfsmount *clone_private_mount(struct path *path);
author
#2 lore raw

RFC PATCH 2/4 nsproxy - make create_new_namespaces() non-static

In reply to Ian Kent

create_new_namespaces() will be needed by call_usermodehelper_ns() and call_usermodehelper_keys() for namespace restricted execution.

Cc Al Viro <[email protected]>
Cc J. Bruce Fields <[email protected]>
Cc David Howells <[email protected]>
Cc Trond Myklebust <[email protected]>
Cc Stanislav Kinsbursky <[email protected]>
Cc Oleg Nesterov <[email protected]>
Cc Eric W. Biederman <[email protected]>
---
 include/linux/nsproxy.h |    3 +++
 kernel/nsproxy.c        |    2 +-
 2 files changed, 4 insertions(+), 1 deletion(-)
include/linux/nsproxy.h +3 -0
index 35fa08f..dfe7dda 100644
@@ -62,6 +62,9 @@ extern struct nsproxy init_nsproxy;
  *
  */
 
+struct nsproxy *create_new_namespaces(unsigned long flags,
+	struct task_struct *tsk, struct user_namespace *user_ns,
+	struct fs_struct *new_fs);
 int copy_namespaces(unsigned long flags, struct task_struct *tsk);
 void exit_task_namespaces(struct task_struct *tsk);
 void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new);
kernel/nsproxy.c +1 -1
index ef42d0a..dcfbb13 100644
@@ -56,7 +56,7 @@ static inline struct nsproxy *create_nsproxy(void)
  * Return the newly created nsproxy.  Do not attach this to the task,
  * leave it to the caller to do proper locking and attach it to task.
  */
-static struct nsproxy *create_new_namespaces(unsigned long flags,
+struct nsproxy *create_new_namespaces(unsigned long flags,
 	struct task_struct *tsk, struct user_namespace *user_ns,
 	struct fs_struct *new_fs)
 {
author
#3 lore raw

RFC PATCH 3/4 kmod - add call_usermodehelper_ns() helper

In reply to Ian Kent

The call_usermodehelper() function executes all binaries in the global "init" root context. This doesn't allow a binary to be run within the callers namespace (aka. a container). So create a new function call_usermodehelper_ns() to do this.

Both containerized NFS client and NFS server need the ability to execute a binary within their container. To do this create a new nsproxy within the callers' context so it can be used for setup prior to calling do_execve() from the user mode helper thread runner.

Cc Al Viro <[email protected]>
Cc J. Bruce Fields <[email protected]>
Cc David Howells <[email protected]>
Cc Trond Myklebust <[email protected]>
Cc Stanislav Kinsbursky <[email protected]>
Cc Oleg Nesterov <[email protected]>
Cc Eric W. Biederman <[email protected]>
---
 include/linux/kmod.h |   17 +++++++++++++++++
 kernel/kmod.c        |   39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+)
include/linux/kmod.h +17 -0
index 0555cc6..fd5509a 100644
@@ -69,6 +69,23 @@ struct subprocess_info {
 extern int
 call_usermodehelper(char *path, char **argv, char **envp, int wait);
 
+#if !defined(CONFIG_PROC_FS) || !defined(CONFIG_NAMESPACES)
+inline struct nsproxy *umh_open_ns(void)
+{
+	return NULL;
+}
+
+inline int
+call_usermodehelper_ns(char *path, char **argv, char **envp, int wait)
+{
+	return -ENOTSUP;
+}
+#else
+extern struct nsproxy *umh_open_ns(void);
+extern int
+call_usermodehelper_ns(char *path, char **argv, char **envp, int wait);
+#endif
+
 extern struct subprocess_info *
 call_usermodehelper_setup(char *path, char **argv, char **envp, gfp_t gfp_mask,
 			  int (*init)(struct subprocess_info *info, struct cred *new),
kernel/kmod.c +39 -0
index 80f7a6d..0ddcfbb 100644
@@ -39,6 +39,7 @@
 #include <linux/rwsem.h>
 #include <linux/ptrace.h>
 #include <linux/async.h>
+#include <linux/mount.h>
 #include <asm/uaccess.h>
 
 #include <trace/events/module.h>
@@ -642,6 +643,44 @@ int call_usermodehelper(char *path, char **argv, char **envp, int wait)
 }
 EXPORT_SYMBOL(call_usermodehelper);
 
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_NAMESPACES)
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);
+	switch_task_namespaces(current, ns);
+	return 0;
+}
+
+struct nsproxy *umh_open_ns(void)
+{
+	return create_new_namespaces(0, current, current_user_ns(), current->fs);
+}
+
+/* Call a usermode helper to execute within current namespace. */
+int call_usermodehelper_ns(char *path, char **argv, char **envp, int wait)
+{
+	struct subprocess_info *info;
+	struct nsproxy *ns;
+	gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
+
+	ns = umh_open_ns();
+	if (IS_ERR(ns))
+		return PTR_ERR(ns);
+
+	info = call_usermodehelper_setup(path, argv, envp,
+					 gfp_mask, umh_set_ns, NULL, ns);
+	if (!info) {
+		free_nsproxy(ns);
+		return -ENOMEM;
+	}
+
+	return call_usermodehelper_exec(info, wait);
+}
+EXPORT_SYMBOL(call_usermodehelper_ns);
+#endif
+
 static int proc_cap_handler(struct ctl_table *table, int write,
 			 void __user *buffer, size_t *lenp, loff_t *ppos)
 {
#4 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Quoted 4 lines · Ian Kent

The call_usermodehelper() function executes all binaries in the global "init" root context. This doesn't allow a binary to be run within the callers namespace (aka. a container).

Please see below.

Quoted 5 lines

Both containerized NFS client and NFS server need the ability to execute a binary within their container. To do this create a new nsproxy within the callers' context so it can be used for setup prior to calling do_execve() from the user mode helper thread runner.

and probably we also need this for coredump helpers, we want them to be per-namespace.

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 651

Quoted 5 lines
kernel/kmod.c +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 652

Quoted 1 line
kernel/kmod.c +1 -0
+	switch_task_namespaces(current, ns);

This doesn't look sane because this won't switch task_active_pid_ns().

And this reminds me another discussion, please look at
http://marc.info/?l=linux-kernel&m=138479570926192

Once again, this is just an idea to provoke more discussion. I am starting to think that perhaps we need pid_ns->umh_helper (init by default). And PR_SET_NS_UMH_HELPER.

Not sure.

Oleg.

#5 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 651

Quoted 6 lines
kernel/kmod.c +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

Oleg.

#6 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 25 lines · Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 651

Quoted 6 lines
kernel/kmod.c +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

That would certainly be a better approach, and roughly equivalent to what exists here. That would even ensure we remain in the proper cgroups, and lsm context.

The practical problem with the approach presented here is that I can hijack any user mode helper I wish, and make it run in any executable I wish as the global root user.

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

Eric

author
#7 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 34 lines · Eric W. Biederman
Quoted 25 lines · Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 651

Quoted 6 lines
kernel/kmod.c +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

That would certainly be a better approach, and roughly equivalent to what exists here. That would even ensure we remain in the proper cgroups, and lsm context.

Quoted 6 lines

The practical problem with the approach presented here is that I can hijack any user mode helper I wish, and make it run in any executable I wish as the global root user.

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

LOL, OK, so there's a problem with this.

But, how should a user mode helper execute within a namespace (or more specifically within a container)?

Suppose a user mode helper program scans through the pid list and somehow picks the correct process pid and then does an open()/setns()/execve().

Does that then satisfy the requirements? What needs to be done to safely do that in kernel?

The other approach I've considered is doing a full open()/setns() in kernel (since the caller already knows its pid) but it sounds like that's not right either.

Ian

#8 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 51 lines · Ian Kent
Quoted 34 lines · Eric W. Biederman
Quoted 25 lines · Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 651

Quoted 6 lines
kernel/kmod.c +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

That would certainly be a better approach, and roughly equivalent to what exists here. That would even ensure we remain in the proper cgroups, and lsm context.

Quoted 6 lines

The practical problem with the approach presented here is that I can hijack any user mode helper I wish, and make it run in any executable I wish as the global root user.

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

LOL, OK, so there's a problem with this.

But, how should a user mode helper execute within a namespace (or more specifically within a container)?

Suppose a user mode helper program scans through the pid list and somehow picks the correct process pid and then does an open()/setns()/execve().

Quoted 6 lines

Does that then satisfy the requirements? What needs to be done to safely do that in kernel?

The other approach I've considered is doing a full open()/setns() in kernel (since the caller already knows its pid) but it sounds like that's not right either.

The approach we agreed upon with the core dump helper was to provide enough information that userspace could figure out what was the appropriate policy and call nsenter/setns.

The only sane approach I can think of in the context of nfs is to fork a kernel thread at mount time that has all of the appropriate context because it was captured from the privileged mounting process, and use that kernel as the equivalent of kthreadd.

There may be some intermediate ground where we capture things or we use the init process of the pid namespace (captured at mount time) as our template/reference process.

If we are going to set this stuff up in the kernel we need a reference process that we can create children of because what is possible with respect to containers keeps changing, and it is extremely error prone to figure out what all othe crazy little bits are, and to update everything every time someone tweaks the kernel's capabilities. We have kthreadd because it was too error prone to scrub a userspace thread of all of the userspace bits and make it the equivalent of what kthreadd is today.

Of course it is also rather nice to have something to hang everything else on.

In summary we need a reference struct task that is all setup properly so that we can create an appropriate kernel thread.

Eric

author
#9 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 56 lines · Eric W. Biederman
Quoted 54 lines · Ian Kent
Quoted 34 lines · Eric W. Biederman
Quoted 25 lines · Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 651

Quoted 6 lines
kernel/kmod.c +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

That would certainly be a better approach, and roughly equivalent to what exists here. That would even ensure we remain in the proper cgroups, and lsm context.

Quoted 6 lines

The practical problem with the approach presented here is that I can hijack any user mode helper I wish, and make it run in any executable I wish as the global root user.

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

LOL, OK, so there's a problem with this.

But, how should a user mode helper execute within a namespace (or more specifically within a container)?

Suppose a user mode helper program scans through the pid list and somehow picks the correct process pid and then does an open()/setns()/execve().

Does that then satisfy the requirements? What needs to be done to safely do that in kernel?

Quoted 7 lines
Quoted 3 lines

The other approach I've considered is doing a full open()/setns() in kernel (since the caller already knows its pid) but it sounds like that's not right either.

The approach we agreed upon with the core dump helper was to provide enough information that userspace could figure out what was the appropriate policy and call nsenter/setns.

Your recommending I have a look at the core dump helper, that's fine, I'll do that.

Quoted 18 lines

The only sane approach I can think of in the context of nfs is to fork a kernel thread at mount time that has all of the appropriate context because it was captured from the privileged mounting process, and use that kernel as the equivalent of kthreadd.

There may be some intermediate ground where we capture things or we use the init process of the pid namespace (captured at mount time) as our template/reference process.

If we are going to set this stuff up in the kernel we need a reference process that we can create children of because what is possible with respect to containers keeps changing, and it is extremely error prone to figure out what all othe crazy little bits are, and to update everything every time someone tweaks the kernel's capabilities. We have kthreadd because it was too error prone to scrub a userspace thread of all of the userspace bits and make it the equivalent of what kthreadd is today.

Quoted 5 lines

Of course it is also rather nice to have something to hang everything else on.

In summary we need a reference struct task that is all setup properly so that we can create an appropriate kernel thread.

I'm having trouble understanding what your getting at here but I'm not that sharp so bear with me.

When call_usermodehelper() is called it's called from a process that is within the context within which the execution is required.

So what information do we not have available for setup?

Are you saying that the problem is that when the user mode helper run thread is invoked we don't have the information available that was present when call_usermodehelper() was called and that's where the challenge lies?

Ian

author
#10 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 12 lines · Ian Kent
Quoted 12 lines

If we are going to set this stuff up in the kernel we need a reference process that we can create children of because what is possible with respect to containers keeps changing, and it is extremely error prone to figure out what all othe crazy little bits are, and to update everything every time someone tweaks the kernel's capabilities. We have kthreadd because it was too error prone to scrub a userspace thread of all of the userspace bits and make it the equivalent of what kthreadd is today.

Of course it is also rather nice to have something to hang everything else on.

Quoted 8 lines
Quoted 2 lines

In summary we need a reference struct task that is all setup properly so that we can create an appropriate kernel thread.

I'm having trouble understanding what your getting at here but I'm not that sharp so bear with me.

When call_usermodehelper() is called it's called from a process that is within the context within which the execution is required.

Umm .. OK, that's probably not quite right either ....

For nfsd I think it's OK but for nfs clients the context is probably that of the caller ....

Whereas the helper to get a key info maybe does need to be called in the context of the caller .....

Quoted 10 lines

So what information do we not have available for setup?

Are you saying that the problem is that when the user mode helper run thread is invoked we don't have the information available that was present when call_usermodehelper() was called and that's where the challenge lies?

Ian

#11 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

In reply to Ian Kent
Quoted 61 lines · Ian Kent
Quoted 60 lines · Eric W. Biederman
Quoted 57 lines · Ian Kent
Quoted 34 lines · Eric W. Biederman
Quoted 25 lines · Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

Review

Quoted 6 lines
(quoted diff) +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

That would certainly be a better approach, and roughly equivalent to what exists here. That would even ensure we remain in the proper cgroups, and lsm context.

Quoted 6 lines

The practical problem with the approach presented here is that I can hijack any user mode helper I wish, and make it run in any executable I wish as the global root user.

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

LOL, OK, so there's a problem with this.

But, how should a user mode helper execute within a namespace (or more specifically within a container)?

Suppose a user mode helper program scans through the pid list and somehow picks the correct process pid and then does an open()/setns()/execve().

Does that then satisfy the requirements? What needs to be done to safely do that in kernel?

The other approach I've considered is doing a full open()/setns() in kernel (since the caller already knows its pid) but it sounds like that's not right either.

Quoted 6 lines
Quoted 3 lines

The approach we agreed upon with the core dump helper was to provide enough information that userspace could figure out what was the appropriate policy and call nsenter/setns.

Your recommending I have a look at the core dump helper, that's fine, I'll do that.

I am just describing it because it came up. Core dumps are a much easier case than nfs.

Frankly if we can figure out how to run the user mode helpers from the kernel with an appropriate context and not involve userspace I think that will be better for everyone, as it involves fewer moving parts at the end of the day.

Quoted 20 lines
Quoted 20 lines

The only sane approach I can think of in the context of nfs is to fork a kernel thread at mount time that has all of the appropriate context because it was captured from the privileged mounting process, and use that kernel as the equivalent of kthreadd.

There may be some intermediate ground where we capture things or we use the init process of the pid namespace (captured at mount time) as our template/reference process.

If we are going to set this stuff up in the kernel we need a reference process that we can create children of because what is possible with respect to containers keeps changing, and it is extremely error prone to figure out what all othe crazy little bits are, and to update everything every time someone tweaks the kernel's capabilities. We have kthreadd because it was too error prone to scrub a userspace thread of all of the userspace bits and make it the equivalent of what kthreadd is today.

Of course it is also rather nice to have something to hang everything else on.

Quoted 8 lines
Quoted 2 lines

In summary we need a reference struct task that is all setup properly so that we can create an appropriate kernel thread.

I'm having trouble understanding what your getting at here but I'm not that sharp so bear with me.

When call_usermodehelper() is called it's called from a process that is within the context within which the execution is required.

No it is not. That is precisely why we have call_usermodehelper instead of just forking and exec'ing something. The context which triggers the call can be completely different from where you want to run.

Quoted 6 lines

So what information do we not have available for setup?

Are you saying that the problem is that when the user mode helper run thread is invoked we don't have the information available that was present when call_usermodehelper() was called and that's where the challenge lies?

That is part of it.

However in the context of nfs the correct context is determined at mount time, and so when call_usermodehelper() is invoked the only link to that correct context that we have is the nfs super block.

In a pathological case a userspace application can create a new user namespace, and a new mount namespace and completely rearrange the mounts, and deliberately trigger an nfs user mode helper. If we were to use that applications context it could control which userspace application the kernel invoked.

So deeply and fundamentally for the case of nfs you need to capture the context at nfs mount time. The easiest way would be to fork a kernel thread when nfs is mounted, that captures the entire context.

We might be able to do something a little more sophisticated and a little less resource intensive, like pick on the init process that exists when nfs is mounted.

Those are the general parameters.

Eric

#12 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

n Tue, 25 Nov 2014, Eric W. Biederman wrote:
Hi,

Quoted 124 lines
Quoted 61 lines · Ian Kent
Quoted 60 lines · Eric W. Biederman
Quoted 57 lines · Ian Kent
Quoted 34 lines · Eric W. Biederman
Quoted 25 lines · Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

Review

Quoted 6 lines
(quoted diff) +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

That would certainly be a better approach, and roughly equivalent to what exists here. That would even ensure we remain in the proper cgroups, and lsm context.

Quoted 6 lines

The practical problem with the approach presented here is that I can hijack any user mode helper I wish, and make it run in any executable I wish as the global root user.

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

LOL, OK, so there's a problem with this.

But, how should a user mode helper execute within a namespace (or more specifically within a container)?

Suppose a user mode helper program scans through the pid list and somehow picks the correct process pid and then does an open()/setns()/execve().

Does that then satisfy the requirements? What needs to be done to safely do that in kernel?

The other approach I've considered is doing a full open()/setns() in kernel (since the caller already knows its pid) but it sounds like that's not right either.

Quoted 6 lines
Quoted 3 lines

The approach we agreed upon with the core dump helper was to provide enough information that userspace could figure out what was the appropriate policy and call nsenter/setns.

Your recommending I have a look at the core dump helper, that's fine, I'll do that.

I am just describing it because it came up. Core dumps are a much easier case than nfs.

Frankly if we can figure out how to run the user mode helpers from the kernel with an appropriate context and not involve userspace I think that will be better for everyone, as it involves fewer moving parts at the end of the day.

Quoted 20 lines
Quoted 20 lines

The only sane approach I can think of in the context of nfs is to fork a kernel thread at mount time that has all of the appropriate context because it was captured from the privileged mounting process, and use that kernel as the equivalent of kthreadd.

There may be some intermediate ground where we capture things or we use the init process of the pid namespace (captured at mount time) as our template/reference process.

If we are going to set this stuff up in the kernel we need a reference process that we can create children of because what is possible with respect to containers keeps changing, and it is extremely error prone to figure out what all othe crazy little bits are, and to update everything every time someone tweaks the kernel's capabilities. We have kthreadd because it was too error prone to scrub a userspace thread of all of the userspace bits and make it the equivalent of what kthreadd is today.

Of course it is also rather nice to have something to hang everything else on.

Quoted 8 lines
Quoted 2 lines

In summary we need a reference struct task that is all setup properly so that we can create an appropriate kernel thread.

I'm having trouble understanding what your getting at here but I'm not that sharp so bear with me.

When call_usermodehelper() is called it's called from a process that is within the context within which the execution is required.

No it is not. That is precisely why we have call_usermodehelper instead of just forking and exec'ing something. The context which triggers the call can be completely different from where you want to run.

Quoted 6 lines

So what information do we not have available for setup?

Are you saying that the problem is that when the user mode helper run thread is invoked we don't have the information available that was present when call_usermodehelper() was called and that's where the challenge lies?

That is part of it.

However in the context of nfs the correct context is determined at mount time, and so when call_usermodehelper() is invoked the only link to that correct context that we have is the nfs super block.

Quoted 5 lines

In a pathological case a userspace application can create a new user namespace, and a new mount namespace and completely rearrange the mounts, and deliberately trigger an nfs user mode helper. If we were to use that applications context it could control which userspace application the kernel invoked.

Then it seems it would never be safe to try to execve /sbin/request-key within the calling process' mnt_ns, so if we want to do some work within that namespace the transition to it needs to happen after the upcall, after /sbin/request-key calls the appropriate helper.

And instead of transitioning into this context before execve, instead pass it along to allow the userspace helper to do setns()..

Quoted 7 lines

So deeply and fundamentally for the case of nfs you need to capture the context at nfs mount time. The easiest way would be to fork a kernel thread when nfs is mounted, that captures the entire context.

We might be able to do something a little more sophisticated and a little less resource intensive, like pick on the init process that exists when nfs is mounted.

The only namespace that needs to be preserved from mount time would be the mount namespace which can be pulled from the mount itself and passed along in the key request.

Or if we tracked the mount namespace when creating a user namespace, we could climb out of the mount namespaces created within user namespaces by following them out. Does that sound sane, or completely nuts?

Quoted 1 line

Those are the general parameters.

It does seem very expensive to keep a thread around for every mount; I'm still trying to find a way around it..

Ben

Quoted 3 lines

Eric

author
#13 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 133 lines · Benjamin Coddington

n Tue, 25 Nov 2014, Eric W. Biederman wrote:
Hi,

Quoted 124 lines
Quoted 61 lines · Ian Kent
Quoted 60 lines · Eric W. Biederman
Quoted 57 lines · Ian Kent
Quoted 34 lines · Eric W. Biederman
Quoted 25 lines · Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

Review

Quoted 6 lines
(quoted diff) +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

That would certainly be a better approach, and roughly equivalent to what exists here. That would even ensure we remain in the proper cgroups, and lsm context.

Quoted 6 lines

The practical problem with the approach presented here is that I can hijack any user mode helper I wish, and make it run in any executable I wish as the global root user.

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

LOL, OK, so there's a problem with this.

But, how should a user mode helper execute within a namespace (or more specifically within a container)?

Suppose a user mode helper program scans through the pid list and somehow picks the correct process pid and then does an open()/setns()/execve().

Does that then satisfy the requirements? What needs to be done to safely do that in kernel?

The other approach I've considered is doing a full open()/setns() in kernel (since the caller already knows its pid) but it sounds like that's not right either.

Quoted 6 lines
Quoted 3 lines

The approach we agreed upon with the core dump helper was to provide enough information that userspace could figure out what was the appropriate policy and call nsenter/setns.

Your recommending I have a look at the core dump helper, that's fine, I'll do that.

I am just describing it because it came up. Core dumps are a much easier case than nfs.

Frankly if we can figure out how to run the user mode helpers from the kernel with an appropriate context and not involve userspace I think that will be better for everyone, as it involves fewer moving parts at the end of the day.

Quoted 20 lines
Quoted 20 lines

The only sane approach I can think of in the context of nfs is to fork a kernel thread at mount time that has all of the appropriate context because it was captured from the privileged mounting process, and use that kernel as the equivalent of kthreadd.

There may be some intermediate ground where we capture things or we use the init process of the pid namespace (captured at mount time) as our template/reference process.

If we are going to set this stuff up in the kernel we need a reference process that we can create children of because what is possible with respect to containers keeps changing, and it is extremely error prone to figure out what all othe crazy little bits are, and to update everything every time someone tweaks the kernel's capabilities. We have kthreadd because it was too error prone to scrub a userspace thread of all of the userspace bits and make it the equivalent of what kthreadd is today.

Of course it is also rather nice to have something to hang everything else on.

Quoted 8 lines
Quoted 2 lines

In summary we need a reference struct task that is all setup properly so that we can create an appropriate kernel thread.

I'm having trouble understanding what your getting at here but I'm not that sharp so bear with me.

When call_usermodehelper() is called it's called from a process that is within the context within which the execution is required.

No it is not. That is precisely why we have call_usermodehelper instead of just forking and exec'ing something. The context which triggers the call can be completely different from where you want to run.

Quoted 6 lines

So what information do we not have available for setup?

Are you saying that the problem is that when the user mode helper run thread is invoked we don't have the information available that was present when call_usermodehelper() was called and that's where the challenge lies?

That is part of it.

However in the context of nfs the correct context is determined at mount time, and so when call_usermodehelper() is invoked the only link to that correct context that we have is the nfs super block.

Quoted 5 lines

In a pathological case a userspace application can create a new user namespace, and a new mount namespace and completely rearrange the mounts, and deliberately trigger an nfs user mode helper. If we were to use that applications context it could control which userspace application the kernel invoked.

Quoted 7 lines

Then it seems it would never be safe to try to execve /sbin/request-key within the calling process' mnt_ns, so if we want to do some work within that namespace the transition to it needs to happen after the upcall, after /sbin/request-key calls the appropriate helper.

And instead of transitioning into this context before execve, instead pass it along to allow the userspace helper to do setns()..

I still don't see the difference between, given an appropriate pid, using the open()/setns() mechanism in kernel similar to nsenter(1).

In that case the setup is done before the first exec rather than between the first and second exec (the one in nsenter). Our other approach essentially did that but (incorrectly) used the current process pid and didn't deal with all the namespace types as nsenter does.

Isn't the real problem getting hold of an appropriate pid to use for the open/setns that belongs to a process that's still running? What else is needed, Eric?

Not having looked at how core dumping works before, I'm having difficulty working out what gets executed in user space so I can't be sure that what I'm saying is accurate, can someone spell this out for me please?

Quoted 6 lines
Quoted 6 lines

So deeply and fundamentally for the case of nfs you need to capture the context at nfs mount time. The easiest way would be to fork a kernel thread when nfs is mounted, that captures the entire context.

Quoted 7 lines
Quoted 3 lines

We might be able to do something a little more sophisticated and a little less resource intensive, like pick on the init process that exists when nfs is mounted.

The only namespace that needs to be preserved from mount time would be the mount namespace which can be pulled from the mount itself and passed along in the key request.

Sounds simple but isn't it the case we don't know and shouldn't have to care about what system calls the helper uses since the environment should "see" what it's supposed to?

Quoted 10 lines

Or if we tracked the mount namespace when creating a user namespace, we could climb out of the mount namespaces created within user namespaces by following them out. Does that sound sane, or completely nuts?

Quoted 1 line

Those are the general parameters.

It does seem very expensive to keep a thread around for every mount; I'm still trying to find a way around it..

Yeah, that's not such a good idea.

Several hundred mounts is going to create a big mess for system administrators, not to mention the overhead. It's right up there with symlinking /etc/mtab to /proc/self/mounts at sites with large direct mount maps.

Ian

#14 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 134 lines · Ian Kent
Quoted 133 lines · Benjamin Coddington

n Tue, 25 Nov 2014, Eric W. Biederman wrote:
Hi,

Quoted 129 lines
Quoted 61 lines · Ian Kent
Quoted 60 lines · Eric W. Biederman
Quoted 57 lines · Ian Kent
Quoted 34 lines · Eric W. Biederman
Quoted 25 lines · Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

Review

Quoted 6 lines
(quoted diff) +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

That would certainly be a better approach, and roughly equivalent to what exists here. That would even ensure we remain in the proper cgroups, and lsm context.

Quoted 6 lines

The practical problem with the approach presented here is that I can hijack any user mode helper I wish, and make it run in any executable I wish as the global root user.

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

LOL, OK, so there's a problem with this.

But, how should a user mode helper execute within a namespace (or more specifically within a container)?

Suppose a user mode helper program scans through the pid list and somehow picks the correct process pid and then does an open()/setns()/execve().

Does that then satisfy the requirements? What needs to be done to safely do that in kernel?

The other approach I've considered is doing a full open()/setns() in kernel (since the caller already knows its pid) but it sounds like that's not right either.

Quoted 6 lines
Quoted 3 lines

The approach we agreed upon with the core dump helper was to provide enough information that userspace could figure out what was the appropriate policy and call nsenter/setns.

Your recommending I have a look at the core dump helper, that's fine, I'll do that.

I am just describing it because it came up. Core dumps are a much easier case than nfs.

Frankly if we can figure out how to run the user mode helpers from the kernel with an appropriate context and not involve userspace I think that will be better for everyone, as it involves fewer moving parts at the end of the day.

Quoted 20 lines
Quoted 20 lines

The only sane approach I can think of in the context of nfs is to fork a kernel thread at mount time that has all of the appropriate context because it was captured from the privileged mounting process, and use that kernel as the equivalent of kthreadd.

There may be some intermediate ground where we capture things or we use the init process of the pid namespace (captured at mount time) as our template/reference process.

If we are going to set this stuff up in the kernel we need a reference process that we can create children of because what is possible with respect to containers keeps changing, and it is extremely error prone to figure out what all othe crazy little bits are, and to update everything every time someone tweaks the kernel's capabilities. We have kthreadd because it was too error prone to scrub a userspace thread of all of the userspace bits and make it the equivalent of what kthreadd is today.

Of course it is also rather nice to have something to hang everything else on.

Quoted 8 lines
Quoted 2 lines

In summary we need a reference struct task that is all setup properly so that we can create an appropriate kernel thread.

I'm having trouble understanding what your getting at here but I'm not that sharp so bear with me.

When call_usermodehelper() is called it's called from a process that is within the context within which the execution is required.

No it is not. That is precisely why we have call_usermodehelper instead of just forking and exec'ing something. The context which triggers the call can be completely different from where you want to run.

Quoted 6 lines

So what information do we not have available for setup?

Are you saying that the problem is that when the user mode helper run thread is invoked we don't have the information available that was present when call_usermodehelper() was called and that's where the challenge lies?

That is part of it.

However in the context of nfs the correct context is determined at mount time, and so when call_usermodehelper() is invoked the only link to that correct context that we have is the nfs super block.

In a pathological case a userspace application can create a new user namespace, and a new mount namespace and completely rearrange the mounts, and deliberately trigger an nfs user mode helper. If we were to use that applications context it could control which userspace application the kernel invoked.

Quoted 4 lines
Quoted 4 lines

Then it seems it would never be safe to try to execve /sbin/request-key within the calling process' mnt_ns, so if we want to do some work within that namespace the transition to it needs to happen after the upcall, after /sbin/request-key calls the appropriate helper.

No. We never want to do work in the calling process's mnt_ns. We want to work in the mounters mnt_ns which given mount propagation can be something completely and totally different.

Quoted 6 lines
Quoted 2 lines

And instead of transitioning into this context before execve, instead pass it along to allow the userspace helper to do setns()..

I still don't see the difference between, given an appropriate pid, using the open()/setns() mechanism in kernel similar to nsenter(1).

Quoted 8 lines

In that case the setup is done before the first exec rather than between the first and second exec (the one in nsenter). Our other approach essentially did that but (incorrectly) used the current process pid and didn't deal with all the namespace types as nsenter does.

Isn't the real problem getting hold of an appropriate pid to use for the open/setns that belongs to a process that's still running? What else is needed, Eric?

Essentially that is the real problem. Getting ahold of the context of the container in which a filesystem was mounted.

Now it isn't just namespaces but also cgroups and uids and gids and capabilities and process groups and sessions. There are a whole host of little things that need to be right to run a user mode helper, and in particular to run a user mode helper in a ``container''.

Quoted 4 lines

Not having looked at how core dumping works before, I'm having difficulty working out what gets executed in user space so I can't be sure that what I'm saying is accurate, can someone spell this out for me please?

In that case what happens is that the normal user mode helper process runs started by kthreadd and if %P is specified that processes knows the pid in the initial pid namespace. Which is good enough to use nsenter and otherwise figure out the appropriate container from the process that is dumping core.

It is easier with core dumping as we don't have the disconnect in contexts that we do with filesystems.

Quoted 11 lines
Quoted 11 lines
Quoted 7 lines

So deeply and fundamentally for the case of nfs you need to capture the context at nfs mount time. The easiest way would be to fork a kernel thread when nfs is mounted, that captures the entire context.

We might be able to do something a little more sophisticated and a little less resource intensive, like pick on the init process that exists when nfs is mounted.

The only namespace that needs to be preserved from mount time would be the mount namespace which can be pulled from the mount itself and passed along in the key request.

We absolutely can not pull the mount from the mount namespace. Mount propagtion makes that impossible.

Quoted 3 lines

Sounds simple but isn't it the case we don't know and shouldn't have to care about what system calls the helper uses since the environment should "see" what it's supposed to?

The tricky bit is setting the environment correctly.

Quoted 4 lines
Quoted 4 lines

Or if we tracked the mount namespace when creating a user namespace, we could climb out of the mount namespaces created within user namespaces by following them out. Does that sound sane, or completely nuts?

For what we have today (where the global root user has to mount the nfs filesystem) tracking the mount namespace at the time nfs is mounted is close to enough. However if we are going to work this out we need to solve this for an nfs where we are brave enough to set
".fs_flags = FS_USERNS_MOUNT".

The NFS code base is nearly at the point where we can reasonably talk about supporting unprivileged mounts.

Quoted 11 lines
Quoted 4 lines
Quoted 1 line

Those are the general parameters.

It does seem very expensive to keep a thread around for every mount; I'm still trying to find a way around it..

Yeah, that's not such a good idea.

Several hundred mounts is going to create a big mess for system administrators, not to mention the overhead. It's right up there with symlinking /etc/mtab to /proc/self/mounts at sites with large direct mount maps.

A thread will cost you maybe 40k. In the grand scheme of things that really isn't a lot. I agree that it would be nice to do better than
one thread per mount. But I start with a thread as a reference point
as that is the trivial way to get things correct.

To relax it from a thread per mount we need definitions that we can explain to folks in userspace about when and where user mode helpers will run.

I think something like Oleg's proposal to use the init process of a pid namespace as our reference is worth exploring. We certainly don't match the mounters exact environment when running user mode helpers today, so there is leeway for some relaxation without breaking userspace.

There are funny issues without forking a thread to run the user mode helpers, such as what happens if all of the processes in the container exit what should happen? Remember that mount propagation allows the filesystem to propagate into different mount namespaces and bind mounts allow the filesystem to show up in different places in the those mount namespaces.

If the container has exited should the filesystem remain functional? Should the kernel keep pieces of the container alive to run user mode helpers?

Eric

#15 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 234 lines · Eric W. Biederman
Quoted 134 lines · Ian Kent
Quoted 133 lines · Benjamin Coddington

n Tue, 25 Nov 2014, Eric W. Biederman wrote:
Hi,

Quoted 129 lines
Quoted 61 lines · Ian Kent
Quoted 60 lines · Eric W. Biederman
Quoted 57 lines · Ian Kent
Quoted 34 lines · Eric W. Biederman
Quoted 25 lines · Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

Review

Quoted 6 lines
(quoted diff) +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

That would certainly be a better approach, and roughly equivalent to what exists here. That would even ensure we remain in the proper cgroups, and lsm context.

Quoted 6 lines

The practical problem with the approach presented here is that I can hijack any user mode helper I wish, and make it run in any executable I wish as the global root user.

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

LOL, OK, so there's a problem with this.

But, how should a user mode helper execute within a namespace (or more specifically within a container)?

Suppose a user mode helper program scans through the pid list and somehow picks the correct process pid and then does an open()/setns()/execve().

Does that then satisfy the requirements? What needs to be done to safely do that in kernel?

The other approach I've considered is doing a full open()/setns() in kernel (since the caller already knows its pid) but it sounds like that's not right either.

Quoted 6 lines
Quoted 3 lines

The approach we agreed upon with the core dump helper was to provide enough information that userspace could figure out what was the appropriate policy and call nsenter/setns.

Your recommending I have a look at the core dump helper, that's fine, I'll do that.

I am just describing it because it came up. Core dumps are a much easier case than nfs.

Frankly if we can figure out how to run the user mode helpers from the kernel with an appropriate context and not involve userspace I think that will be better for everyone, as it involves fewer moving parts at the end of the day.

Quoted 20 lines
Quoted 20 lines

The only sane approach I can think of in the context of nfs is to fork a kernel thread at mount time that has all of the appropriate context because it was captured from the privileged mounting process, and use that kernel as the equivalent of kthreadd.

There may be some intermediate ground where we capture things or we use the init process of the pid namespace (captured at mount time) as our template/reference process.

If we are going to set this stuff up in the kernel we need a reference process that we can create children of because what is possible with respect to containers keeps changing, and it is extremely error prone to figure out what all othe crazy little bits are, and to update everything every time someone tweaks the kernel's capabilities. We have kthreadd because it was too error prone to scrub a userspace thread of all of the userspace bits and make it the equivalent of what kthreadd is today.

Of course it is also rather nice to have something to hang everything else on.

Quoted 8 lines
Quoted 2 lines

In summary we need a reference struct task that is all setup properly so that we can create an appropriate kernel thread.

I'm having trouble understanding what your getting at here but I'm not that sharp so bear with me.

When call_usermodehelper() is called it's called from a process that is within the context within which the execution is required.

No it is not. That is precisely why we have call_usermodehelper instead of just forking and exec'ing something. The context which triggers the call can be completely different from where you want to run.

Quoted 6 lines

So what information do we not have available for setup?

Are you saying that the problem is that when the user mode helper run thread is invoked we don't have the information available that was present when call_usermodehelper() was called and that's where the challenge lies?

That is part of it.

However in the context of nfs the correct context is determined at mount time, and so when call_usermodehelper() is invoked the only link to that correct context that we have is the nfs super block.

In a pathological case a userspace application can create a new user namespace, and a new mount namespace and completely rearrange the mounts, and deliberately trigger an nfs user mode helper. If we were to use that applications context it could control which userspace application the kernel invoked.

Quoted 4 lines
Quoted 4 lines

Then it seems it would never be safe to try to execve /sbin/request-key within the calling process' mnt_ns, so if we want to do some work within that namespace the transition to it needs to happen after the upcall, after /sbin/request-key calls the appropriate helper.

No. We never want to do work in the calling process's mnt_ns. We want to work in the mounters mnt_ns which given mount propagation can be something completely and totally different.

Quoted 6 lines
Quoted 2 lines

And instead of transitioning into this context before execve, instead pass it along to allow the userspace helper to do setns()..

I still don't see the difference between, given an appropriate pid, using the open()/setns() mechanism in kernel similar to nsenter(1).

Quoted 8 lines

In that case the setup is done before the first exec rather than between the first and second exec (the one in nsenter). Our other approach essentially did that but (incorrectly) used the current process pid and didn't deal with all the namespace types as nsenter does.

Isn't the real problem getting hold of an appropriate pid to use for the open/setns that belongs to a process that's still running? What else is needed, Eric?

Essentially that is the real problem. Getting ahold of the context of the container in which a filesystem was mounted.

Now it isn't just namespaces but also cgroups and uids and gids and capabilities and process groups and sessions. There are a whole host of little things that need to be right to run a user mode helper, and in particular to run a user mode helper in a ``container''.

Quoted 4 lines

Not having looked at how core dumping works before, I'm having difficulty working out what gets executed in user space so I can't be sure that what I'm saying is accurate, can someone spell this out for me please?

In that case what happens is that the normal user mode helper process runs started by kthreadd and if %P is specified that processes knows the pid in the initial pid namespace. Which is good enough to use nsenter and otherwise figure out the appropriate container from the process that is dumping core.

It is easier with core dumping as we don't have the disconnect in contexts that we do with filesystems.

Quoted 11 lines
Quoted 11 lines
Quoted 7 lines

So deeply and fundamentally for the case of nfs you need to capture the context at nfs mount time. The easiest way would be to fork a kernel thread when nfs is mounted, that captures the entire context.

We might be able to do something a little more sophisticated and a little less resource intensive, like pick on the init process that exists when nfs is mounted.

The only namespace that needs to be preserved from mount time would be the mount namespace which can be pulled from the mount itself and passed along in the key request.

We absolutely can not pull the mount from the mount namespace. Mount propagtion makes that impossible.

Quoted 3 lines

Sounds simple but isn't it the case we don't know and shouldn't have to care about what system calls the helper uses since the environment should "see" what it's supposed to?

The tricky bit is setting the environment correctly.

Quoted 4 lines
Quoted 4 lines

Or if we tracked the mount namespace when creating a user namespace, we could climb out of the mount namespaces created within user namespaces by following them out. Does that sound sane, or completely nuts?

For what we have today (where the global root user has to mount the nfs filesystem) tracking the mount namespace at the time nfs is mounted is close to enough. However if we are going to work this out we need to solve this for an nfs where we are brave enough to set
".fs_flags = FS_USERNS_MOUNT".

The NFS code base is nearly at the point where we can reasonably talk about supporting unprivileged mounts.

Quoted 11 lines
Quoted 4 lines
Quoted 1 line

Those are the general parameters.

It does seem very expensive to keep a thread around for every mount; I'm still trying to find a way around it..

Yeah, that's not such a good idea.

Several hundred mounts is going to create a big mess for system administrators, not to mention the overhead. It's right up there with symlinking /etc/mtab to /proc/self/mounts at sites with large direct mount maps.

A thread will cost you maybe 40k. In the grand scheme of things that really isn't a lot. I agree that it would be nice to do better than
one thread per mount. But I start with a thread as a reference point
as that is the trivial way to get things correct.

Quoted 8 lines

To relax it from a thread per mount we need definitions that we can explain to folks in userspace about when and where user mode helpers will run.

I think something like Oleg's proposal to use the init process of a pid namespace as our reference is worth exploring. We certainly don't match the mounters exact environment when running user mode helpers today, so there is leeway for some relaxation without breaking userspace.

That might look like a "khelper" workqueue for each pid namespace, and the user of call_usermodehelper() would specify that the current pid namespace helper should or should not be used.

Oleg also mentioned only using (or creating) the namespace helper if requested via prctl(). I can imagine that some users of namespaces would want to keep calls to userspace helpers within the global root space.

If the helper would only be created by request, that takes away the overhead of thread-per-mount or thread-per-pid-namespace a bit.

As you mentioned earlier, it is a good idea to think about providing userspace with a clear understanding of how and where user mode helpers run. This approach allows userspace to think about them as being tied to the first process in each container.

Ben

Quoted 14 lines

There are funny issues without forking a thread to run the user mode helpers, such as what happens if all of the processes in the container exit what should happen? Remember that mount propagation allows the filesystem to propagate into different mount namespaces and bind mounts allow the filesystem to show up in different places in the those mount namespaces.

If the container has exited should the filesystem remain functional? Should the kernel keep pieces of the container alive to run user mode helpers?

Eric

author
#16 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

In reply to Eric W. Biederman
Quoted 13 lines · Eric W. Biederman
Quoted 6 lines
Quoted 5 lines
Quoted 2 lines

Those are the general parameters.

It does seem very expensive to keep a thread around for every mount; I'm still trying to find a way around it..

Quoted 6 lines

Yeah, that's not such a good idea.

Several hundred mounts is going to create a big mess for system administrators, not to mention the overhead. It's right up there with symlinking /etc/mtab to /proc/self/mounts at sites with large direct mount maps.

Quoted 4 lines

A thread will cost you maybe 40k. In the grand scheme of things that really isn't a lot. I agree that it would be nice to do better than
one thread per mount. But I start with a thread as a reference point
as that is the trivial way to get things correct.

Sure it has merit because it's straight forward but my criticism isn't about overhead. It's about system administrators annoyance and frustration level when they're trying get things done.

For example, with the symlinking of the mount table and even just a few hundred direct automounts, listing the mount table fills the screen with tombs of stuff that really should be hidden (and only listed if requested). That just gets in the way when you're trying desperately to resolve some urgent problem.

There is a resource issue as well since so many site administration applications will read the table and, as the number of entries gets larger, so does the time these things take to run. Not having to deal with this on a day to day basis tends to make us forget about these little side issues but I think they are important.

Point is, for process listings the issue is almost exactly the same.

Ian

author
#17 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

In reply to Eric W. Biederman
Quoted 160 lines · Eric W. Biederman
Quoted 134 lines · Ian Kent
Quoted 133 lines · Benjamin Coddington

n Tue, 25 Nov 2014, Eric W. Biederman wrote:
Hi,

Quoted 129 lines
Quoted 61 lines · Ian Kent
Quoted 60 lines · Eric W. Biederman
Quoted 57 lines · Ian Kent
Quoted 34 lines · Eric W. Biederman
Quoted 25 lines · Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

Review

Quoted 6 lines
(quoted diff) +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

That would certainly be a better approach, and roughly equivalent to what exists here. That would even ensure we remain in the proper cgroups, and lsm context.

Quoted 6 lines

The practical problem with the approach presented here is that I can hijack any user mode helper I wish, and make it run in any executable I wish as the global root user.

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

LOL, OK, so there's a problem with this.

But, how should a user mode helper execute within a namespace (or more specifically within a container)?

Suppose a user mode helper program scans through the pid list and somehow picks the correct process pid and then does an open()/setns()/execve().

Does that then satisfy the requirements? What needs to be done to safely do that in kernel?

The other approach I've considered is doing a full open()/setns() in kernel (since the caller already knows its pid) but it sounds like that's not right either.

Quoted 6 lines
Quoted 3 lines

The approach we agreed upon with the core dump helper was to provide enough information that userspace could figure out what was the appropriate policy and call nsenter/setns.

Your recommending I have a look at the core dump helper, that's fine, I'll do that.

I am just describing it because it came up. Core dumps are a much easier case than nfs.

Frankly if we can figure out how to run the user mode helpers from the kernel with an appropriate context and not involve userspace I think that will be better for everyone, as it involves fewer moving parts at the end of the day.

Quoted 20 lines
Quoted 20 lines

The only sane approach I can think of in the context of nfs is to fork a kernel thread at mount time that has all of the appropriate context because it was captured from the privileged mounting process, and use that kernel as the equivalent of kthreadd.

There may be some intermediate ground where we capture things or we use the init process of the pid namespace (captured at mount time) as our template/reference process.

If we are going to set this stuff up in the kernel we need a reference process that we can create children of because what is possible with respect to containers keeps changing, and it is extremely error prone to figure out what all othe crazy little bits are, and to update everything every time someone tweaks the kernel's capabilities. We have kthreadd because it was too error prone to scrub a userspace thread of all of the userspace bits and make it the equivalent of what kthreadd is today.

Of course it is also rather nice to have something to hang everything else on.

Quoted 8 lines
Quoted 2 lines

In summary we need a reference struct task that is all setup properly so that we can create an appropriate kernel thread.

I'm having trouble understanding what your getting at here but I'm not that sharp so bear with me.

When call_usermodehelper() is called it's called from a process that is within the context within which the execution is required.

No it is not. That is precisely why we have call_usermodehelper instead of just forking and exec'ing something. The context which triggers the call can be completely different from where you want to run.

Quoted 6 lines

So what information do we not have available for setup?

Are you saying that the problem is that when the user mode helper run thread is invoked we don't have the information available that was present when call_usermodehelper() was called and that's where the challenge lies?

That is part of it.

However in the context of nfs the correct context is determined at mount time, and so when call_usermodehelper() is invoked the only link to that correct context that we have is the nfs super block.

In a pathological case a userspace application can create a new user namespace, and a new mount namespace and completely rearrange the mounts, and deliberately trigger an nfs user mode helper. If we were to use that applications context it could control which userspace application the kernel invoked.

Quoted 4 lines
Quoted 4 lines

Then it seems it would never be safe to try to execve /sbin/request-key within the calling process' mnt_ns, so if we want to do some work within that namespace the transition to it needs to happen after the upcall, after /sbin/request-key calls the appropriate helper.

No. We never want to do work in the calling process's mnt_ns. We want to work in the mounters mnt_ns which given mount propagation can be something completely and totally different.

Quoted 6 lines
Quoted 2 lines

And instead of transitioning into this context before execve, instead pass it along to allow the userspace helper to do setns()..

I still don't see the difference between, given an appropriate pid, using the open()/setns() mechanism in kernel similar to nsenter(1).

Quoted 8 lines

In that case the setup is done before the first exec rather than between the first and second exec (the one in nsenter). Our other approach essentially did that but (incorrectly) used the current process pid and didn't deal with all the namespace types as nsenter does.

Isn't the real problem getting hold of an appropriate pid to use for the open/setns that belongs to a process that's still running? What else is needed, Eric?

Quoted 7 lines

Essentially that is the real problem. Getting ahold of the context of the container in which a filesystem was mounted.

Now it isn't just namespaces but also cgroups and uids and gids and capabilities and process groups and sessions. There are a whole host of little things that need to be right to run a user mode helper, and in particular to run a user mode helper in a ``container''.

And I think this is where the confusion starts because ....

As it stands (whether executing within a container or not) the mounting process is mostly long gone when these calls are made and they are executed from the root init context.

So doesn't adding a new init context, for example the init context of a container, then using that for the template amount to the same thing within the container?

If we accept that then the open/setns is "fairly" straight forward.

TBH I'm struggling to see where would be different.

Ian

maintainer
#18 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

In reply to Eric W. Biederman
Quoted 2 lines · Eric W. Biederman

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

Is there any particular reason the upcalled program has to be run as root? Could the kernel not run it as something else - perhaps the caller's UID,GID or even something anonymous?

Also, call_sbin_request_key() could be given a parameter to call something other than /sbin/request-key, and key_type::request_key could be used.

David

#19 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 11 lines · David Howells
Quoted 2 lines · Eric W. Biederman

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

Is there any particular reason the upcalled program has to be run as root? Could the kernel not run it as something else - perhaps the caller's UID,GID or even something anonymous?

Also, call_sbin_request_key() could be given a parameter to call something other than /sbin/request-key, and key_type::request_key could be used.

Fundamentally the upcall needs to happen with enough privileges to do the job, and that means running in practice running as root in the appropriate context. If we didn't need to gain privileges we wouldn't need an upcall.

In the code I was critisizing struct cred was not being changed because of what I believe was an ignorance of what task->nsproxy was about and is for.

It is straight forward to save off a for a kernel thread from the process calling mount and make it responsible for the upcall and use that as the parent for all of the containerized upcalls, and we could easily run with that threads permissions.

We can't use the context of the triggering user but we instead need to use the context of the mounter of the filesystem. As otherwise the triggering user can control what is /sbin/request-key and cause problems that way.

Eric

#20 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 28 lines · Eric W. Biederman
Quoted 11 lines · David Howells
Quoted 2 lines · Eric W. Biederman

Ian if we were to merge this I believe you would win the award for easiest path to a root shell.

Is there any particular reason the upcalled program has to be run as root? Could the kernel not run it as something else - perhaps the caller's UID,GID or even something anonymous?

Also, call_sbin_request_key() could be given a parameter to call something other than /sbin/request-key, and key_type::request_key could be used.

Fundamentally the upcall needs to happen with enough privileges to do the job, and that means running in practice running as root in the appropriate context. If we didn't need to gain privileges we wouldn't need an upcall.

In the code I was critisizing struct cred was not being changed because of what I believe was an ignorance of what task->nsproxy was about and is for.

It is straight forward to save off a for a kernel thread from the process calling mount and make it responsible for the upcall and use that as the parent for all of the containerized upcalls, and we could easily run with that threads permissions.

Quoted 4 lines

We can't use the context of the triggering user but we instead need to use the context of the mounter of the filesystem. As otherwise the triggering user can control what is /sbin/request-key and cause problems that way.

That makes sense to me. There are three cases I think we care about:

- client idmapper upcall: translates between on-the-wire "user@domain" names and local uid's/gid's. The results may be cached and used by other users, and you certainly don't want the user who happened to trigger the upcall by doing a "ls" to poison the cache with weird values.

- server-side state recovery call: used to record some information about which clients hold active state. There's no "mount" on the server side, but "root in the appropriate context" still sounds right--we probably want to save the context of whoever started the server. One extra thread per server would certainly be no big deal, so if that's how we need to save the context, fine.

- client request-key call: this isn't actually implemented yet and I don't feel like I understand it.

--b.

author
#21 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

In reply to Oleg Nesterov
Quoted 25 lines · Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Yes.

And I have another concern... this is mostly a feeling, I can be easily wrong but:

Quoted 12 lines · Ian Kent

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 651

Quoted 6 lines
kernel/kmod.c +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

Are you saying that the user space program should be modified to do this?

Ian

author
#22 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

In reply to Oleg Nesterov
Quoted 4 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Oleg, thanks for your comments.

Quoted 9 lines · Ian Kent
Quoted 4 lines

The call_usermodehelper() function executes all binaries in the global "init" root context. This doesn't allow a binary to be run within the callers namespace (aka. a container).

Please see below.

Quoted 8 lines
Quoted 5 lines

Both containerized NFS client and NFS server need the ability to execute a binary within their container. To do this create a new nsproxy within the callers' context so it can be used for setup prior to calling do_execve() from the user mode helper thread runner.

and probably we also need this for coredump helpers, we want them to be per-namespace.

To save me some time could you point me to some of the related code please. I don't normally play in that area.

Quoted 13 lines

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 651

Quoted 6 lines
kernel/kmod.c +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 3 lines

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 652

Quoted 1 line
kernel/kmod.c +1 -0
+	switch_task_namespaces(current, ns);

This doesn't look sane because this won't switch task_active_pid_ns().

I wondered about that too but I didn't design the open()/setns() interface and TBH I've been wondering how he hell it is supposed to work because of exactly this.

The statement amounts to saying that the
fd = open(/proc/<pid in target namespace>/ns/mnt); setns(fd);
won't set the namespace properly but the documentation I've seen so far (there's probably more that I need to see, I'll look further) implies this is sufficient.

How does one correctly set the namespace in user space since each of the /proc/<pid>/ns/<namespace> will use a slightly different proc_ns_operations install function?

Are we saying that, for example, if open(/proc/<pid>/ns/pid)/setns() is used then the process must not do path lookups if it expects them to be within the namespace and restrict itself to pid related system calls only and so on for each of the other namespaces?

Or is it assumed that userspace will do open(/proc/<pid>/ns/<namespace>)/setns()/close() every time it makes systems calls that rely on a specific type of namespace?

Quoted 7 lines

And this reminds me another discussion, please look at
http://marc.info/?l=linux-kernel&m=138479570926192

Once again, this is just an idea to provoke more discussion. I am starting to think that perhaps we need pid_ns->umh_helper (init by default). And PR_SET_NS_UMH_HELPER.

Yeah, I'll need to digest that for a while.

Ian

#23 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 50 lines · Ian Kent
Quoted 4 lines · Oleg Nesterov

Let me first apologize, I didn't actually read this series yet.

But I have to admit that so far I do not like this approach... probably I am biased.

Oleg, thanks for your comments.

Quoted 9 lines · Ian Kent
Quoted 4 lines

The call_usermodehelper() function executes all binaries in the global "init" root context. This doesn't allow a binary to be run within the callers namespace (aka. a container).

Please see below.

Quoted 8 lines
Quoted 5 lines

Both containerized NFS client and NFS server need the ability to execute a binary within their container. To do this create a new nsproxy within the callers' context so it can be used for setup prior to calling do_execve() from the user mode helper thread runner.

and probably we also need this for coredump helpers, we want them to be per-namespace.

To save me some time could you point me to some of the related code please. I don't normally play in that area.

Quoted 13 lines

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 651

Quoted 6 lines
kernel/kmod.c +5 -0
+static int umh_set_ns(struct subprocess_info *info, struct cred *new)
+{
+	struct nsproxy *ns = info->data;
+
+	mntns_setfs(ns->mnt_ns);

Firstly, it is not clear to me if we should use the caller's ->mnt_ns. Let me remind about the coredump. The dumping task can cloned with CLONE_NEWNS or it cam do unshare(NEWNS)... but OK, I do not understand this enough.

Quoted 3 lines

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 652

Quoted 1 line
kernel/kmod.c +1 -0
+	switch_task_namespaces(current, ns);

This doesn't look sane because this won't switch task_active_pid_ns().

I wondered about that too but I didn't design the open()/setns() interface and TBH I've been wondering how he hell it is supposed to work because of exactly this.

Quoted 6 lines

The statement amounts to saying that the
fd = open(/proc/<pid in target namespace>/ns/mnt); setns(fd);
won't set the namespace properly but the documentation I've seen so far (there's probably more that I need to see, I'll look further) implies this is sufficient.

It is but it is a bit peculiar.

Quoted 8 lines

How does one correctly set the namespace in user space since each of the /proc/<pid>/ns/<namespace> will use a slightly different proc_ns_operations install function?

Are we saying that, for example, if open(/proc/<pid>/ns/pid)/setns() is used then the process must not do path lookups if it expects them to be within the namespace and restrict itself to pid related system calls only and so on for each of the other namespaces?

In userspace you can only set the pid namespace for new children. You can never change your own pid namespace. Because actually changing a processes pid is too nasty to contemplate, or implement and because in a login daemon context having your first child be the initial process of the pid namespace is actually what is desirable.

Quoted 3 lines

Or is it assumed that userspace will do open(/proc/<pid>/ns/<namespace>)/setns()/close() every time it makes systems calls that rely on a specific type of namespace?

setns is designed to be the exception, rather thant something you need to do every time.

But nsproxy is not the one true source of namespaces, nsproxy is simply a convinient place so we don't bloat struct task. The primary reference for the pid namespace is in a struct pid, what is in nsproxy is just which pid namespace children will be created in. The user namespace reference comes from struct cred.

Quoted 8 lines
Quoted 6 lines

And this reminds me another discussion, please look at
http://marc.info/?l=linux-kernel&m=138479570926192

Once again, this is just an idea to provoke more discussion. I am starting to think that perhaps we need pid_ns->umh_helper (init by default). And PR_SET_NS_UMH_HELPER.

Yeah, I'll need to digest that for a while.

Eric

author
#24 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

Quoted 10 lines · Eric W. Biederman
Quoted 9 lines

How does one correctly set the namespace in user space since each of the /proc/<pid>/ns/<namespace> will use a slightly different proc_ns_operations install function?

Are we saying that, for example, if open(/proc/<pid>/ns/pid)/setns() is used then the process must not do path lookups if it expects them to be within the namespace and restrict itself to pid related system calls only and so on for each of the other namespaces?

Quoted 6 lines

In userspace you can only set the pid namespace for new children. You can never change your own pid namespace. Because actually changing a processes pid is too nasty to contemplate, or implement and because in a login daemon context having your first child be the initial process of the pid namespace is actually what is desirable.

Maybe using the pid namespace was a bad example but now it seems I don't understand the purpose of /proc/<pid>/ns/pid with the use of setns() either.

I wasn't thinking of changing the process pid here at all, as you say from the kernel POV that must not happen, I was thinking of changing an execed userspace process view of pids.

Assuming that <pid> is valid within a callers namespace (and I believe that's checked along the way) doesn't using this allow the created userspace process to see pids in the view of the given namespace? Isn't that the intended use of this?

Ian

#25 lore raw

Re: [RFC PATCH 3/4] kmod - add call_usermodehelper_ns() helper

In reply to Ian Kent

I didn't have time to follow this thread today, will try tomorrow. Perhaps this was already answered...

Quoted 8 lines · Ian Kent
Quoted 3 lines · Oleg Nesterov

and probably we also need this for coredump helpers, we want them to be per-namespace.

To save me some time could you point me to some of the related code please. I don't normally play in that area.

See call_usermodehelper_*() in do_coredump(). This has the same problems (and just in case, of course other problems, starting from the fact that core_pattern is global). We need the right root to find the binary, etc.

Quoted 5 lines
Quoted 3 lines

kernel/kmod.c int call_usermodehelper(char *path, char **argv, char **envp, int wait) · line 652

Quoted 1 line
kernel/kmod.c +1 -0
+	switch_task_namespaces(current, ns);

This doesn't look sane because this won't switch task_active_pid_ns().

I wondered about that too but I didn't design the open()/setns()

No, I don't think we should use setns() in this case...

Quoted 5 lines
Quoted 2 lines

And otoh. If we actually want to use the caller's mnt_ns/namespaces we could simply fork/reparent a child which will do execve ?

Are you saying that the user space program should be modified to do this?

No, no, I meant that the kernel could do this (yes, not that trivial) on behalf of the caller's process.

Oleg.

author
#26 lore raw

RFC PATCH 4/4 KEYS: exec request-key within the requesting task's namespace

In reply to Ian Kent

From: Benjamin Coddington <[email protected]>

Copy the current task's namespaces into the request-key userspace helper to restrict contained processes from executing key instantiation processes outside their containers.

Cc Al Viro <[email protected]>
Cc J. Bruce Fields <[email protected]>
Cc David Howells <[email protected]>
Cc Trond Myklebust <[email protected]>
Cc Stanislav Kinsbursky <[email protected]>
Cc Oleg Nesterov <[email protected]>
Cc Eric W. Biederman <[email protected]>
---
 security/keys/request_key.c |   45 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 39 insertions(+), 6 deletions(-)
security/keys/request_key.c +39 -6
index bb4337c..b03feec 100644
@@ -17,6 +17,8 @@
 #include <linux/err.h>
 #include <linux/keyctl.h>
 #include <linux/slab.h>
+#include <linux/nsproxy.h>
+#include <linux/mount.h>
 #include "internal.h"
 
 #define key_negative_timeout	60	/* default timeout on a negative key's existence */
@@ -46,6 +48,11 @@ void complete_request_key(struct key_construction *cons, int error)
 }
 EXPORT_SYMBOL(complete_request_key);
 
+struct request_key_info {
+	struct key	*keyring;
+	struct nsproxy	*nsproxy;
+};
+
 /*
  * Initialise a usermode helper that is going to have a specific session
  * keyring.
@@ -55,9 +62,14 @@ EXPORT_SYMBOL(complete_request_key);
  */
 static int umh_keys_init(struct subprocess_info *info, struct cred *cred)
 {
-	struct key *keyring = info->data;
+	struct request_key_info *rki = info->data;
+	struct nsproxy *ns = rki->nsproxy;
 
-	return install_session_keyring_to_cred(cred, keyring);
+	if (ns) {
+		mntns_setfs(ns->mnt_ns);
+		switch_task_namespaces(current, ns);
+	}
+	return install_session_keyring_to_cred(cred, rki->keyring);
 }
 
 /*
@@ -65,8 +77,9 @@ static int umh_keys_init(struct subprocess_info *info, struct cred *cred)
  */
 static void umh_keys_cleanup(struct subprocess_info *info)
 {
-	struct key *keyring = info->data;
-	key_put(keyring);
+	struct request_key_info *rki = info->data;
+	key_put(rki->keyring);
+	kfree(rki);
 }
 
 /*
@@ -76,12 +89,32 @@ static int call_usermodehelper_keys(char *path, char **argv, char **envp,
 					struct key *session_keyring, int wait)
 {
 	struct subprocess_info *info;
+	struct request_key_info *rki;
+	struct nsproxy *ns;
+
+	ns = umh_open_ns();
+	if (ns && IS_ERR(ns))
+		return PTR_ERR(ns);
+
+	rki = kmalloc(sizeof(*rki), GFP_KERNEL);
+	if (!rki) {
+		if (ns)
+			free_nsproxy(ns);
+		return -ENOMEM;
+	}
+
+	rki->keyring = session_keyring;
+	rki->nsproxy = ns;
 
 	info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL,
 					  umh_keys_init, umh_keys_cleanup,
-					  session_keyring);
-	if (!info)
+					  rki);
+	if (!info) {
+		if (ns)
+			free_nsproxy(ns);
+		kfree(rki);
 		return -ENOMEM;
+	}
 
 	key_get(session_keyring);
 	return call_usermodehelper_exec(info, wait);