[lxc-devel] [PATCH 1/1] cgmanager: chown cgroups to the container root

Serge Hallyn serge.hallyn at ubuntu.com
Fri Jan 24 05:56:15 UTC 2014


After this patch, starting an unprivileged container using
cgmanager gets the cgroup chown to the container root, so
that it can install the cgmanager (proxy) and make cgroup
requests.

(Still desirable and not in this patch is the automatic setup of
/sys/fs/cgroup/manager/sock, which you can currently do with
two lxc.mount.entries)

Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
---
 src/lxc/cgmanager.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/lxc/cgroup.c    |   8 +++
 src/lxc/cgroup.h    |   2 +
 src/lxc/start.c     |   3 +
 4 files changed, 178 insertions(+), 2 deletions(-)

diff --git a/src/lxc/cgmanager.c b/src/lxc/cgmanager.c
index e43e1f7..4f83272 100644
--- a/src/lxc/cgmanager.c
+++ b/src/lxc/cgmanager.c
@@ -75,6 +75,44 @@ static void cgmanager_disconnected(DBusConnection *connection)
 	}
 }
 
+static int send_creds(int sock, int rpid, int ruid, int rgid)
+{
+	struct msghdr msg = { 0 };
+	struct iovec iov;
+	struct cmsghdr *cmsg;
+	struct ucred cred = {
+		.pid = rpid,
+		.uid = ruid,
+		.gid = rgid,
+	};
+	char cmsgbuf[CMSG_SPACE(sizeof(cred))];
+	char buf[1];
+	buf[0] = 'p';
+
+	msg.msg_control = cmsgbuf;
+	msg.msg_controllen = sizeof(cmsgbuf);
+
+	cmsg = CMSG_FIRSTHDR(&msg);
+	cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+	cmsg->cmsg_level = SOL_SOCKET;
+	cmsg->cmsg_type = SCM_CREDENTIALS;
+	memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred));
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+
+	iov.iov_base = buf;
+	iov.iov_len = sizeof(buf);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	if (sendmsg(sock, &msg, 0) < 0) {
+		perror("sendmsg");
+		return -1;
+	}
+	return 0;
+}
+
 #define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
 bool lxc_init_cgmanager(void)
 {
@@ -120,10 +158,121 @@ static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path
 		return false;
 	}
 
-	// TODO - try to chown the cgroup to the container root
 	return true;
 }
 
+struct chown_data {
+	const char *controller;
+	const char *cgroup_path;
+};
+
+static int do_chown_cgroup(const char *controller, const char *cgroup_path)
+{
+	int sv[2] = {-1, -1}, optval = 1;
+	char buf[1];
+
+	if (setgid(0) < 0)
+		WARN("Failed to setgid to 0");
+	if (setuid(0) < 0)
+		WARN("Failed to setuid to 0");
+
+	if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
+		SYSERROR("Error creating socketpair");
+		exit(1);
+	}
+	if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
+		SYSERROR("setsockopt failed");
+		exit(1);
+	}
+	if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
+		SYSERROR("setsockopt failed");
+		exit(1);
+	}
+	if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller,
+				       cgroup_path, sv[1]) != 0) {
+		ERROR("call to cgmanager_chown_scm_sync failed");
+ 		return -1;
+ 	}
+	/* now send credentials */
+
+	fd_set rfds;
+	FD_ZERO(&rfds);
+	FD_SET(sv[0], &rfds);
+	if (select(sv[0]+1, &rfds, NULL, NULL, NULL) < 0) {
+		ERROR("Error getting go-ahead from server: %s", strerror(errno));
+		return -1;
+	}
+	if (read(sv[0], &buf, 1) != 1) {
+		ERROR("Error getting reply from server over socketpair");
+		return -1;
+	}
+	if (send_creds(sv[0], getpid(), getuid(), getgid())) {
+		ERROR("Error sending pid over SCM_CREDENTIAL");
+		return -1;
+	}
+	FD_ZERO(&rfds);
+	FD_SET(sv[0], &rfds);
+	if (select(sv[0]+1, &rfds, NULL, NULL, NULL) < 0) {
+		ERROR("Error getting go-ahead from server: %s", strerror(errno));
+		return -1;
+	}
+	if (read(sv[0], &buf, 1) != 1) {
+		ERROR("Error getting reply from server over socketpair");
+		return -1;
+	}
+	if (send_creds(sv[0], getpid(), 0, 0)) {
+		ERROR("Error sending pid over SCM_CREDENTIAL");
+		return -1;
+	}
+	FD_ZERO(&rfds);
+	FD_SET(sv[0], &rfds);
+	if (select(sv[0]+1, &rfds, NULL, NULL, NULL) < 0) {
+		ERROR("Error getting go-ahead from server: %s", strerror(errno));
+		return -1;
+	}
+	int ret = read(sv[0], buf, 1);
+	close(sv[0]);
+	close(sv[1]);
+	if (ret == 1 && *buf == '1')
+		return 0;
+	return -1;
+}
+
+static int chown_cgroup_wrapper(void *data)
+{
+	struct chown_data *arg = data;
+	return do_chown_cgroup(arg->controller, arg->cgroup_path);
+}
+
+static bool chown_cgroup(const char *controller, const char *cgroup_path,
+			struct lxc_conf *conf)
+{
+	pid_t pid;
+	struct chown_data data;
+	data.controller = controller;
+	data.cgroup_path = cgroup_path;
+
+	if (lxc_list_empty(&conf->id_map)) {
+		if (do_chown_cgroup(controller, cgroup_path) < 0)
+			return false;
+		return true;
+	}
+
+	if ((pid = fork()) < 0) {
+		SYSERROR("fork");
+		return false;
+	}
+	if (pid > 0) {
+		if (wait_for_pid(pid)) {
+			ERROR("Error chowning cgroup");
+			return false;
+		}
+		return true;
+	}
+	if (userns_exec_1(conf, chown_cgroup_wrapper, &data) < 0)
+		exit(1);
+	exit(0);
+}
 
 struct cgm_data {
 	int nr_subsystems;
@@ -432,6 +581,19 @@ static bool cgm_setup_limits(struct lxc_handler *handler, bool with_devices)
 	return setup_limits(handler, with_devices);
 }
 
