커널 번역(기사,문서)

[번역] lwn - Namespaces in operation, part 5: User namespaces

iamyooon 2018. 9. 8. 22:30

By Michael Kerrisk

February 27, 2013

원문 : https://lwn.net/Articles/532593/


우리의 네임스페이스 시리즈를 이어서, 이 기사는 유저 네임스페이스를 더 자세히 살펴 본다. 이 capability은 리눅스 3.8에서 (대부분) 구현되었다. (나머지 작업은 XFS와 다른 여러 파일 시스템에 대한 변경으로 이루어져 있으며 후자는 이미 3.9에 병합되었다.) 유저 네임스페이스는 사용자 및 그룹ID의 네임스페이스 매핑을 허용한다. 즉, 유저 네임스페이스 내부의 프로세스 사용자 및 그룹ID는 네임스페이스 외부의 ID와 다를 수 있다. 특히 프로세스는 네임스페이스 외부에서 0이 아닌 유저ID를 가질 수 있으며 동시에 네임스페이스 내부에서 유저ID가 0이다. 즉, 프로세스는 유저 네임스페이스 외부의 작업에 대해 권한이 없지만 네임스페이스 내부에는 루트 권한이 있다.

Continuing our ongoing series on namespaces, this article looks more closely at user namespaces, a feature whose implementation was (largely) completed in Linux 3.8. (The remaining work consists of changes for XFS and a number of other filesystems; the latter has already been merged for 3.9.) User namespaces allow per-namespace mappings of user and group IDs. This means that a process's user and group IDs inside a user namespace can be different from its IDs outside of the namespace. Most notably, a process can have a nonzero user ID outside a namespace while at the same time having a user ID of zero inside the namespace; in other words, the process is unprivileged for operations outside the user namespace but has root privileges inside the namespace.


 


유저 네임스페이스 만들기(Creating user namespaces)

 clone() 또는 unshare()를 호출 할 때 CLONE_NEWUSER 플래그를 지정하여 유저 네임스페이스를 만든다. Linux 3.8부터 (다른 유형의 네임스페이스를 생성하는 데 사용되는 플래그와 달리) 유저 네임스페이스를 생성하는데 필요한 권한은 없다. 아래 예에서 모든 유저 네임스페이스는 권한이 없는 유저ID 1000을 사용하여 생성된다.

User namespaces are created by specifying the CLONE_NEWUSER flag when calling clone() or unshare(). Starting with Linux 3.8 (and unlike the flags used for creating other types of namespaces), no privilege is required to create a user namespace. In our examples below, all of the user namespaces are created using the unprivileged user ID 1000.


 유저 네임스페이스를 조사하기 위해 새로운 유저 네임스페이스에 자식 네임스페이스를 생성하는 작은 프로그램 demo_userns.c를 사용하자. 자식 네임스페이스는 단순히 그것의 effective user와 그룹ID와 capability를 표시한다. 권한이 없는 사용자로 이 프로그램을 실행하면 다음과 같은 결과가 생성된다.

To begin investigating user namespaces, we'll make use of a small program, demo_userns.c, that creates a child in a new user namespace. The child simply displays its effective user and group IDs as well as its capabilities. Running this program as an unprivileged user produces the following result:


     $ id -u          # Display effective user ID of shell process

    1000

    $ id -g          # Effective group ID of shell

    1000

    $ ./demo_userns

    eUID = 65534;  eGID = 65534;  capabilities: =ep


 이 프로그램의 출력은 흥미로운 세부 사항을 보여준다. 그 중 하나가 자식 프로세스에 할당된 capability다. 문자열 "= ep"(capability 집합을 텍스트 표현으로 변환하는 라이브러리 함수 cap_to_text()에 의해 생성 되는)는 권한이 없는 계정에서 프로그램을 실행 했더라도 자식 네임스페이스는 permitted and effective capability를 가진다는 것을 나타낸다. 유저 네임스페이스가 만들어지면 네임스페이스의 첫 번째 프로세스에 네임스페이스의 전체 capablitiy 집합이 부여된다. 이렇게 하면 프로세스가 네임스페이스에서 다른 프로세스를 만들기 전에 네임스페이스에서 필요한 모든 초기화를 수행 할 수 있다.

