ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Linux] unshare 과 PID Namespace 격리 알아보기
    System 2024. 6. 19. 07:03
    반응형

    - 목차

     

    들어가며.

    이번 글에서는 Docker 와 같은 Container 가상화 기술의 근간이 되는 PID Namespace 에 대해서 알아봅니다.

    PID Namespace 격리는 unshare 명령어를 통해서 구현할 수 있습니다.

    기본적으로 Root Level 의 PID Namespace 가 존재합니다.

     

    아래와 같이 PID 가 1번인 Root Process 를 포함하여 9 ~ 15번에 해당하는 여러 Process 가 생성될 수 있습니다.

    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 23:58 pts/0    00:00:00 bash
    root         9     1  0 23:58 pts/0    00:00:00 sleep 1000
    root        10     1  0 23:58 pts/0    00:00:00 sleep 1000
    root        11     1  0 23:59 pts/0    00:00:00 sleep 1000
    root        12     1  0 23:59 pts/0    00:00:00 sleep 1000
    root        13     1  0 23:59 pts/0    00:00:00 sleep 1000
    root        14     1  0 23:59 pts/0    00:00:00 sleep 1000
    root        15     1  0 23:59 pts/0    00:00:00 ps -ef

     

    그리고 이러한 Process 들은 동일한 PID Namespace 에 존재하게 됩니다.

     

    반면, 새로운 PID Namespace 를 생성하고, 새로운 PID Namespace 내부에 Process 들을 생성하게 되면,

    이 새로운 Process 들은 다른 PID Namespace 의 Process 들을 확인할 수 없습니다.

    하나의 예시로써 아래와 같이 unshare 를 활용하여 새로운 PID Namespace 생성과 함께 Process 를 생성하게 되면 기존과 다른 새로운 Process ID 들을 확인할 수 있습니다.

     

    unshare --fork --pid --mount-proc bash -c "
    	sleep 100 & \
    	sleep 100 & \
    	sleep 100 & \
    	sleep 100 & \
        ps -ef
    "
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 00:02 pts/0    00:00:00 bash -c  sleep 100 & sleep 100 & sleep 100 & sleep 100 &
    root         2     1  0 00:02 pts/0    00:00:00 sleep 100
    root         3     1  0 00:02 pts/0    00:00:00 sleep 100
    root         4     1  0 00:02 pts/0    00:00:00 sleep 100
    root         5     1  0 00:02 pts/0    00:00:00 sleep 100
    root         6     1  0 00:02 pts/0    00:00:00 ps -ef

     

     

    이어지는 내용에서 PID Namespace 격리에 대해서 자세히 알아보도록 하겠습니다.

     

    부모 PID Namespace 와 자식 PID Namespace 의 관계.

    PID Namespace 는 Process ID 의 공간을 격리시키는 방식입니다.

    이는 부모와 자식의 수직적인 관계를 가집니다.

    이는 다른 식으로 표현하면 부모 PID Namespace 공간에서는 자식 PID Namespace 의 Process 들을 확인할 수 있습니다.

    반면 자식 PID Namespace 에서는 부모 PID Namespace 의 Process 들을 확인할 수 없습니다.

     

    아래의 상태는 갓 생성된 부모 PID Namespace 에서의 ps 목록입니다.

     

    ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 00:05 pts/0    00:00:00 bash
    root        11     1  0 00:05 pts/0    00:00:00 ps -ef

     

     

    그리고 unshare 명령어를 통해서 자식 PID Namespace 를 생성할 수 있습니다.

     

    unshare --fork --pid --mount-proc bash

     

    그리고 부모 PID Namespace 에서 ps 목록을 확인하게 되면,

    아래와 같이 unshare 명령어로 인해 생성된 bash Process 를 확인할 수 있습니다.

     

    ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 00:05 pts/0    00:00:00 bash
    root        12     1  0 00:06 pts/0    00:00:00 unshare --fork --pid --mount-proc bash
    root        13    12  0 00:06 pts/0    00:00:00 bash
    root        25     0  0 00:06 pts/1    00:00:00 bash
    root        33    25  0 00:06 pts/1    00:00:00 ps -ef
    root@6f6f6c689020:/#

     

    그리고 아래의 ps 목록은 자식 PID Namespace 에서 확인한 ps 의 목록입니다.

    주목할 점은 자식 PID Namespace 에서 생성된 bash Process 는 자신이 1번 Process 로 지정된다는 사실입니다.

    그 외의 PID Namespace 의 Process 들은 확인되지 않습니다.

     

    ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 00:06 pts/0    00:00:00 bash
    root         4     1  0 00:07 pts/0    00:00:00 ps -ef

     

     

    이는 부모 PID Namespace 에서 bash Process 는 13번 ID 를 가지지만, 새로운 PID Namespace 에서는 1번 ID 를 가지는 Root Process 가 됩니다.

     

    PID Namespace 확인하기.

    /proc 디렉토리는 실행 중인 Process 의 상태를 저장하는 공간입니다.

    이는 실제 파일 시스템은 아니고 커널에 의해서 제공되는 메모리 상의 가상 파일 시스템입니다.

    아래와 같이 ls -l 명령어를 통해서 PID Namespace 의 고유값을 확인할 수 있습니다.

     

    ls -l /proc/$$/ns/pid

     

    아래는 기존의 PID Namespace 와 새로운 PID Namespace 에서 확인한 PID Link File 의 값입니다.

    이는 각각 동일한 PID Namespace 의 Link File 을 가리킵니다.

     

    자식 PID Namespace 의 1번 프로세스와 부모 PID Namespace 의 13번 Process 는 동일한 Process 입니다.

    반면, 부모 PID Namespace 의 1번 Process 는 Root Process 에 해당합니다.

    // 자식 PID Namespace 에서 확인한 PID
    ls -l /proc/1/ns/pid
    lrwxrwxrwx 1 root root 0 Jan 31 00:10 /proc/1/ns/pid -> 'pid:[4026532694]'
    
    // 부모 PID Namespace 에서 확인한 PID
    ls -l /proc/13/ns/pid
    lrwxrwxrwx 1 root root 0 Jan 31 00:11 /proc/13/ns/pid -> 'pid:[4026532694]'
    
    // 부모 PID Namespace 에서 확인한 ROOT PID
    ls -l /proc/1/ns/pid
    lrwxrwxrwx 1 root root 0 Jan 31 00:13 /proc/1/ns/pid -> 'pid:[4026532563]'

     

     

    위 결과처럼 새로운 PID Namespace 에서 Process 와 기존의 Root Process 는 서로 다른 PID Namespace 를 가집니다.

     

    /proc 확인해보기.

    아래의 사진은 부모와 자식의 PID Namespace 에서의 /proc 디렉토리 하위의 파일을 나타냅니다.

    첫번째 사진은 자식 PID Namespace 에서의 /proc 디렉토리 상태입니다.

    그리고 두번째 사진은 부모 PID Namespace 에서의 /proc 디렉토리의 상태입니다.

    두 Namespace 에서 관리 중인 Process 는 아래와 같이 서로 다르게 표현됩니다.

    서로 Process ID 가 다를 뿐더러 확인할 수 있는 Process 도 상이합니다.

    이러한 방식으로 서로 다른 PID Namespace 는 Process 간의 노출과 접근이 제한됩니다.

     

     

     

    unshare 명령어 알아보기.

    unshare 명령어를 통해서 PID Namespace 를 격리할 수 있습니다.

    unshare 명령어를 사용해서 새로운 PID Namespace 의 생성과 Process 실행 방법에 대해서 알아보겠습니다.

     

    기본적인 사용법은 아래와 같습니다.

    --fork, --pid, --mount-proc 와 같은 Flag 와 함께 실행하고자하는 Process 를 명시합니다.

     

    unshare --fork --pid --mount-proc bash

     

    --fork.

    --fork Flag 는 Fork System Call 과 같이 새로운 자식 Process 를 생성하는 목적으로 사용됩니다.

    아래와 같이 --fork Flag 만을 사용하여 새로운 Process 를 생성합니다.

    그리고 ps 목록을 출력해보면 아래와 같은 ps 목록이 출력됩니다.

    아래와 같이 unshare Process 와 unshare Process 의 자식 Process 인 sleep Process 가 실행됩니다.

     

    unshare --fork sleep 1000
    ps aux
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         1  0.0  0.0   3752  2828 pts/0    Ss+  00:05   0:00 bash
    root        25  0.0  0.0   3752  2724 pts/1    Ss   00:06   0:00 bash
    root        62  0.0  0.0   1952   984 pts/0    S    00:28   0:00 unshare --fork sleep 1000
    root        63  0.0  0.0   1948  1104 pts/0    S    00:28   0:00 sleep 1000
    root        65  0.0  0.0   5480  2244 pts/1    R+   00:28   0:00 ps aux

     

     

    하지만 --fork 만 수행하고 --pid Flag 를 생략한 경우에는 아래와 같이 새로운 PID 가 생성되지 않습니다.

    동일한 PID Namespace 를 공유하게 됩니다.

    root@6f6f6c689020:/# ls -l /proc/1/ns/pid
    >> /proc/1/ns/pid -> 'pid:[4026532563]'
    
    root@6f6f6c689020:/# ls -l /proc/63/ns/pid
    >> /proc/63/ns/pid -> 'pid:[4026532563]'

     

    --pid.

    --pid Flag 는 새로운 PID Namespace 를 생성하기 위한 Flag 입니다.

     

    unshare --fork --pid bash

     

    생성된 ps 목록은 아래와 같습니다.

     

    ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 00:05 pts/0    00:00:00 bash
    root        25     0  0 00:06 pts/1    00:00:00 bash
    root       118     1  0 00:35 pts/0    00:00:00 unshare --fork --pid bash
    root       119   118  0 00:35 pts/0    00:00:00 bash
    root       123   119  0 00:35 pts/0    00:00:00 ps -ef

     

    그리고 1번 Process 와 119 Process 는 서로 다른 PID Namespace 를 다룹니다.

     

    root@6f6f6c689020:/# ls -l /proc/119/ns/pid
    >> /proc/119/ns/pid -> 'pid:[4026532693]'
    
    root@6f6f6c689020:/# ls -l /proc/1/ns/pid
    >> /proc/1/ns/pid -> 'pid:[4026532563]'

     

    그리고 새로운 PID Namespace 내부에서 Process ID 는 아래와 같이 119번이 아닌 1번 Process 로 인식됩니다.

     

    echo $$
    >> 1

     

     

    --mount-proc.

    --mount-proc 는 새로운 /proc 디렉토리를 생성하는 Flag 입니다.

    사실상 --mount-proc 를 생략하게 되면 PID Namespace 가 서로 다르더라도 모든 Process 를 확인할 수 있습니다.

    ps 명령어는 /proc 디렉토리를 기준으로 ps 목록을 제공하기 때문에 --mount-proc 를 사용해서 PID Namespace 별로 /proc 를 새롭게 구성해야합니다.

    참고로 /proc 는 커널에서 제공하는 Process 의 상태이며, Namespace 에 따라서 커널에 알맞은 Process 정보를 제공하게 됩니다.

     

    unshare --fork --pid --mount-proc bash

     

    저는 부모 PID Namespace 에서 sleep Process 를 10개 정도 생성하였습니다.

    이는

     

    ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 00:05 pts/0    00:00:00 bash
    root        25     0  0 00:06 pts/1    00:00:00 bash
    root       131     1  0 00:41 pts/0    00:00:00 unshare --fork --pid --mount-proc bash
    root       132   131  0 00:41 pts/0    00:00:00 bash
    root       136    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       137    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       138    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       139    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       140    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       141    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       142    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       143    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       144    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       145    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       146    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       147    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       148    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       149    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       150    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       151    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       152    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       153    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       154    25  0 00:41 pts/1    00:00:00 sleep 10000
    root       155    25  0 00:41 pts/1    00:00:00 ps -ef

     

    이는 /proc 디렉토리 하위에서 개별 파일로써 관리됩니다.

     

     

    반면 이는 자식 PID Namespace 에서는 확인되지 않습니다.

     

    ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 00:41 pts/0    00:00:00 bash
    root         4     1  0 00:42 pts/0    00:00:00 ps -ef

     

     

    반응형
Designed by Tistory.