+static bool cgm_chown(struct lxc_handler *handler)
+{
+	struct cgm_data *d = handler->cgroup_info->data;
+	int i;
+
+	for (i = 0; i < d->nr_subsystems; i++) {
+		if (!chown_cgroup(d->subsystems[i], d->cgroup_path, handler->conf))
+			WARN("Failed to chown %s:%s to container root",
+				d->subsystems[i], d->cgroup_path);
+	}
+	return true;
+}
+
 static struct cgroup_ops cgmanager_ops = {
 	.destroy = cgm_destroy,
 	.init = cgm_init,
@@ -443,6 +605,7 @@ static struct cgroup_ops cgmanager_ops = {
 	.set = cgm_set,
 	.unfreeze_fromhandler = cgm_unfreeze_fromhandler,
 	.setup_limits = cgm_setup_limits,
-	.name = "cgmanager"
+	.name = "cgmanager",
+	.chown = cgm_chown,
 };
 #endif
diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c
index 4482b32..ce1096d 100644
--- a/src/lxc/cgroup.c
+++ b/src/lxc/cgroup.c
@@ -2199,6 +2199,7 @@ static struct cgroup_ops cgfs_ops = {
 	.unfreeze_fromhandler = cgfs_unfreeze_fromhandler,
 	.setup_limits = cgroupfs_setup_limits,
 	.name = "cgroupfs",
+	.chown = NULL,
 };
 static void init_cg_ops(void)
 {
@@ -2297,3 +2298,10 @@ bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices)
 {
 	return active_cg_ops->setup_limits(handler, with_devices);
 }
+
+bool cgroup_chown(struct lxc_handler *handler)
+{
+	if (active_cg_ops->chown)
+		return active_cg_ops->chown(handler);
+	return true;
+}
diff --git a/src/lxc/cgroup.h b/src/lxc/cgroup.h
index af5a5c8..fbb3b87 100644
--- a/src/lxc/cgroup.h
+++ b/src/lxc/cgroup.h
@@ -180,6 +180,7 @@ struct cgroup_ops {
 	int (*get)(const char *filename, char *value, size_t len, const char *name, const char *lxcpath);
 	int (*unfreeze_fromhandler)(struct lxc_handler *handler);
 	bool (*setup_limits)(struct lxc_handler *handler, bool with_devices);
+	bool (*chown)(struct lxc_handler *handler);
 	const char *name;
 };
 
@@ -209,6 +210,7 @@ extern void cgroup_destroy(struct lxc_handler *handler);
 extern bool cgroup_init(struct lxc_handler *handler);
 extern bool cgroup_create(struct lxc_handler *handler);
 extern bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices);
+extern bool cgroup_chown(struct lxc_handler *handler);
 extern bool cgroup_enter(struct lxc_handler *handler);
 extern void cgroup_cleanup(struct lxc_handler *handler);
 extern bool cgroup_create_legacy(struct lxc_handler *handler);
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 2fcd845..6de7a83 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -804,6 +804,9 @@ static int lxc_spawn(struct lxc_handler *handler)
 	if (!cgroup_enter(handler))
 		goto out_delete_net;
 
+	if (!cgroup_chown(handler))
+		goto out_delete_net;
+
 	if (failed_before_rename)
 		goto out_delete_net;
 
-- 
1.8.5.3



More information about the lxc-devel mailing list