The output from this program shows some interesting details. One of these is the capabilities that were assigned to the child process. The string "=ep" (produced by the library function cap_to_text(), which converts capability sets to a textual representation) indicates that the child has a full set of permitted and effective capabilities, even though the program was run from an unprivileged account. When a user namespace is created, the first process in the namespace is granted a full set of capabilities in the namespace. This allows that process to perform any initializations that are necessary in the namespace before other processes are created in the namespace.


 두 번째 흥미로운 점은 자식 프로세스의 사용자ID와 그룹ID이다. 위에서 언급했듯이 유저 네임스페이스 내부 및 외부의 프로세스 사용자 및 그룹ID는 다를 수 있다. 그러나 유저 네임스페이스 안의 유저ID에서 네임스페이스 외부의 해당 유저ID 세트로의 매핑이 필요하다. 동일한 ID가 그룹ID에도 적용된다. 이를 통해 시스템은 유저 네임스페이스의 프로세스가 넓은 시스템에 영향을 주는 작업(예 : 네임스페이스 외부의 프로세스에 신호를 보내거나 파일에 액세스하는 등)을 수행 할 때 적절한 권한 검사를 수행 할 수 있다.

The second point of interest is the user and group IDs of the child process. As noted above, a process's user and group IDs inside and outside a user namespace can be different. However, there needs to be a mapping from the user IDs inside a user namespace to a corresponding set of user IDs outside the namespace; the same is true of group IDs. This allows the system to perform the appropriate permission checks when a process in a user namespace performs operations that affect the wider system (e.g., sending a signal to a process outside the namespace or accessing a file).


 프로세스 사용자 및 그룹ID를 반환하는 시스템콜(예 : getuid() 및 getgid())은 시스템콜을 호출한 프로세스가 상주하는 유저 네임스페이스 내부에 나타날 때 항상 credential을 반환한다. 유저ID가 네임스페이스 내부에 매핑이 없는 경우 유저ID를 반환하는 시스템 호출은 /proc/sys/kernel/ overflowuid 파일에 정의 된 값을 반환한다. 이 값은 표준 시스템에서 65534가 기본값이다. 처음에는 유저 네임스페이스에 유저ID 맵핑이 없으므로 네임스페이스 내의 모든 유저ID가 이 값에 맵핑된다. 마찬가지로 새로운 유저 네임스페이스에는 그룹ID에 대한 매핑이 없고 매핑되지 않은 모든 그룹ID는 /proc/sys/kernel/overflowgid (overflowuid와 기본값이 같다)에 매핑된다.

System calls that return process user and group IDs—for example, getuid() and getgid()—always return credentials as they appear inside the user namespace in which the calling process resides. If a user ID has no mapping inside the namespace, then system calls that return user IDs return the value defined in the file /proc/sys/kernel/overflowuid, which on a standard system defaults to the value 65534. Initially, a user namespace has no user ID mapping, so all user IDs inside the namespace map to this value. Likewise, a new user namespace has no mappings for group IDs, and all unmapped group IDs map to /proc/sys/kernel/overflowgid (which has the same default as overflowuid).


 위 출력에서 얻을 수 없는 주목할 만한 또 하나의 중요한 점이 있다. 새로운 프로세스는 새로운 유저 네임스페이스가 full set of capability를 가지고 있지만 부모 네임스페이스에는 capability가 없다. 이는 clone()을 호출하는 프로세스의 credential 및 capability에 관계없이 적용된다. 특히 루트가 clone(CLONE_NEWUSER)을 사용하더라도 결과로 생성 된 자식 프로세스는 상위 네임스페이스에서의 capability는 갖지 않는다.

