2017-02-21 10 views
0

루트는 네트워크 네임 스페이스 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); 
    } 
} 

답변

0

이 커널은 보안 검사를 할 것 네트워크 네임 스페이스 일치의 userns "소유자"/ 제작자 기존 네트워크 네임 스페이스에 조인을 허용하기 전에.

사용자 네임 스페이스 내에 한 번 네트워크 네임 스페이스에 가입 할 수 있지만 초기 네트워크 네임 스페이스는 동일한 사용자 네임 스페이스로 만들어야합니다. runc 도구와 같은 컨테이너 런타임에서는 만든 네임 스페이스가있는 사용자 네임 스페이스에서 간단한 컨테이너를 시작한 다음 첫 번째 컨테이너의 사용자네트워크 네임 스페이스 경로에 대한 참조를 사용하여 두 번째 컨테이너를 시작하여이 동작을 확인할 수 있습니다. 이전 DockerCon에서 runc을 사용하여 데모했습니다. 사용자 네임 스페이스와 네트워크 네임 스페이스를 공유하는 것을 볼 수 있습니다. in this segment starting at 41:24