[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