There is one other important point worth noting that can't be gleaned from the output above. Although the new process has a full set of capabilities in the new user namespace, it has no capabilities in the parent namespace. This is true regardless of the credentials and capabilities of the process that calls clone(). In particular, even if root employs clone(CLONE_NEWUSER), the resulting child process will have no capabilities in the parent namespace.


 유저 네임스페이스의 생성에 관해 마지막 사항은 네임스페이스를 중첩 할 수 있다는 것이다. 즉, 각 유저 네임스페이스(초기 유저 네임스페이스 제외)는 상위 유저 네임스페이스를 가지며 0 개 이상의 자식 유저 네임스페이스를 가질 수 있다. 유저 네임스페이스의 부모는 CLONE_NEWUSER 플래그로 clone() 또는 unshare()를 호출하여 유저 네임스페이스를 만드는 프로세스의 유저 네임스페이스이다. 이 기사의 나머지 부분을 통해 유저 네임스페이스 간의 부모 - 자식 관계의 중요성이 더욱 명확해지게 될 것이다.

One final point to be made about the creation of user namespaces is that namespaces can be nested; that is, each user namespace (other than the initial user namespace) has a parent user namespace, and can have zero or more child user namespaces. The parent of a user namespace is the user namespace of the process that creates the user namespace via a call to clone() or unshare() with the CLONE_NEWUSER flag. The significance of the parent-child relationship between user namespaces will become clearer in the remainder of this article.


 유저ID, 그룹ID 매핑하기(Mapping user and group IDs)

 일반적으로 새 유저 네임스페이스를 만든 후 첫 번째 단계 중 하나는 해당 네임스페이스에 생성 될 프로세스의 사용자 및 그룹ID에 사용되는 매핑을 정의하는 것이다. 이는 유저 네임스페이스의 프로세스 중 하나에 해당하는 /proc/PID/uid_map 및 /proc/PID/gid_map 파일에 매핑 정보를 작성하여 수행된다. (이 두 파일은 처음에는 비어 있음)이 정보는 하나 이상의 행으로 구성되며 각 행에는 공백으로 구분 된 세 개의 값이 들어 있다.

Normally, one of the first steps after creating a new user namespace is to define the mappings used for the user and group IDs of the processes that will be created in that namespace. This is done by writing mapping information to the /proc/PID/uid_map and /proc/PID/gid_map files corresponding to one of the processes in the user namespace. (Initially, these two files are empty.) This information consists of one or more lines, each of which contains three values separated by white space:


ID-inside-ns   ID-outside-ns   length


 ID-inside-ns 및 length 값은 함께 네임스페이스 외부의 동일한 길이의 ID 범위에 매핑 될 네임스페이스 내부의 ID 범위를 정의한다. ID-outside-ns 값은 외부 범위의 시작점을 지정한다. ID-outside-ns가 해석되는 방법은 /proc/PID/uid_map (또는 /proc/PID/gid_map) 파일을 여는 프로세스가 프로세스 PID와 동일한 유저 네임스페이스에 있는지 여부에 따라 달라진다.

Together, the ID-inside-ns and length values define a range of IDs inside the namespace that are to be mapped to an ID range of the same length outside the namespace. The ID-outside-ns value specifies the starting point of the outside range. How ID-outside-ns is interpreted depends on the whether the process opening the file /proc/PID/uid_map (or /proc/PID/gid_map) is in the same user namespace as the process PID:


 두 프로세스가 동일한 네임스페이스에 있으면 ID-outside-ns는 프로세스 PID의 상위 유저 네임스페이스에서 유저ID (그룹ID)로 해석된다. 일반적인 경우는 프로세스가 자체 매핑 파일 (/proc/self/uid_map 또는 /proc/self/gid_map)에 쓰는 경우이다.

If the two processes are in the same namespace, then ID-outside-ns is interpreted as a user ID (group ID) in the parent user namespace of the process PID. The common case here is that a process is writing to its own mapping file (/proc/self/uid_map or /proc/self/gid_map).


 두 프로세스가 다른 네임스페이스에 있으면 ID-outside-ns는 /proc/PID/uid_map (/proc/PID/gid_map)을 여는 프로세스의 유저 네임스페이스에서 유저ID (그룹ID)로 해석된다. writing 프로세스는 자체 유저 네임스페이스와 관련된 매핑을 정의한다.

If the two processes are in different namespaces, then ID-outside-ns is interpreted as a user ID (group ID) in the user namespace of the process opening /proc/PID/uid_map (/proc/PID/gid_map). The writing process is then defining the mapping relative to its own user namespace.


 demo_userns 프로그램을 다시 한번 호출한다고 가정 해보자. 그대신 이번에는 단일 명령 행 인자 (임의의 문자열)를 사용한다고 가정 해 보자. 이로 인해 프로그램이 반복되어 몇 초마다 credential과 capability이 계속 표시된다.

