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

Serge Hallyn serge.hallyn at ubuntu.com
Tue Jan 21 23:41:32 UTC 2014


Quoting S.Çağlar Onur (caglar at 10ur.org):
> 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, looks good except for two issues below.  With those fixed,

Acked-by: Serge E. Hallyn <serge.hallyn at ubuntu.com>

> ---
>  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");

pipefds should be closed here

> +		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");

and here

> +		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


More information about the lxc-devel mailing list