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

Stéphane Graber stgraber at ubuntu.com
Wed Jan 22 02:44:16 UTC 2014


On Tue, Jan 21, 2014 at 09:36:06PM -0500, S.Çağlar Onur wrote:
> 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 :)

I did indeed and also updated the ERROR messages to be a bit more
relevant (they were mentioning template creation).

> 
> >> ---
> >>  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>
> _______________________________________________
> 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/7b3b8ebc/attachment-0001.pgp>


More information about the lxc-devel mailing list