Suppose that we once more invoke our demo_userns program, but this time with a single command-line argument (any string). This causes the program to loop, continuously displaying credentials and capabilities every few seconds:


     $ ./demo_userns x

    eUID = 65534;  eGID = 65534;  capabilities: =ep

    eUID = 65534;  eGID = 65534;  capabilities: =ep


 이제 다른 네임스페이스 (즉, demo_userns를 실행하는 프로세스의 부모 유저 네임스페이스)에서 실행중인 쉘 프로세스로 전환하고 demo_userns가 작성한 새 유저 네임스페이스에 있는 자식 프로세스에 대한 유저ID 매핑을 만든다.

Now we switch to another terminal window—to a shell process running in another namespace (namely, the parent user namespace of the process running demo_userns) and create a user ID mapping for the child process in the new user namespace created by demo_userns:


     $ ps -C demo_userns -o 'pid uid comm'      # Determine PID of clone child

      PID   UID COMMAND

     4712  1000 demo_userns                    # This is the parent

     4713  1000 demo_userns                    # Child in a new user namespace

    $ echo '0 1000 1' > /proc/4713/uid_map


demo_userns에서 리턴하면 아래를 볼 수 있다:

If we return to the window running demo_userns, we now see:


     eUID = 0;  eGID = 65534;  capabilities: =ep


즉, 이전에 65534에 매핑 된 상위 유저 네임스페이스의 유저ID 1000이 demo_userns에 의해 생성 된 유저 네임스페이스의 유저ID 0에 매핑되었다. 이 시점부터 이 유저ID를 처리하는 새 유저 네임스페이스 내의 모든 작업에는 숫자 0이 표시되고 상위 유저 네임스페이스에서의 해당 작업에는 유저ID 1000과 동일한 프로세스가 표시된다.

In other words, the user ID 1000 in the parent user namespace (which was formerly mapped to 65534) has been mapped to user ID 0 in the user namespace created by demo_userns. From this point, all operations within the new user namespace that deal with this user ID will see the number 0, while corresponding operations in the parent user namespace will see the same process as having user ID 1000.


 마찬가지로 새 유저 네임스페이스에서 그룹ID에 대한 매핑을 만들 수 있다. 다른 터미널 창으로 전환하면 부모 유저 네임스페이스의 단일 그룹ID 1000에 대한 매핑이 새 유저 네임스페이스의 그룹ID 0에 생성된다.

We can likewise create a mapping for group IDs in the new user namespace. Switching to another terminal window, we create a mapping for the single group ID 1000 in the parent user namespace to the group ID 0 in the new user namespace:


     $ echo '0 1000 1' > /proc/4713/gid_map


 demo_userns를 실행하는 창으로 다시 전환하면 유효 그룹ID의 표시에 변경 사항이 반영된 것을 볼 수 있다.

Switching back to the window running demo_userns, we see that change reflected in the display of the effective group ID:


     eUID = 0;  eGID = 0;  capabilities: =ep


 매핑파일에 쓰는 룰(Rules for writing to mapping files)

 uid_map 파일에 쓰기를 관리하는 규칙은 여러 가지가 있다. 유사한 규칙이 gid_map 파일에 기록하는 데 적용된다. 이 규칙 중 가장 중요한 것은 다음과 같다.

There are a number of rules governing writing to uid_map files; analogous rules apply for writing to gid_map files. The most important of these rules are as follows.


 매핑 정의는 네임스페이스 당 한 번만 수행된다. 즉, 유저 네임스페이스의 프로세스 중 하나 인 uid_map 파일에 단일 줄 바꿈 (여러 줄 바꿈으로 구분 된 레코드가 포함될 수 있음)을 수행 할 수 있다. 또한 파일에 기록 할 수 있는 행 수는 현재 5 개로 제한되어 있다 (미래에 증가 할 수 있는 임의의 한도).

