3. User Namespace

먼저 이 글에서 사용한 코드는 linux kernel 4.16 임을 알려드립니다.

User namespace는 시큐리티와 관련된 식별자 및 속성을 분리하며, 특히 User ID와 Group ID, 루트 디렉토리, Key, Capability를 분리합니다. 프로세스의 User, Group ID는 user namespace 내,외부적으로 다를수 있습니다. 특히 프로세스는 User namespace 외부에 권한이 없는 정상적인 User ID를 가질 수 있으며, 동시에 namepsace 내부에 User ID 0을 가질 수 있습니다. 즉, 프로세스에는 user namespace 내의 작업에 대한 전체 권한이 있지만 namespace 외부 작업에 대한 권한이 없습니다.

Nested namespaces, Namespace membership

User namespace는 PID namespace 처럼 중첩되어 질 수 있습니다. 이 말은 root namespace를 제외하고 각 User namespace는 부모 user namespace를 가질 수 있다는 것입니다. 다른 관점에서 본다면 User namespace는 0개 또는 그 이상의 자식 User namespace를 가질 수 있습니다. 부모 User namespace는 CLONE_NEWUSER flag를 사용한 unshare(2) 또는 clone(2) 시스템콜을 통해 user namespace를 생성하는 프로세스의 namespace입니다. 음, 새로 생성된 namespace는 그 namespace를 생성하는 프로세스의 namespace를 부모 namespace로 가진다는 의미입니다.

커널은 이렇게 중첩할 수 있는 user namespace의 레벨을 32개로 제한하고 있습니다.

아래 struct user_namespace를 살펴보시죠. 아래 line.61에 int level; 구조체 멤버변수가 보이시나요? user namespace 는 이처럼 레벨을 관리하고 있습니다.

 File path : include/linux/user_namespace.h

 55 struct user_namespace {
 56     struct uid_gid_map  uid_map;
 57     struct uid_gid_map  gid_map;
 58     struct uid_gid_map  projid_map;
 59     atomic_t        count;
 60     struct user_namespace   *parent;
 61     int         level;
 62     kuid_t          owner;
 63     kgid_t          group;
 64     struct ns_common    ns;
 65     unsigned long       flags;
 66
 67     /* Register of per-UID persistent keyrings for this namespace */
 68 #ifdef CONFIG_PERSISTENT_KEYRINGS
 69     struct key      *persistent_keyring_register;
 70     struct rw_semaphore persistent_keyring_register_sem;
 71 #endif
 72     struct work_struct  work;
 73 #ifdef CONFIG_SYSCTL
 74     struct ctl_table_set    set;
 75     struct ctl_table_header *sysctls;
 76 #endif
 77     struct ucounts      *ucounts;
 78     int ucount_max[UCOUNT_COUNTS];
 79 } __randomize_layout;

만약 limit을 초과하게 되면 EUSERS 에러가 발생하게 됩니다.

모든 프로세스들은 User namespace 중 하나에 속합니다. 여러분들이 프로세스 생성에 많이 사용하는 fork(2), **clone(2)**을 사용할 때 flag로 CLONE_NEWUSER를 전달하지 않는다면 해당 시스템 콜을 호출한 프로세스의 User namespace에 속하게 됩니다. 싱글스레드 프로세스는 setns(2) 시스템 콜을 사용하여 다른 user namespace로 포함될 수 있습니다. 조건은 setns(2) 시스템 콜을 호출하는 프로세스가 CAP_SYS_ADMIN Capability를 가지고 있어야 합니다.

여기서 주의할 점은 멀티스레드 프로세스에서는 setns(2) 시스템 콜을 호출한 스레드만 namespace가 변경되어 버립니다. 그럼, 이상한 문제점들이 발생하게 될겁니다.

