【问题标题】:Join network namespace from inside user namespace从用户命名空间内部加入网络命名空间
【发布时间】:2017-02-21 20:32:57
【问题描述】:

Root 创建一个网络命名空间testns,并使用CLONE_NEWUSER 标志进行克隆,以使子节点进入用户命名空间。然后孩子尝试通过调用setns 加入testns,显示权限被拒绝。 是否可以让用户命名空间内的孩子加入网络命名空间?

刚刚编写了以下测试:(在运行之前,我通过调用sudo ip netns add testns 创建了testns

#define _GNU_SOURCE
#include <sched.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>

#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];

static void update_map(char *mapping, char *map_file) {
    int fd, j;
    size_t map_len;

    map_len = strlen(mapping);
    for (j = 0; j < map_len; j++)
        if (mapping[j] == ',')
            mapping[j] = '\n';

    fd = open(map_file, O_RDWR);
    if (fd == -1) {
        fprintf(stderr, "open %s: %s\n", map_file, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (write(fd, mapping, map_len) != map_len) {
        fprintf(stderr, "write %s: %s\n", map_file, strerror(errno));
        exit(EXIT_FAILURE);
    }

    close(fd);
}

static void proc_setgroups_write(pid_t child_pid, char *str) {
    char setgroups_path[PATH_MAX];
    int fd;

    snprintf(setgroups_path, PATH_MAX, "/proc/%ld/setgroups",
            (long) child_pid);

    fd = open(setgroups_path, O_RDWR);
    if (fd == -1) {
        if (errno != ENOENT)
            fprintf(stderr, "ERROR: open %s: %s\n", setgroups_path,
                strerror(errno));
        return;
    }

    if (write(fd, str, strlen(str)) == -1)
        fprintf(stderr, "ERROR: write %s: %s\n", setgroups_path,
            strerror(errno));

    close(fd);
}

static void update_userns(pid_t pid, char *uidMap, char *gidMap) {
    char map_path[PATH_MAX];

    snprintf(map_path, PATH_MAX, "/proc/%ld/uid_map", (long) pid);
    update_map(uidMap, map_path);

    proc_setgroups_write(pid, "deny");
    snprintf(map_path, PATH_MAX, "/proc/%ld/gid_map", (long) pid);
    update_map(gidMap, map_path);
}

static int join_netns(char *netns_name) {
    char netns_path[256];
    snprintf(netns_path, sizeof(netns_path), "/var/run/netns/%s", netns_name);
    int fd = open(netns_path, O_RDONLY);
    if (fd == -1) {
        fprintf(stderr, "open netns path failed: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (setns(fd, CLONE_NEWNET) == -1) {
        fprintf(stderr, "set netns failed: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    return 0;
}

static int child(void* arg) {
    // Sleep so that userns has the correct mapping.
    sleep(1);

    return join_netns("testns");
}

int main(int argc, char *argv[]) {
    uid_t uid = getuid();
    char mapping[10];
    snprintf(mapping, 10, "0 %d 1", uid);

    int pid1 = clone(child, child_stack + STACK_SIZE, CLONE_NEWUSER | SIGCHLD, NULL);
    if (pid1 == -1) {
        perror("Clone");
        exit(EXIT_FAILURE);
    }
    update_userns(pid1, mapping, mapping);

    if (waitpid(pid1, NULL, 0) == -1) {
        perror("waitpid"); 
        exit(EXIT_FAILURE);
    }
}

【问题讨论】:

    标签: linux linux-namespaces


    【解决方案1】:

    在允许加入现有的网络命名空间之前,内核将执行网络命名空间的用户“所有者”/创建者匹配的安全检查。

    您绝对可以在用户命名空间内加入网络命名空间,但必须使用相同的用户命名空间创建初始网络命名空间。在像 runc 工具这样的容器运行时中,您可以通过在创建了网络命名空间的用户命名空间中启动一个简单的容器来验证这一点,然后使用第一个容器的 usernetwork 命名空间路径。我在之前的 DockerCon 上使用runc 演示了这个;你可以看到我共享用户命名空间和网络命名空间in this segment starting at 41:24

    【讨论】:

      猜你喜欢
      • 2015-01-08
      • 1970-01-01
      • 2019-01-23
      • 2016-07-26
      • 1970-01-01
      • 1970-01-01
      • 2012-03-13
      • 2016-05-03
      • 1970-01-01
      相关资源
      最近更新 更多