Defining a mapping is a one-time operation per namespace: we can perform only a single write (that may contain multiple newline-delimited records) to a uid_map file of exactly one of the processes in the user namespace. Furthermore, the number of lines that may be written to the file is currently limited to five (an arbitrary limit that may be increased in the future).


 /proc/PID/uid_map 파일은 네임스페이스를 생성 한 유저ID가 소유하며 해당 사용자 (또는 권한있는 사용자) 만 쓸 수 있다. 또한 다음 요구 사항을 모두 충족해야 한다.

The /proc/PID/uid_map file is owned by the user ID that created the namespace, and is writeable only by that user (or a privileged user). In addition, all of the following requirements must be met:


 쓰기 프로세스는 프로세스 PID의 유저 네임스페이스에서 CAP_SETUID(gid_map의 CAP_SETGID) capability을 가져야한다.

The writing process must have the CAP_SETUID (CAP_SETGID for gid_map) capability in the user namespace of the process PID.

 capability에 관계없이 쓰기 프로세스는 프로세스 PID의 유저 네임스페이스 또는 프로세스 PID의 (직접적인) 상위 유저 네임스페이스 내에 있어야 한다.

Regardless of capabilities, the writing process must be in either the user namespace of the process PID or inside the (immediate) parent user namespace of the process PID.


아래 항목중 하나는 반드시 참이다:

One of the following must be true:


 uid_map (gid_map)에 기록 된 데이터는 상위 유저 네임스페이스의 쓰기 프로세스의 유효 유저ID (그룹ID)를 유저 네임스페이스의 유저ID (그룹ID)에 매핑하는 단일 행으로 구성된다. 이 규칙은 유저 네임스페이스 (즉, clone()에 의해 생성 된 자식)의 초기 프로세스가 자신의 유저ID (그룹ID)에 대한 매핑을 작성할 수 있게 한다.

The data written to uid_map (gid_map) consists of a single line that maps (only) the writing process's effective user ID (group ID) in the parent user namespace to a user ID (group ID) in the user namespace. This rule allows the initial process in a user namespace (i.e., the child created by clone()) to write a mapping for its own user ID (group ID).


프로세스는 상위 유저 네임스페이스에서 CAP_SETUID(gid_map의 CAP_SETGID) capability을 갖는다. 이러한 프로세스는 상위 유저 네임스페이스의 임의 유저ID (그룹ID)에 대한 매핑을 정의 할 수 있다. 이전에 언급했듯이 새로운 유저 네임스페이스의 초기 프로세스는 부모 네임스페이스에서의 capability가 없다. 따라서 상위 네임스페이스의 프로세스만 상위 유저 네임스페이스의 임의 ID를 매핑하는 매핑을 작성할 수 있다.

The process has the CAP_SETUID (CAP_SETGID for gid_map) capability in the parent user namespace. Such a process can define mappings to arbitrary user IDs (group IDs) in the parent user namespace. As we noted earlier, the initial process in a new user namespace has no capabilities in the parent namespace. Thus, only a process in the parent namespace can write a mapping that maps arbitrary IDs in the parent user namespace.

 

Capabilities, execve(), and user ID 0

 이 시리즈의 이전 기사에서 우리는 ns_child_exec 프로그램을 개발했다. 이 프로그램은 clone()을 사용하여 명령 줄 옵션으로 지정된 새 네임스페이스의 자식 프로세스를 만든 다음 자식 프로세스에서 셸 명령을 실행한다.

In an earlier article in this series, we developed the ns_child_exec program. This program uses clone() to create a child process in new namespaces specified by command-line options and then executes a shell command in the child process.


 이 프로그램을 사용하여 새 유저 네임스페이스에서 셸을 실행 한 다음 해당 셸 내에서 새 유저 네임스페이스에 대한 유저ID 매핑을 정의하려고 한다고 가정한다. 이렇게 하면 문제가 발생한다.

Suppose that we use this program to execute a shell in a new user namespace and then within that shell we try to define the user ID mapping for the new user namespace. In doing so, we run into a problem:


    $ ./ns_child_exec -U  bash

    $ echo '0 1000 1' > /proc/$$/uid_map       # $$ is the PID of the shell

    bash: echo: write error: Operation not permitted


 이 오류는 다음 명령에서 볼 수 있듯이 셸이 새 유저 네임스페이스 내에 capability을 가지고 있지 않기 때문에 발생한다.

