jail: add basic support for network namespaces
authorDaniel Golle <daniel@makrotopia.org>
Wed, 4 Dec 2019 13:06:06 +0000 (14:06 +0100)
committerDaniel Golle <daniel@makrotopia.org>
Tue, 21 Jan 2020 10:44:31 +0000 (12:44 +0200)
Add new 'netns' flag for procd_add_jail to make ujail setup a new
network namespace for the jailed service.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
CMakeLists.txt
jail/jail.c
service/instance.c
service/instance.h

index a566acdfd259c01b5bf80d817c3398f9a216b4bb..cff47cfa38c4db46e35a8189ffeb62200d5f7475 100644 (file)
@@ -105,7 +105,7 @@ endif()
 
 IF(JAIL_SUPPORT)
 ADD_EXECUTABLE(ujail jail/jail.c jail/elf.c jail/fs.c jail/capabilities.c)
-TARGET_LINK_LIBRARIES(ujail ${ubox} ${blobmsg_json})
+TARGET_LINK_LIBRARIES(ujail ${ubox} ${ubus} ${blobmsg_json})
 INSTALL(TARGETS ujail
        RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
 )
index c6096f89502936509589f47ae506ec8088b2a4be..2d23ad22ddceebd8e082ec8901116579bf8ed27c 100644 (file)
 #include "log.h"
 
 #include <libubox/uloop.h>
+#include <libubus.h>
 
 #define STACK_SIZE     (1024 * 1024)
-#define OPT_ARGS       "S:C:n:h:r:w:d:psulocU:G:"
+#define OPT_ARGS       "S:C:n:h:r:w:d:psulocU:G:N"
+
+#define NAMESPACE_MOUNT                (1U << 0)
+#define NAMESPACE_IPC          (1U << 1)
+#define NAMESPACE_NET          (1U << 2)
+#define NAMESPACE_PID          (1U << 3)
+#define NAMESPACE_USER         (1U << 4)
+#define NAMESPACE_UTS          (1U << 5)
+#define NAMESPACE_CGROUP       (1U << 6)
 
 static struct {
        char *name;
@@ -224,6 +233,7 @@ static void usage(void)
        fprintf(stderr, "  -n <name>\tthe name of the jail\n");
        fprintf(stderr, "namespace jail options:\n");
        fprintf(stderr, "  -h <hostname>\tchange the hostname of the jail\n");
+       fprintf(stderr, "  -N\t\tjail has network namespace\n");
        fprintf(stderr, "  -r <file>\treadonly files that should be staged\n");
        fprintf(stderr, "  -w <file>\twriteable files that should be staged\n");
        fprintf(stderr, "  -p\t\tjail has /proc\n");
@@ -348,6 +358,29 @@ static void jail_handle_signal(int signo)
        kill(jail_process.pid, signo);
 }
 
