[lxc-devel] [PATCH] make lxcapi_get_interfaces and lxcapi_get_ips unprivileged container aware

S.Çağlar Onur caglar at 10ur.org
Tue Jan 21 00:57:28 UTC 2014


Based on Stéphane's suggestion, those two API methods now;

* fork a new process,
* switch to appropriate namespace(s),
* do what we want,
* return the data over a pipe to the parent which returns the result to the original caller.

For the whole thread please see;

 https://lists.linuxcontainers.org/pipermail/lxc-devel/2014-January/007362.html

This patch also makes lxc-ls and lxc-info call those functions.

I'm adding Stéphane as an author here since both the idea as well as
the initial setns code come from him.

Author: S.Çağlar Onur <caglar at 10ur.org>
Author: Stéphane Graber <stgraber at ubuntu.com>
Signed-off-by: S.Çağlar Onur <caglar at 10ur.org>
---
 src/lxc/lxc-ls         |   2 +-
 src/lxc/lxc_info.c     |  20 ++--
 src/lxc/lxccontainer.c | 276 +++++++++++++++++++++++++++++++------------------
 3 files changed, 182 insertions(+), 116 deletions(-)

diff --git a/src/lxc/lxc-ls b/src/lxc/lxc-ls
index 68c0b41..fa53fac 100755
--- a/src/lxc/lxc-ls
+++ b/src/lxc/lxc-ls
@@ -265,7 +265,7 @@ for container_name in lxc.list_containers(config_path=nest_lxcpath):
 
             # FIXME: We should get get_ips working as non-root
             if container.running:
-                if not SUPPORT_SETNS_NET or os.geteuid() != 0:
+                if not SUPPORT_SETNS_NET:
                     entry[protocol] = 'UNKNOWN'
                     continue
 
diff --git a/src/lxc/lxc_info.c b/src/lxc/lxc_info.c
index 0990036..b515087 100644
--- a/src/lxc/lxc_info.c
+++ b/src/lxc/lxc_info.c
@@ -310,19 +310,15 @@ static int print_info(const char *name, const char *lxcpath)
 	}
 
 	if (ips) {
-		if (geteuid() == 0) {
-			char **addresses = c->get_ips(c, NULL, NULL, 0);
-			if (addresses) {
-				char *address;
-				i = 0;
-				while (addresses[i]) {
-					address = addresses[i];
-					print_info_msg_str("IP:", address);
-					i++;
-				}
+		char **addresses = c->get_ips(c, NULL, NULL, 0);
+		if (addresses) {
+			char *address;
+			i = 0;
+			while (addresses[i]) {
+				address = addresses[i];
+				print_info_msg_str("IP:", address);
+				i++;
 			}
-		} else {
-			print_info_msg_str("IP:", "UNKNOWN");
 		}
 	}
 
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index 368cb46..1336a3d 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -1372,48 +1372,51 @@ static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key)
 	return ret == 0;
 }
 