This error occurs because the shell has no capabilities inside the new user namespace, as can be seen from the following commands:


    $ id -u         # Verify that user ID and group ID are not mapped

    65534

    $ id -g

    65534

    $ cat /proc/$$/status | egrep 'Cap(Inh|Prm|Eff)'

    CapInh: 0000000000000000

    CapPrm: 0000000000000000

    CapEff: 0000000000000000


 문제점은 bash 쉘을 실행 한 execve() 호출에서 발생했다. 유저ID가 0이 아닌 프로세스가 execve()를 수행하면 프로세스의 capability 세트가 지워진다. (capabilities (7) 매뉴얼 페이지는 execve() 중 capability 처리에 대해 자세히 설명한다.)

The problem occurred at the execve() call that executed the bash shell: when a process with non-zero user IDs performs an execve(), the process's capability sets are cleared. (The capabilities(7) manual page details the treatment of capabilities during an execve().)


이 문제점을 방지하려면 execve()를 수행하기 전에 유저 네임스페이스 내에 유저ID 맵핑을 작성해야 한다. 이것은 ns_child_exec 프로그램에서는 불가능하다. 이를 허용하는 약간 향상된 버전의 프로그램이 필요하다.

To avoid this problem, it is necessary to create a user ID mapping inside the user namespace before performing the execve(). This is not possible with the ns_child_exec program; we need a slightly enhanced version of the program that does allow this.


userns_child_exec.c 프로그램은 ns_child_exec 프로그램과 동일한 작업을 수행하며 두 개의 추가 명령 줄 옵션 인 -M 및 -G를 허용한다는 점을 제외하면 동일한 명령 줄 인터페이스를 사용한다. 이 옵션은 새 유저 네임스페이스에 대한 사용자 및 그룹ID 맵을 정의하는 데 사용되는 문자열 인수를 허용한다. 예를 들어, 다음 명령은 새 유저 네임스페이스에서 유저ID 1000 및 그룹ID 1000을 0으로 매핑한다.

The userns_child_exec.c program performs the same task as the ns_child_exec program, and has the same command-line interface, except that it allows two additional command-line options, -M and -G. These options accept string arguments that are used to define user and group ID maps for the new user namespace. For example, the following command maps both user ID 1000 and group ID 1000 to 0 in the new user namespace:


     $ ./userns_child_exec -U -M '0 1000 1' -G '0 1000 1' bash


 이번에는 매핑 파일 업데이트가 성공하고 쉘에 예상되는 유저ID, 그룹ID 및 capability이 있음을 알 수 있다.

This time, updating the mapping files succeeds, and we see that the shell has the expected user ID, group ID, and capabilities:


    $ id -u

    0

    $ id -g

    0

    $ cat /proc/$$/status | egrep 'Cap(Inh|Prm|Eff)'

    CapInh: 0000000000000000

    CapPrm: 0000001fffffffff

    CapEff: 0000001fffffffff


 userns_child_exec 프로그램 구현에는 약간의 미묘한 차이가 있다. 먼저, 부모 프로세스 (즉, clone()의 호출자) 또는 새로운 자식 프로세스가 새로운 유저 네임스페이스의 유저ID 및 그룹ID 맵을 업데이트 할 수 있다. 그러나 위의 규칙을 따르면, 자식 프로세스가 정의 할 수 있는 유일한 맵핑 유형은 고유 한 유효 유저ID만을 맵핑하는 것이다. 자식 프로세스에서 임의의 사용자 및 그룹ID 매핑을 정의하려는 경우 부모 프로세스에서 이 매핑을 수행해야 한다. 또한 부모 프로세스에는 적절한 capability, 즉 CAP_SETUID, CAP_SETGID 및 (부모가 매핑 파일을 여는 데 필요한 권한을 가지도록) CAP_DAC_OVERRIDE가 있어야한다.

