[lxc-devel] [PATCH 4/4] lxc-attach: Change cgroup, personality and drop capabilities when attaching to container

Christian Seiler christian at iwakd.de
Fri Feb 3 12:54:21 UTC 2012


lxc-attach is reworked so that it adds the newly attached process to the
cgroup of the container, changes the personality of the process to that
of the container and drops capabilities to those specified in the container
configuration file. The latter can be overridden with a new option that
allows to retain capabilities.

In order to correctly put the new process in the correct cgroup, lxc-attach
now uses a similar synchronization logic to lxc-start, i.e. the parent
process puts the child into the cgroup, and then tells the child to proceed.
The child then attaches itself to the correct namespace, changes personality
and drops capabilities. This also implies that the fork() is done before
attaching to the container's namespaces, i.e. only the child process will do
that, the parent is left out of it.
---
 src/lxc/lxc_attach.c |  151 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 135 insertions(+), 16 deletions(-)

diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c
index ed3d5a4..016806d 100644
--- a/src/lxc/lxc_attach.c
+++ b/src/lxc/lxc_attach.c
@@ -35,13 +35,35 @@
 #include "namespace.h"
 #include "caps.h"
 #include "log.h"
+#include "conf.h"
+#include "confile.h"
+#include "start.h"
+#include "sync.h"
+#include "cgroup.h"
 
 lxc_log_define(lxc_attach_ui, lxc);
 
+static struct lxc_list defines;
+
 static const struct option my_longopts[] = {
+	{"rcfile", required_argument, 0, 'f'},
+	{"define", required_argument, 0, 's'},
+	{"keep-capabilities", no_argument, 0, 'k'},
 	LXC_COMMON_OPTIONS
 };
 
+static int keep_capabilities = 0;
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+	switch (c) {
+	case 'f': args->rcfile = arg; break;
+	case 's': return lxc_config_define_add(&defines, arg);
+	case 'k': keep_capabilities = 1; break;
+	}
+	return 0;
+}
+
 static struct lxc_arguments my_args = {
 	.progname = "lxc-attach",
 	.help     = "\
@@ -50,19 +72,31 @@ static struct lxc_arguments my_args = {
 Execute the specified command - enter the container NAME\n\
 \n\
 Options :\n\
-  -n, --name=NAME   NAME for name of the container\n",
+  -n, --name=NAME        NAME for name of the container\n\
+  -f, --rcfile=FILE      Load configuration file FILE\n\
+  -s, --define KEY=VAL   Assign VAL to configuration variable KEY\n\
+  -k, --keep-kapabilties Don't drop capabilities when attaching to container\n\
+                         WARNING: This may leak capabilities into the\n\
+                         container, especially if using lxc-attach to\n\
+                         start programs such as sshd or cron.",
 	.options  = my_longopts,
-	.parser   = NULL,
+	.parser   = my_parser,
 	.checker  = NULL,
 };
 
 int main(int argc, char *argv[], char *envp[])
 {
+	int err = -1;
 	int ret;
 	pid_t pid;
 	struct passwd *passwd;
+	struct lxc_conf *conf;
+	struct lxc_handler *handler;
 	uid_t uid;
 	char *curdir;
+	char *rcfile = NULL;
+	
+	lxc_list_init(&defines);
 
 	ret = lxc_caps_init();
 	if (ret)
@@ -77,40 +111,96 @@ int main(int argc, char *argv[], char *envp[])
 	if (ret)
 		return ret;
 
+	/* rcfile is specified in the cli option */
+	if (my_args.rcfile)
+		rcfile = (char *)my_args.rcfile;
+	else {
+		int rc;
+
+		rc = asprintf(&rcfile, LXCPATH "/%s/config", my_args.name);
+		if (rc == -1) {
+			SYSERROR("failed to allocate memory");
+			return err;
+		}
+
+		/* container configuration does not exist */
+		if (access(rcfile, F_OK)) {
+			free(rcfile);
+			rcfile = NULL;
+		}
+	}
+
+	conf = lxc_conf_init();
+	if (!conf) {
+		ERROR("failed to initialize configuration");
+		return err;
+	}
+
+	if (rcfile && lxc_config_read(rcfile, conf)) {
+		ERROR("failed to read configuration file");
+		return err;
+	}
+
+	if (lxc_config_define_load(&defines, conf))
+		return err;
+
 	pid = get_init_pid(my_args.name);
 	if (pid < 0) {
 		ERROR("failed to get the init pid");
 		return -1;
 	}
 
-	curdir = get_current_dir_name();
-
-	ret = lxc_attach(pid);
-	if (ret < 0) {
-		ERROR("failed to enter the namespace");
+	/* hack: we need sync.h infrastructure - and that needs a handler */
+	handler = malloc(sizeof(*handler));
+	if (!handler) {
+		ERROR("failed to allocate memory for internal handle");
 		return -1;
 	}
 
-	if (curdir && chdir(curdir))
-		WARN("could not change directory to '%s'", curdir);
+	memset(handler, 0, sizeof(*handler));
 
-	free(curdir);
+	handler->conf = conf;
+
+	if (lxc_sync_init(handler)) {
+		ERROR("failed to initialize synchronization socket");
+		return -1;
+	}
 
-	pid = fork();
+	handler->pid = fork();
 
-	if (pid < 0) {
+	if (handler->pid < 0) {
 		SYSERROR("failed to fork");
 		return -1;
 	}
 
-	if (pid) {
+	if (handler->pid) {
 		int status;
 
+		lxc_sync_fini_child(handler);
+
+		if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE))
+			return -1;
+
+		/* attach child process to cgroup of container */
+		if (lxc_cgroup_attach(my_args.name, handler->pid)) {
+			SYSERROR("could not put child process in cgroup of container");
+			return -1;
+		}
+
+		/* Tell the child to continue its initialization and wait for
+		 * it to exec or return an error
+		 */
+		if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE))
+			return -1;
+		
+		/* we're done */
+		lxc_sync_fini(handler);
+
 	again:
-		if (waitpid(pid, &status, 0) < 0) {
+		if (waitpid(handler->pid, &status, 0) < 0) {
 			if (errno == EINTR)
 				goto again;
-			SYSERROR("failed to wait '%d'", pid);
+			SYSERROR("failed to wait '%d'", handler->pid);
 			return -1;
 		}
 
@@ -120,7 +210,36 @@ int main(int argc, char *argv[], char *envp[])
 		return -1;
 	}
 
-	if (!pid) {
+	if (!handler->pid) {
+		lxc_sync_fini_parent(handler);
+
+		/* Tell the parent task it can begin to put us in
+		 * the cgroup and wait for it to finish
+		 */
+		if (lxc_sync_barrier_parent(handler, LXC_SYNC_CONFIGURE))
+			return -1;
+
+		curdir = get_current_dir_name();
+
+		ret = lxc_attach(pid);
+		if (ret < 0) {
+			ERROR("failed to enter the namespace");
+			lxc_sync_wake_parent(handler, LXC_SYNC_POST_CONFIGURE);
+			return -1;
+		}
+
+		if (curdir && chdir(curdir))
+			WARN("could not change directory to '%s'", curdir);
+
+		free(curdir);
+
+		if (lxc_setup_for_attach(my_args.name, conf,
+		                         keep_capabilities)) {
+			SYSERROR("failed to change process context to "
+			         "match that of the container");
+			lxc_sync_wake_parent(handler, LXC_SYNC_POST_CONFIGURE);
+			return -1;
+		}
 
 		if (my_args.argc) {
 			execve(my_args.argv[0], my_args.argv, envp);
-- 
1.7.2.5





More information about the lxc-devel mailing list