-static inline void exit_from_ns(struct lxc_container *c, int *old_netns, int *new_netns) {
-	/* Switch back to original netns */
-	if (*old_netns >= 0 && setns(*old_netns, CLONE_NEWNET))
-		SYSERROR("failed to setns");
-	if (*new_netns >= 0)
-		close(*new_netns);
-	if (*old_netns >= 0)
-		close(*old_netns);
-}
-
-static inline bool enter_to_ns(struct lxc_container *c, int *old_netns, int *new_netns) {
-	int ret = 0;
+static inline bool enter_to_ns(struct lxc_container *c) {
+	int netns, userns, ret = 0, init_pid = 0;;
 	char new_netns_path[MAXPATHLEN];
+	char new_userns_path[MAXPATHLEN];
 
 	if (!c->is_running(c))
 		goto out;
 
-	/* Save reference to old netns */
-	*old_netns = open("/proc/self/ns/net", O_RDONLY);
-	if (*old_netns < 0) {
-		SYSERROR("failed to open /proc/self/ns/net");
-		goto out;
+	init_pid = c->init_pid(c);
+
+	/* Switch to new userns */
+	if (geteuid() && access("/proc/self/ns/user", F_OK) == 0) {
+		ret = snprintf(new_userns_path, MAXPATHLEN, "/proc/%d/ns/user", init_pid);
+		if (ret < 0 || ret >= MAXPATHLEN)
+			goto out;
+
+		userns = open(new_userns_path, O_RDONLY);
+		if (userns < 0) {
+			SYSERROR("failed to open %s", new_userns_path);
+			goto out;
+		}
+
+		if (setns(userns, CLONE_NEWUSER)) {
+			SYSERROR("failed to setns for CLONE_NEWUSER");
+			goto out;
+		}
 	}
 
 	/* Switch to new netns */
-	ret = snprintf(new_netns_path, MAXPATHLEN, "/proc/%d/ns/net", c->init_pid(c));
+	ret = snprintf(new_netns_path, MAXPATHLEN, "/proc/%d/ns/net", init_pid);
 	if (ret < 0 || ret >= MAXPATHLEN)
 		goto out;
 
-	*new_netns = open(new_netns_path, O_RDONLY);
-	if (*new_netns < 0) {
+	netns = open(new_netns_path, O_RDONLY);
+	if (netns < 0) {
 		SYSERROR("failed to open %s", new_netns_path);
 		goto out;
 	}
 
-	if (setns(*new_netns, CLONE_NEWNET)) {
-		SYSERROR("failed to setns");
+	if (setns(netns, CLONE_NEWNET)) {
+		SYSERROR("failed to setns for CLONE_NEWNET");
 		goto out;
 	}
 	return true;
 out:
-	exit_from_ns(c, old_netns, new_netns);
 	return false;
 }
 
@@ -1490,135 +1493,202 @@ static bool remove_from_array(char ***names, char *cname, int size)
 
 static char** lxcapi_get_interfaces(struct lxc_container *c)
 {
-	int i, count = 0;
-	struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
+	pid_t pid;
+	int i, count = 0, pipefd[2];
 	char **interfaces = NULL;
-	int old_netns = -1, new_netns = -1;
+	char interface[IFNAMSIZ];
 
-	if (am_unpriv()) {
-		ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
-		goto out;
+	if(pipe(pipefd) < 0) {
+		SYSERROR("pipe failed");
+		return NULL;
 	}
 
-	if (!enter_to_ns(c, &old_netns, &new_netns))
-		goto out;
+	pid = fork();
+	if (pid < 0) {
+		SYSERROR("failed to fork task for container creation template\n");
+		return NULL;
+	}
 
-	/* Grab the list of interfaces */
-	if (getifaddrs(&interfaceArray)) {
-		SYSERROR("failed to get interfaces list");
-		goto out;
+	if (pid == 0) { // child
+		int ret = 1, nbytes;
+		struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
+
+		/* close the read-end of the pipe */
+		close(pipefd[0]);
+
+		if (!enter_to_ns(c)) {
+			SYSERROR("failed to enter namespace");
+			goto out;
+		}
+
+		/* Grab the list of interfaces */
+		if (getifaddrs(&interfaceArray)) {
+			SYSERROR("failed to get interfaces list");
+			goto out;
+		}
+
+		/* Iterate through the interfaces */
+		for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
+			nbytes = write(pipefd[1], tempIfAddr->ifa_name, IFNAMSIZ);
+			if (nbytes < 0) {
+				ERROR("write failed");
+				goto out;
+			}
+			count++;
+		}
+		ret = 0;
+
+	out:
+		if (interfaceArray)
+			freeifaddrs(interfaceArray);
+
+		/* close the write-end of the pipe, thus sending EOF to the reader */
+		close(pipefd[1]);
+		exit(ret);
 	}
 
-	/* Iterate through the interfaces */
-	for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
-		if (array_contains(&interfaces, tempIfAddr->ifa_name, count))
-			continue;
+	/* close the write-end of the pipe */
+	close(pipefd[1]);
 
-		if(!add_to_array(&interfaces, tempIfAddr->ifa_name, count))
-			goto err;
+	while (read(pipefd[0], &interface, IFNAMSIZ) > 0) {
+		if (array_contains(&interfaces, interface, count))
+				continue;
+
+		if(!add_to_array(&interfaces, interface, count))
+			ERROR("PARENT: add_to_array failed");
 		count++;
 	}
 
-out:
-	if (interfaceArray)
-		freeifaddrs(interfaceArray);
+	if (wait_for_pid(pid) != 0) {
+		for(i=0;i<count;i++)
+			free(interfaces[i]);
+		free(interfaces);
+		interfaces = NULL;
+	}
 
-	exit_from_ns(c, &old_netns, &new_netns);
+	/* close the read-end of the pipe */
+	close(pipefd[0]);
 
 	/* Append NULL to the array */
 	if(interfaces)
 		interfaces = (char **)lxc_append_null_to_array((void **)interfaces, count);
 
 	return interfaces;
-
-err:
-	for(i=0;i<count;i++)
-		free(interfaces[i]);
-	free(interfaces);
-	interfaces = NULL;
-	goto out;
 }
 
 static char** lxcapi_get_ips(struct lxc_container *c, const char* interface, const char* family, int scope)
 {
-	int i, count = 0;
-	struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
-	char addressOutputBuffer[INET6_ADDRSTRLEN];
-	void *tempAddrPtr = NULL;
+	pid_t pid;
+	int i, count = 0, pipefd[2];
 	char **addresses = NULL;
-	char *address = NULL;
-	int old_netns = -1, new_netns = -1;
+	char address[INET6_ADDRSTRLEN];
 
-	if (am_unpriv()) {
-		ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
-		goto out;
+	if(pipe(pipefd) < 0) {
+		SYSERROR("pipe failed");
+		return NULL;
 	}
 
-	if (!enter_to_ns(c, &old_netns, &new_netns))
-		goto out;
-
-	/* Grab the list of interfaces */
-	if (getifaddrs(&interfaceArray)) {
-		SYSERROR("failed to get interfaces list");
-		goto out;
+	pid = fork();
+	if (pid < 0) {
+		SYSERROR("failed to fork task for container creation template\n");
+		return NULL;
 	}
 
-	/* Iterate through the interfaces */
-	for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
-		if (tempIfAddr->ifa_addr == NULL)
-			continue;
+	if (pid == 0) { // child
+		int ret = 1, nbytes;
+		struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
+		char addressOutputBuffer[INET6_ADDRSTRLEN];
+		void *tempAddrPtr = NULL;
+		char *address = NULL;
 
-		if(tempIfAddr->ifa_addr->sa_family == AF_INET) {
-			if (family && strcmp(family, "inet"))
-				continue;
-			tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr;
+		/* close the read-end of the pipe */
+		close(pipefd[0]);
+
+		if (!enter_to_ns(c)) {
+			SYSERROR("failed to enter namespace");
+			goto out;
+		}
+
+		/* Grab the list of interfaces */
+		if (getifaddrs(&interfaceArray)) {
+			SYSERROR("failed to get interfaces list");
+			goto out;
 		}
-		else {
-			if (family && strcmp(family, "inet6"))
+
+		/* Iterate through the interfaces */
+		for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
+			if (tempIfAddr->ifa_addr == NULL)
 				continue;
 
-			if (((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_scope_id != scope)
+			if(tempIfAddr->ifa_addr->sa_family == AF_INET) {
+				if (family && strcmp(family, "inet"))
+					continue;
+				tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr;
+			}
+			else {
+				if (family && strcmp(family, "inet6"))
+					continue;
+
+				if (((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_scope_id != scope)
+					continue;
+
+				tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr;
+			}
+
+			if (interface && strcmp(interface, tempIfAddr->ifa_name))
 				continue;
+			else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0)
+				continue;
+
+			address = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family,
+						tempAddrPtr,
+						addressOutputBuffer,
+						sizeof(addressOutputBuffer));
+			if (!address)
+					continue;
 
-			tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr;
+			nbytes = write(pipefd[1], address, INET6_ADDRSTRLEN);
+			if (nbytes < 0) {
+				ERROR("write failed");
+				goto out;
+			}
+			count++;
 		}
+		ret = 0;
 
-		if (interface && strcmp(interface, tempIfAddr->ifa_name))
-			continue;
-		else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0)
-			continue;
+	out:
+		if(interfaceArray)
+			freeifaddrs(interfaceArray);
 
-		address = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family,
-					   tempAddrPtr,
-					   addressOutputBuffer,
-					   sizeof(addressOutputBuffer));
-		if (!address)
-			continue;
+		/* close the write-end of the pipe, thus sending EOF to the reader */
+		close(pipefd[1]);
+		exit(ret);
+    }
+
+	/* close the write-end of the pipe */
+	close(pipefd[1]);
 
+	while (read(pipefd[0], &address, INET6_ADDRSTRLEN) > 0) {
 		if(!add_to_array(&addresses, address, count))
-			goto err;
+			ERROR("PARENT: add_to_array failed");
 		count++;
 	}
 
-out:
-	if(interfaceArray)
-		freeifaddrs(interfaceArray);
+	if (wait_for_pid(pid) != 0) {
+		for(i=0;i<count;i++)
+			free(addresses[i]);
+		free(addresses);
+		addresses = NULL;
+	}
 
-	exit_from_ns(c, &old_netns, &new_netns);
+	/* close the read-end of the pipe */
+	close(pipefd[0]);
 
 	/* Append NULL to the array */
 	if(addresses)
 		addresses = (char **)lxc_append_null_to_array((void **)addresses, count);
 
 	return addresses;
-
-err:
-	for(i=0;i<count;i++)
-		free(addresses[i]);
-	free(addresses);
-	addresses = NULL;
-
-	goto out;
 }
 
 static int lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen)
-- 
1.8.3.2



More information about the lxc-devel mailing list