There are some subtleties to the implementation of the userns_child_exec program. First, either the parent process (i.e., the caller of clone()) or the new child process could update the user ID and group ID maps of the new user namespace. However, following the rules above, the only kind of mapping that the child process could define would be one that maps just its own effective user ID. If we want to define arbitrary user and group ID mappings in the child, then that must be done by the parent process. Furthermore, the parent process must have suitable capabilities, namely CAP_SETUID, CAP_SETGID, and (to ensure that the parent has the permissions needed to open the mapping files) CAP_DAC_OVERRIDE.


 게다가 부모는 execve()를 호출하기 전에 매핑 파일을 업데이트해야 한다 (그렇지 않으면 execve() 중에 자식이 capability을 상실 할 수 있는 위에서 설명한 문제가 발생한다). 이를 위해 두 프로세스는 파이프를 사용하여 필요한 동기화를 보장한다. 프로그램 소스 코드의 주석은 자세한 내용을 제공한다.

Furthermore, the parent must ensure that it updates the mapping files before the child calls execve() (otherwise we have exactly the problem described above, where the child will lose capabilities during the execve()). To do this, the two processes employ a pipe to ensure the required synchronization; comments in the program source code give full details.

유저ID와 그룹ID 매핑 보기(Viewing user and group ID mappings)

지금까지의 예제는 매핑을 정의하기 위해 /proc/PID/uid_map과 /proc/PID/gid_map 파일을 사용하는 방법을 보여주었습니다. 이 파일은 프로세스를 관리하는 맵핑을 보는 데 사용될 수도 있다. 이러한 파일에 쓸 때와 마찬가지로 두 번째 (ID-outside-ns) 값은 파일을 여는 프로세스에 따라 해석된다. 파일을 여는 프로세스가 프로세스 PID와 동일한 유저 네임스페이스에 있으면 ID-outside-ns가 상위 유저 네임스페이스와 관련하여 정의된다. 파일을 여는 프로세스가 다른 유저 네임스페이스에 있는 경우 ID-outside-ns는 파일을 여는 프로세스의 유저 네임스페이스와 respect되어 정의된다.

The examples so far showed the use of /proc/PID/uid_map and /proc/PID/gid_map files for defining a mapping. These files can also be used to view the mappings governing a process. As when writing to these files, the second (ID-outside-ns) value is interpreted according to which process is opening the file. If the process opening the file is in the same user namespace as the process PID, then ID-outside-ns is defined with respect to the parent user namespace. If the process opening the file is in a different user namespace, then ID-outside-ns is defined with respect to the user namespace of the process opening the file.


셸을 실행하는 몇 가지 유저 네임스페이스를 만들고 네임스페이스에 있는 프로세스의 uid_map 파일을 검사하여 이를 설명 할 수 있다. 쉘을 실행하는 프로세스로 새로운 유저 네임스페이스를 만드는 것으로 시작한다.

We can illustrate this by creating a couple of user namespaces running shells, and examining the uid_map files of the processes in the namespaces. We begin by creating a new user namespace with a process running a shell:


    $ id -u            # Display effective user ID

    1000

    $ ./userns_child_exec -U -M '0 1000 1' -G '0 1000 1' bash

    $ echo $$          # Show shell's PID for later reference

    2465

    $ cat /proc/2465/uid_map

             0       1000          1

    $ id -u            # Mapping gives this process an effective user ID of 0

    0


 이제 다른 터미널 창으로 전환하여 다른 사용자 및 그룹ID 매핑을 사용하는 형제 유저 네임스페이스를 만드는 것으로 가정한다.

Now suppose we switch to another terminal window and create a sibling user namespace that employs different user and group ID mappings:


    $ ./userns_child_exec -U -M '200 1000 1' -G '200 1000 1' bash

    $ cat /proc/self/uid_map

           200       1000          1

    $ id -u            # Mapping gives this process an effective user ID of 200

    200

    $ echo $$          # Show shell's PID for later reference

    2535


두 번째 유저 네임스페이스에서 실행중인 두 번째 터미널 창에서 계속해서 다른 유저 네임스페이스에 있는 프로세스의 유저ID 매핑을 본다.

Continuing in the second terminal window, which is running in the second user namespace, we view the user ID mapping of the process in the other user namespace:


     $ cat /proc/2465/uid_map

             0        200          1


