[lxc-devel] [PATCH] make lxcapi_get_interfaces and lxcapi_get_ips unprivileged container aware
S.Çağlar Onur
caglar at 10ur.org
Wed Jan 22 02:36:06 UTC 2014
On Tue, Jan 21, 2014 at 6:41 PM, Serge Hallyn <serge.hallyn at ubuntu.com> wrote:
> 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>
I believe Stéphane fixed them while pushing :)
>> ---
>> 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
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel
--
S.Çağlar Onur <caglar at 10ur.org>
More information about the lxc-devel
mailing list