하나의 스레드만 적용되는 이유는 아래 코드에서 확인할 수 있습니다. setns(2) 시스템 콜을 호출하게 되면 아래 함수가 수행됩니다. 여기서 line.268을 확인해보시면 현재 task_struct를 가져오게되고, line.283에서 task_struct를 가져와 namespace를 생성하게 되는데, 스레드는 각각의 task_struct를 가지고 있기에, 해당 thread에 대해서만 변경이 됩니다.

  File path : kernel/nsproxy.c
  266 SYSCALL_DEFINE2(setns, int, fd, int, nstype)
  267 {
  268     struct task_struct *tsk = current;
  269     struct nsproxy *new_nsproxy;
  270     struct file *file;
  271     struct ns_common *ns;
  272     int err;
  273
  274     file = proc_ns_fget(fd);
  275     if (IS_ERR(file))
  276         return PTR_ERR(file);
  277
  278     err = -EINVAL;
  279     ns = get_proc_ns(file_inode(file));
  280     if (nstype && (ns->ops->type != nstype))
  281         goto out;
  282
  283     new_nsproxy = create_new_namespaces(0, tsk, current_user_ns(), tsk->fs);
  284     if (IS_ERR(new_nsproxy)) {
  285         err = PTR_ERR(new_nsproxy);
  286         goto out;
  287     }
  288
  289     err = ns->ops->install(new_nsproxy, ns);
  290     if (err) {
  291         free_nsproxy(new_nsproxy);
  292         goto out;
  293     }
  294     switch_task_namespaces(tsk, new_nsproxy);
  295
  296     perf_event_namespaces(tsk);
  297 out:
  298     fput(file);
  299     return err;
  300 }

Capability

CLONE_NEWUSER flag를 이용하여 clone(2) 시스템 콜로 생성된 자식 프로세스는 새로운 User namespace에서 완전한 Capability 집합을 가지고 실행됩니다. 마찬가지로 unshare(2), setns(2) 시스템 콜도 마찬가지로 Namepsace 내부에서 Capability의 전체 집합을 가지게 됩니다.

반면에, 새로운 네임 스페이스가 생성 되더라도 그 프로세스는 부모 네임 스페이스 (클론 (2)의 경우) 또는 이전 (unshare(2) 및 **setns(2)**의 경우) User namepsace의 CCapability를 갖지 않습니다 또는 루트 사용자 (즉, Root User namepsace에서 사용자 ID가 0 인 프로세스)에 의해 조인됩니다.

다른 경우로서, **execve(2)**를 호출하면 프로세스의 기능이 일반적인 방법으로 재계산됩니다 이 방식은 이 글의 범위를 벗어나기 때문에 다루지 않겠습니다. 관심있으신 분은 capabilites(7) 을 참조해주세요결과적으로 Namespace 내에 프로세스의 사용자 ID가 0이 아니거나 실행 가능 파일에 비어 있지 않은 상속 기능 마스크가 있으면 프로세스가 모든 기능을 잃게됩니다. 음, 자세한 내용은 아래에서 다시 다루겠습니다.

clone(2) 또는 unshared(2) 를 이용해 새로운 IPC, mount, network, PID, UTS namespace를 생성할 때 커널은 새로운 Namespace에 대해 생성한 프로세스의 User namespace를 기록합니다. 새로운 Namespace의 프로세스가 나중에 Namespace 내에 격리된 전역 리소스에서 작동하는 권한 작업을 수행하면 커널이 새 Namespace와 연결된 User namespace의 프로세스 Capability에 따라 검사가 수행됩니다. 즉 Namepsace의 Capability는 User namespace와 상호작용하며 체크하게 된다는 것입니다.

Restrictions on mount namespaces

mount namespace 관련하여 정리한 내용입니다.

mount namespace는 owner user namespace를 가지고 있습니다. owner user namespace가 상위 mount namespace의 owner user namespace와 다른 mount namespace는 권한이 낮은 mount namespace로 간주됩니다. 낮은 권한의 mount namespace가 생성될 때 공유 마운트는 슬레이브 마운트로 축소됩니다. 이렇게하면 권한이 낮은 mount namespace에서 수행 된 매핑이보다 많은 권한을 가진 mount namespace로 전파되지 않습니다.

더 많은 권한을 가진 마운트에서 하나의 단위로 나오는 마운트는 함께 잠기고 특권이 적은 mount namespace에서 분리되지 않을 수 있습니다.