이 명령의 출력은 다른 유저 네임스페이스의 유저ID 0이 이 네임스페이스의 유저ID 200에 매핑됨을 나타낸다. 커널이 파일에서 읽는 프로세스의 유저 네임스페이스에 따라 ID-outside-ns 값을 생성하기 때문에 다른 유저 네임스페이스에서 실행될 때 동일한 명령이 다른 출력을 생성했음을 유의하자. 

The output of this command shows that user ID 0 in the other user namespace maps to user ID 200 in this namespace. Note that the same command produced different output when executed in the other user namespace, because the kernel generates the ID-outside-ns value according to the user namespace of the process that is reading from the file.


 첫 번째 터미널 창으로 다시 전환하여 두 번째 유저 네임스페이스에 프로세스의 유저ID 매핑 파일을 표시하면 역 매핑이 표시된다.

If we switch back to the first terminal window, and display the user ID mapping file for the process in the second user namespace, we see the converse mapping:


     $ cat /proc/2535/uid_map

           200          0          1


 다시 말하지만 ID-outside-ns 값은 파일에서 읽는 프로세스의 유저 네임스페이스에 따라 생성되므로 두 번째 유저 네임스페이스에서 실행될 때 동일한 출력과 다르다. 물론 초기 네임스페이스에서 첫 번째 네임스페이스의 유저ID 0과 두 번째 네임스페이스의 유저ID 200은 모두 유저ID 1000으로 매핑된다. 초기 유저 네임스페이스의 세 번째 셸 창에서 다음 명령을 실행하여 이를 확인할 수 있다.

Again, the output here is different from the same command when executed in the second user namespace, because the ID-outside-ns value is generated according to the user namespace of the process that is reading from the file. Of course, in the initial namespace, user ID 0 in the first namespace and user ID 200 in the second namespace both map to user ID 1000. We can verify this by executing the following commands in a third shell window inside the initial user namespace:


    $ cat /proc/2465/uid_map

             0       1000          1

    $ cat /proc/2535/uid_map

           200       1000          1


Concluding remarks

 이 기사에서는 유저 네임스페이스의 기본 사항 인 유저 네임스페이스 만들기, 사용자 및 그룹ID 맵 파일 사용, 유저 네임스페이스와 capability의 상호 작용을 살펴 보았다.

In this article, we've looked at the basics of user namespaces: creating a user namespace, using user and group ID map files, and the interaction of user namespaces and capabilities.


 이전 기사에서 언급했듯이 유저 네임스페이스를 구현하려는 동기 중 하나는 이전에 루트 사용자로 제한되었던 capability에 비 루트 응용 프로그램에 대한 액세스 권한을 부여하는 것이다. 전통적인 UNIX 시스템에서는 권한이 없는 사용자가 권한이 있는 프로그램의 런타임 환경을 조작하지 못하도록 예기치 않은 또는 바람직하지 않은 방식으로 프로그램의 작동에 영향을 미칠 수 있는 다양한 capability이 루트 사용자로 제한되었다.

As we noted in an earlier article, one of the motivations for implementing user namespaces is to give non-root applications access to functionality that was formerly limited to the root user. In traditional UNIX systems, various pieces of functionality have been limited to the root user in order to prevent unprivileged users from manipulating the runtime environment of privileged programs, which could affect the operation of those programs in unexpected or undesirable ways.


 유저 네임스페이스는 네임스페이스 외부의 권한이 없는 프로세스가 루트 권한을 가질 수 있게 하는 동시에 네임스페이스로 해당 권한의 범위를 제한함으로써 프로세스가 보다 넓은 권한의 프로그램의 런타임 환경을 조작 할 수 없도록 한다. 이러한 루트 권한을 의미 있게 사용하려면 유저 네임스페이스를 다른 유형의 네임스페이스와 결합해야 한다.이 주제는 이 시리즈의 다음 기사의 주제가 된다.

A user namespace allows a process (that is unprivileged outside the namespace) to have root privileges while at the same time limiting the scope of that privilege to the namespace, with the result that the process cannot manipulate the runtime environment of privileged programs in the wider system. In order to use these root privileges meaningfully, we need to combine user namespaces with other types of namespaces—that topic will form the subject of the next article in this series.