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

Stéphane Graber stgraber at ubuntu.com
Tue Jan 21 16:07:01 UTC 2014


On Mon, Jan 20, 2014 at 07:57:28PM -0500, S.Çağlar Onur wrote:
> 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>


Thanks for that change!

I just did a bunch of tests against that patch and all cases I can think
of appear to be properly handled, it's really quite nice having
unprivileged containers now working identically as system ones (at least
for commonly used functions).

Since some of my code is in there and this change has a fairly high risk
potential (child process + communication + setns), I'd like to wait for
Serge to review the change before we push it to master.

Until then, I'll happily run LXC with that patch locally applied ;)

> ---
>  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
> 
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel

-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20140121/554475e8/attachment.pgp>


More information about the lxc-devel mailing list