+static void netns_updown(bool start)
+{
+       struct ubus_context *ctx = ubus_connect(NULL);
+       static struct blob_buf req;
+       uint32_t id;
+       pid_t pid = getpid();
+
+       if (!ctx)
+               return;
+
+       blob_buf_init(&req, 0);
+       blobmsg_add_string(&req, "jail", opts.name);
+       blobmsg_add_u32(&req, "pid", pid);
+       blobmsg_add_u8(&req, "start", start);
+
+       if (ubus_lookup_id(ctx, "network", &id) ||
+           ubus_invoke(ctx, id, "netns_updown", req.head, NULL, NULL, 3000))
+               INFO("ubus request failed\n");
+
+       blob_buf_free(&req);
+       ubus_free(ctx);
+}
+
 int main(int argc, char **argv)
 {
        sigset_t sigmask;
@@ -371,15 +404,15 @@ int main(int argc, char **argv)
                        debug = atoi(optarg);
                        break;
                case 'p':
-                       opts.namespace = 1;
+                       opts.namespace |= NAMESPACE_MOUNT;
                        opts.procfs = 1;
                        break;
                case 'o':
-                       opts.namespace = 1;
+                       opts.namespace |= NAMESPACE_MOUNT;
                        opts.ronly = 1;
                        break;
                case 's':
-                       opts.namespace = 1;
+                       opts.namespace |= NAMESPACE_MOUNT;
                        opts.sysfs = 1;
                        break;
                case 'S':
@@ -395,23 +428,26 @@ int main(int argc, char **argv)
                case 'n':
                        opts.name = optarg;
                        break;
+               case 'N':
+                       opts.namespace |= NAMESPACE_NET;
+                       break;
                case 'h':
                        opts.hostname = optarg;
                        break;
                case 'r':
-                       opts.namespace = 1;
+                       opts.namespace |= NAMESPACE_MOUNT;
                        add_path_and_deps(optarg, 1, 0, 0);
                        break;
                case 'w':
-                       opts.namespace = 1;
+                       opts.namespace |= NAMESPACE_MOUNT;
                        add_path_and_deps(optarg, 0, 0, 0);
                        break;
                case 'u':
-                       opts.namespace = 1;
+                       opts.namespace |= NAMESPACE_MOUNT;
                        add_mount(ubus, 0, -1);
                        break;
                case 'l':
-                       opts.namespace = 1;
+                       opts.namespace |= NAMESPACE_MOUNT;
                        add_mount(log, 0, -1);
                        break;
                case 'U':
@@ -469,19 +505,29 @@ int main(int argc, char **argv)
        }
 
        if (opts.namespace) {
-               add_mount("/dev/full", 0, -1);
-               add_mount("/dev/null", 0, -1);
-               add_mount("/dev/urandom", 0, -1);
-               add_mount("/dev/zero", 0, -1);
-
-               if (opts.user || opts.group) {
-                       add_mount("/etc/passwd", 0, -1);
-                       add_mount("/etc/group", 0, -1);
+               int flags = SIGCHLD | CLONE_NEWPID | CLONE_NEWIPC;
+
+               if (opts.namespace & NAMESPACE_MOUNT) {
+                       flags |= CLONE_NEWNS;
+                       add_mount("/dev/full", 0, -1);
+                       add_mount("/dev/null", 0, -1);
+                       add_mount("/dev/urandom", 0, -1);
+                       add_mount("/dev/zero", 0, -1);
+
+                       if (opts.user || opts.group) {
+                               add_mount("/etc/passwd", 0, -1);
+                               add_mount("/etc/group", 0, -1);
+                       }
                }
 
-               int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | SIGCHLD;
                if (opts.hostname)
                        flags |= CLONE_NEWUTS;
+
+               if (opts.namespace & NAMESPACE_NET) {
+                       unshare(CLONE_NEWNET);
+                       netns_updown(true);
+               };
+
                jail_process.pid = clone(exec_jail, child_stack + STACK_SIZE, flags, NULL);
        } else {
                jail_process.pid = fork();
@@ -498,6 +544,9 @@ int main(int argc, char **argv)
                        uloop_run();
                }
                uloop_done();
+               if (opts.namespace & NAMESPACE_NET)
+                       netns_updown(false);
+
                return jail_return_code;
        } else if (jail_process.pid == 0) {
                /* fork child process */
index 14da86247c7087f476bcfa6435c81fa006c24eee..3b4e93a98ce17331d63df108a1948830f9340100 100644 (file)
@@ -99,6 +99,7 @@ enum {
        JAIL_ATTR_LOG,
        JAIL_ATTR_RONLY,
        JAIL_ATTR_MOUNT,
+       JAIL_ATTR_NETNS,
        __JAIL_ATTR_MAX,
 };
 
@@ -111,6 +112,7 @@ static const struct blobmsg_policy jail_attr[__JAIL_ATTR_MAX] = {
        [JAIL_ATTR_LOG] = { "log", BLOBMSG_TYPE_BOOL },
        [JAIL_ATTR_RONLY] = { "ronly", BLOBMSG_TYPE_BOOL },
        [JAIL_ATTR_MOUNT] = { "mount", BLOBMSG_TYPE_TABLE },
+       [JAIL_ATTR_NETNS] = { "netns", BLOBMSG_TYPE_BOOL },
 };
 
 struct instance_netdev {
@@ -250,6 +252,9 @@ jail_run(struct service_instance *in, char **argv)
        if (jail->ronly)
                argv[argc++] = "-o";
 
+       if (jail->netns)
+               argv[argc++] = "-N";
+
        blobmsg_list_for_each(&jail->mount, var) {
                const char *type = blobmsg_data(var->data);
 
@@ -832,6 +837,10 @@ instance_jail_parse(struct service_instance *in, struct blob_attr *attr)
                jail->ronly = blobmsg_get_bool(tb[JAIL_ATTR_RONLY]);
                jail->argc++;
        }
+       if (tb[JAIL_ATTR_NETNS]) {
+               jail->netns = blobmsg_get_bool(tb[JAIL_ATTR_NETNS]);
+               jail->argc++;
+       }
        if (tb[JAIL_ATTR_MOUNT]) {
                struct blob_attr *cur;
                int rem;
@@ -1218,6 +1227,7 @@ void instance_dump(struct blob_buf *b, struct service_instance *in, int verbose)
                blobmsg_add_u8(b, "ubus", in->jail.ubus);
                blobmsg_add_u8(b, "log", in->jail.log);
                blobmsg_add_u8(b, "ronly", in->jail.ronly);
+               blobmsg_add_u8(b, "netns", in->jail.netns);
                blobmsg_close_table(b, r);
                if (!avl_is_empty(&in->jail.mount.avl)) {
                        struct blobmsg_list_node *var;
index 42cc4be1899383ee01a6aa8e470cef1f38c860d5..e53c6069a3ab434915ca9eecb2e2eed7c106749c 100644 (file)
@@ -28,6 +28,7 @@ struct jail {
        bool ubus;
        bool log;
        bool ronly;
+       bool netns;
        char *name;
        char *hostname;
        struct blobmsg_list mount;