파일 및 디렉토리에 대해서는 다른 namespace 마운트 지점이 아닌 하나의 namespace의 마운트 지점인 파일 또는 디렉터리는 마운트 지점이 아닌 mount namespace에서 이름을 변경하거나 연결 해제하거나 제거(rmdir(2))할 수 있다. 다른 mount namespace에서 마운트 포인트의 파일, 디렉토리를 삭제, rename, unlink를 시도하게 되면 EBUSY 에러가 나타납니다. 이런 결과는 특권이 많은 사용자로부터 DoS 공격을 막기 위한 방안입니다.

Interaction of user namespaces and other types of namespaces

User 네임스페이스는 다른 네임스페이스들과 연관관계를 맺고있습니다.

리눅스 커널 3.8부터 권한이 없는(unprivileged) 프로세스들도 User 네임스페이스를 생성할 수 있습니다. 프로세스가 CAP_SYS_ADMIN Capability 를 가지고 있다면 Mount, PID, IPC, Network, UTS 네임스페이스도 생성할 수 있습니다.

Non-user 네임스페이스(User 네임스페이스를 제외한 다른 네임스페이스)가 생성될 때 새로운 프로세스는 자신을 생성한 프로세스가 속한 User 네임스페이스에 속하게 됩니다. Non-user 네임스페이스은 User 네임스페이스의 기능이 필요합니다. 만약 ** clone(2) ** 또는 ** unshare(2) ** 호출 시 ** CLONE_NEWUSER ** 가 다른 ** CLONE_NEW* ** 플래그와 함께 명시된다면 User 네임페이스를 먼저 생성하게 됩니다. 그 이후 권한을 확인하여 나머지 네임스페이스의 생성을 하게 됩니다. 따라서 권한이 없는 호출 프로세스는 이와 같은 플래그의 조합으로 시스템 콜을 호출할 수 있습니다.

새로운 IPC, mount, network, PID, UTS 네임스페이스가 ** clone(2) ** 또는 **unshare(2) ** 시스템 콜을 통해 생성되면 커널은 새로운 네임스페이스에 대해 생성되는 프로세스의 사용자 네임스페이스를 기록합니다. 새로운 네임스페이스의 프로세스가 네임스페이스 격리된 전역 리소스에서 동작하는 권한이 필요한 작업을 연속적으로 수행하면 커널이 새로운 네임스페이스와 연결된 User 네임스페이스의 새로운 프로세스 기능에 따라 권한을 검사하게 됩니다.

User 네임스페이스는 다른 네임스페이스들과 연관관계를 맺고 있는 부분을 확인 할 수 있으며, 특권(Capability)와 권한(Privilege)에 대한 관계도 관련이 있음을 알 수 있습니다.

User and Group ID mapplings : uid_map and gid_map

User 네임스페이스가 생성되면 상위 User 네임스페이스에 User, Group ID가 매핑되지 않고 시작합니다. ** /proc/[pid]/uid_map ** 과 ** /proc/[pid]/gid_map ** 파일은 해당 프로세스의 User 네임스페이스 내부의 사용자와 그룹 ID 매핑 정보를 보여줍니다. 이 정보는 Namespace(1) 글에서 자료구조 내부적으로 어떻게 관리하는지 본 적이 있습니다.


 55 struct user_namespace {
 56     struct uid_gid_map  uid_map;
 57     struct uid_gid_map  gid_map;
 58     struct uid_gid_map  projid_map;
 59     atomic_t        count;
 60     struct user_namespace   *parent;
 61     int         level;
 62     kuid_t          owner;
 63     kgid_t          group;
 64     struct ns_common    ns;
 65     unsigned long       flags;
 66
 67     /* Register of per-UID persistent keyrings for this namespace */
 68 #ifdef CONFIG_PERSISTENT_KEYRINGS
 69     struct key      *persistent_keyring_register;
 70     struct rw_semaphore persistent_keyring_register_sem;
 71 #endif
 72     struct work_struct  work;
 73 #ifdef CONFIG_SYSCTL
 74     struct ctl_table_set    set;
 75     struct ctl_table_header *sysctls;
 76 #endif
 77     struct ucounts      *ucounts;
 78     int ucount_max[UCOUNT_COUNTS];
 79 } __randomize_layout;

위 구조체에서 uid_map, gid_map이 구조체의 멤버로 존재하며, user_namespace 구조체 내부에서 관리하고 있음을 알 수 있습니다.