[lxc-devel] [PATCH] attach: Support unprivileged containers

Stéphane Graber stgraber at ubuntu.com
Mon Jan 20 22:35:16 UTC 2014


This change makes lxc-attach and the matching API functions work
properly with unprivileged containers.

The trick needed to make that possible was to always start with the
userns when attaching and also relocate the cgroup management code so
that the intermediate process is moved to the cgroup before attaching to
the container's namespace as doing so later would fail due to missing
permissions.

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 src/lxc/attach.c       | 87 ++++++++++++++++++++++++++++++--------------------
 src/lxc/lxc_attach.c   |  5 ---
 src/lxc/lxccontainer.c | 10 ------
 3 files changed, 52 insertions(+), 50 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index de32549..3ee1d4d 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -144,10 +144,10 @@ static int lxc_attach_to_ns(pid_t pid, int which)
 	 * the file for user namepsaces in /proc/$pid/ns will be called
 	 * 'user' once the kernel supports it
 	 */
-	static char *ns[] = { "mnt", "pid", "uts", "ipc", "user", "net" };
+	static char *ns[] = { "user", "mnt", "pid", "uts", "ipc", "net" };
 	static int flags[] = {
-		CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC,
-		CLONE_NEWUSER, CLONE_NEWNET
+		CLONE_NEWUSER, CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC,
+		CLONE_NEWNET
 	};
 	static const int size = sizeof(ns) / sizeof(char *);
 	int fd[size];
@@ -593,7 +593,7 @@ static lxc_attach_options_t attach_static_default_options = LXC_ATTACH_OPTIONS_D
 int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_function, void* exec_payload, lxc_attach_options_t* options, pid_t* attached_process)
 {
 	int ret, status;
-	pid_t init_pid, pid, attached_pid;
+	pid_t init_pid, pid, attached_pid, expected;
 	struct lxc_proc_context_info *init_ctx;
 	char* cwd;
 	char* new_cwd;
@@ -689,7 +689,6 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
 
 	if (pid) {
 		pid_t to_cleanup_pid = pid;
-		int expected = 0;
 
 		/* inital thread, we close the socket that is for the
 		 * subprocesses
@@ -697,6 +696,44 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
 		close(ipc_sockets[1]);
 		free(cwd);
 
+		/* attach to cgroup, if requested */
+		if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) {
+			struct cgroup_meta_data *meta_data;
+			struct cgroup_process_info *container_info;
+
+			meta_data = lxc_cgroup_load_meta();
+			if (!meta_data) {
+				ERROR("could not move attached process %ld to cgroup of container", (long)pid);
+				goto cleanup_error;
+			}
+
+			container_info = lxc_cgroup_get_container_info(name, lxcpath, meta_data);
+			lxc_cgroup_put_meta(meta_data);
+			if (!container_info) {
+				ERROR("could not move attached process %ld to cgroup of container", (long)pid);
+				goto cleanup_error;
+			}
+
+			/*
+			 * TODO - switch over to using a cgroup_operation.  We can't use
+			 * cgroup_enter() as that takes a handler.
+			 */
+			ret = lxc_cgroupfs_enter(container_info, pid, false);
+			lxc_cgroup_process_info_free(container_info);
+			if (ret < 0) {
+				ERROR("could not move attached process %ld to cgroup of container", (long)pid);
+				goto cleanup_error;
+			}
+		}
+
+		/* Let the child process know to go ahead */
+		status = 0;
+		ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status));
+		if (ret <= 0) {
+			ERROR("error using IPC to notify attached process for initialization (0)");
+			goto cleanup_error;
+		}
+
 		/* get pid from intermediate process */
 		ret = lxc_read_nointr_expect(ipc_sockets[0], &attached_pid, sizeof(attached_pid), NULL);
 		if (ret <= 0) {
@@ -730,36 +767,6 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
 			goto cleanup_error;
 		}
 
-		/* attach to cgroup, if requested */
-		if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) {
-			struct cgroup_meta_data *meta_data;
-			struct cgroup_process_info *container_info;
-
-			meta_data = lxc_cgroup_load_meta();
-			if (!meta_data) {
-				ERROR("could not move attached process %ld to cgroup of container", (long)attached_pid);
-				goto cleanup_error;
-			}
-
-			container_info = lxc_cgroup_get_container_info(name, lxcpath, meta_data);
-			lxc_cgroup_put_meta(meta_data);
-			if (!container_info) {
-				ERROR("could not move attached process %ld to cgroup of container", (long)attached_pid);
-				goto cleanup_error;
-			}
-
-			/*
-			 * TODO - switch over to using a cgroup_operation.  We can't use
-			 * cgroup_enter() as that takes a handler.
-			 */
-			ret = lxc_cgroupfs_enter(container_info, attached_pid, false);
-			lxc_cgroup_process_info_free(container_info);
-			if (ret < 0) {
-				ERROR("could not move attached process %ld to cgroup of container", (long)attached_pid);
-				goto cleanup_error;
-			}
-		}
-
 		/* tell attached process we're done */
 		status = 2;
 		ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status));
@@ -798,6 +805,16 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
 	 */
 	close(ipc_sockets[0]);
 
+	/* Wait for the parent to have setup cgroups */
+	expected = 0;
+	status = -1;
+	ret = lxc_read_nointr_expect(ipc_sockets[1], &status, sizeof(status), &expected);
+	if (ret <= 0) {
+		ERROR("error communicating with child process");
+		shutdown(ipc_sockets[1], SHUT_RDWR);
+		rexit(-1);
+	}
+
 	/* attach now, create another subprocess later, since pid namespaces
 	 * only really affect the children of the current process
 	 */
diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c
index 1159d09..6744c05 100644
--- a/src/lxc/lxc_attach.c
+++ b/src/lxc/lxc_attach.c
@@ -188,11 +188,6 @@ int main(int argc, char *argv[])
 	lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
 	lxc_attach_command_t command;
 
-        if (geteuid() != 0) {
-                ERROR("lxc-attach is not currently supported with unprivileged containers");
-                return -1;
-        }
-
 	ret = lxc_caps_init();
 	if (ret)
 		return ret;
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index 368cb46..38cf24e 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -2567,11 +2567,6 @@ static int lxcapi_attach(struct lxc_container *c, lxc_attach_exec_t exec_functio
 	if (!c)
 		return -1;
 
-	if (am_unpriv()) {
-		ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
-		return -1;
-	}
-
 	return lxc_attach(c->name, c->config_path, exec_function, exec_payload, options, attached_process);
 }
 
@@ -2584,11 +2579,6 @@ static int lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t
 	if (!c)
 		return -1;
 
-	if (am_unpriv()) {
-		ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
-		return -1;
-	}
-
 	command.program = (char*)program;
 	command.argv = (char**)argv;
 	r = lxc_attach(c->name, c->config_path, lxc_attach_run_command, &command, options, &pid);
-- 
1.8.5.3



More information about the lxc-devel mailing list