실습

해당 실습은 AWS 환경에서 Ubuntu Server 20.04 LTS 에서 실습을 진행하다고 가정하고 작성되었습니다.

Install Docker

sudo apt update && sudo apt install -y docker.io

Chroot

  1. 루트 유저로 전환

    sudo -i
  2. /tmp 디렉토리 하위에 새로운 루트 디렉토리로 사용할 디렉토리 생성

    mkdir /tmp/new-root
  3. 위에서 생성한 디렉토리를 루트 디렉토리로 변경 시도

    chroot /tmp/new-root
  4. 새로운 루트로 사용할 디렉토리 하위에 bin 디렉토리를 생성

    mkdir /tmp/new-root/bin
  5. 호스트에 있는 /bin/bash 파일을 새로운 루트 디렉토리로 사용할 디렉토리 하위의 bin 디렉토리로 복사

    cp /bin/bash /tmp/new-root/bin
  6. 루트 디렉토리 변경 재시도

    chroot /tmp/new-root
  7. bash 명령어 실행에 필요한 연관 라이브러리 파일 확인

    ldd /bin/bash
  8. 새로운 루트로 사용할 디렉토리로 Bash 프로세스 실행에 필요한 연관 라이브러리 복사

    {
        mkdir /tmp/new-root/lib/x86_64-linux-gnu -p
        mkdir /tmp/new-root/lib64
    
        cp /lib/x86_64-linux-gnu/libtinfo.so.6 /tmp/new-root/lib/x86_64-linux-gnu
        cp /lib/x86_64-linux-gnu/libdl.so.2 /tmp/new-root/lib/x86_64-linux-gnu
        cp /lib/x86_64-linux-gnu/libc.so.6 /tmp/new-root/lib/x86_64-linux-gnu
        cp /lib64/ld-linux-x86-64.so.2 /tmp/new-root/lib64
    }
  9. 루트 디렉토리 변경 재시도

    chroot /tmp/new-root
  10. 현재 작업중인 디렉토리 확인

    pwd
  11. 아래의 명령어를 실행해서 새로운 루트 디렉토리로 실행된 Bash 프로세스 종료

    exit
  12. 새로운 루트로 사용할 디렉토리 하위에 텍스트 파일 생성

    date > /tmp/new-root/date.txt
  13. 새로운 루트로 사용할 디렉토리로 /bin/ls 파일 및 ls 프로세스 실행에 필요한 연관 라이브러리 복사

    {
        cp /bin/ls /tmp/new-root/bin
        cp /lib/x86_64-linux-gnu/libselinux.so.1 /tmp/new-root/lib/x86_64-linux-gnu
        cp /lib/x86_64-linux-gnu/libpcre2-8.so.0 /tmp/new-root/lib/x86_64-linux-gnu
        cp /lib/x86_64-linux-gnu/libpthread.so.0 /tmp/new-root/lib/x86_64-linux-gnu
    }
  14. 루트 디렉토리 변경

    chroot /tmp/new-root
  15. 위에서 생성한 텍스트파일이 보이는지 확인

    ls
  16. 현재 작업중인 디렉토리 확인

    pwd
  17. 새로운 루트 디렉토리로 실행된 Bash 프로세스 종료

    exit
  18. 새로운 루트로 사용할 디렉토리로 /bin/cat 파일 및 cat 프로세스 실행에 필요한 연관 라이브러리 복사하고 루트 디렉토리를 변경하고 cat 명령어로 텍스트 파일 내용 확인

문제 답안
  1. 새로운 루트로 사용할 디렉토리로 /bin/cat 파일 및 cat 프로세스 실행에 필요한 연관 라이브러리 복사

    cp /bin/cat /tmp/new-root/bin
  2. 루트 디렉토리 변경

    chroot /tmp/new-root
  3. 위에서 생성한 텍스트 파일 내용 확인

    cat date.txt
  4. 새로운 루트 디렉토리로 실행된 Bash 프로세스 종료

    exit

Docker Image

  1. 도커 허브에 있는 nginx 이미지로 컨테이너 생성

    docker create --name nginx nginx 
  2. 로컬 호스트에 생성된 컨테이너 목록 확인

    docker container ls -a
  3. /tmp/nginx 디렉토리 생성

    mkdir /tmp/nginx
  4. /tmp/nginx 디렉토리로 nginx 컨테이너의 파일들을 복사

    docker cp $(docker container ls -aq -f name=nginx):/ /tmp/nginx
  5. nginx 컨테이너가 실행될때 수행되는 명령어 확인

    docker inspect $(docker container ls -aq -f name=nginx) --format='{{.Config.Cmd}}'
  6. /tmp/nginx 디렉토리로 루트 디렉토리로 변경

    chroot /tmp/nginx
  7. Nginx 웹서버 실행

    nginx -g "daemon off;"
  8. 새로운 SSH 세션을 열고 Nginx 웹서버 구동 확인

    curl localhost
  9. 위에서 새로 연결한 SSH 세션에서 ubuntu 이미지로 컨테이너를 생성하고 해당 컨테이너의 파일들을 /tmp/ubuntu 로 복사하고 /tmp/ubuntu 디렉토리로 루트 디렉토리로 변경. 단 /bin/bash가 아닌 /bin/sh 를 실행

문제 답안
  1. ubuntu 이미지로 도커 컨테이너 생성

    sudo docker create --name ubuntu ubuntu
  2. /tmp/ubuntu 디렉토리 생성

    mkdir /tmp/ubuntu
  3. /tmp/ubuntu 디렉토리로 ubuntu 컨테이너의 파일들을 복사

    sudo docker cp $(sudo docker container ls -aq -f name=ubuntu):/ /tmp/ubuntu
  4. /tmp/ubuntu 디렉토리로 루트 디렉토리로 변경

    sudo chroot /tmp/ubuntu /bin/sh
  5. 정상 동작시 새로운 루트 디렉토리로 실행된 sh 프로세스 종료

    exit

Network Namespace

  1. 첫번째 세션에서 Ctrl+C를 입력해서 Nginx 프로세스 종료

  2. 새로운 루트 디렉토리로 실행된 Bash 프로세스 종료

    exit
  3. 새로운 네트워크 네임스페이스 생성

    ip netns add nginx
  4. 네트워크 네임스페이스 생성 확인

    ip netns list
  5. 위에서 생성한 네트워크 네임스페이스의 네트워크 디바이스 목록 확인

    ip netns exec nginx ip -br link
  6. 위에서 생성한 네트워크 네임스페이스의 루프백 인터페이스 구동

    ip netns exec nginx ip link set dev lo up
  7. 위에서 생성한 네트워크 네임스페이스를 사용하면서 /tmp/nginx 디렉토리로 루트 디렉토리로 변경

    ip netns exec nginx chroot /tmp/nginx
  8. Nginx 웹서버 실행

    nginx -g "daemon off;"
  9. 두번째 SSH 세션으로 이동해서 Nginx 웹서버 구동 확인

    curl localhost
  10. 위에서 생성한 네트워크 네임스페이스의 루프백 인터페이스를 통해서 접근 시도

    sudo ip netns exec nginx curl localhost
  11. 첫번째 SSH 세션으로 이동

  12. Ctrl+C를 입력해서 Nginx 프로세스 종료

  13. 새로운 루트 디렉토리로 실행된 Bash 프로세스 종료

    exit
  14. 디폴트 네트워크 네임스페이스에 가상 네트워크 인터페이스 추가

    ip link add veth0 type veth peer name veth1
  15. 생성된 가상 네트워크 인터페이스 확인

    ip -br link
  16. 디폴트 네트워크 네임스페이스에 생성된 네트워크 디바이스 veth1를 nginx 네트워크 네임스페이스로 이동

    ip link set veth1 netns nginx
  17. 디폴트 네트워크 네임스페이스의 네트워크 디바이스 목록 확인

    ip -br link
  18. nginx 네트워크 네임스페이스의 네트워크 디바이스 목록 확인

    ip netns exec nginx ip -br link
  19. 네트워크 디바이스 veth0에 IP 주소 할당

    ip addr add 192.168.1.1/24 dev veth0
  20. 네트워크 디바이스 veth1에 IP 주소 할당

    ip netns exec nginx ip addr add 192.168.1.2/24 dev veth1
  21. 네트워크 디바이스 veth0 구동

    ip link set dev veth0 up
  22. 네트워크 디바이스 veth1 구동

    ip netns exec nginx ip link set dev veth1 up
  23. 디폴트 네트워크 네임스페이스의 네트워크 디바이스 상태 확인

    ip -br link
  24. nginx 네트워크 네임스페이스의 네트워크 디바이스 상태 확인

    ip netns exec nginx ip -br link
  25. 위에서 생성한 네트워크 네임스페이스를 사용하면서 /tmp/nginx 디렉토리로 루트 디렉토리로 변경

    ip netns exec nginx chroot /tmp/nginx
  26. Nginx 웹서버 실행

    nginx -g "daemon off;"
  27. 두번째 SSH 세션으로 이동해서 Nginx 웹서버 구동 확인

    curl 192.168.1.2
  28. 첫번째 SSH 세션으로 이동

  29. Ctrl+C를 입력해서 Nginx 프로세스 종료

  30. Nginx 접근 로그 확인

    cat /var/log/nginx/access.log       

PID Namespace

  1. 첫번째 SSH 세션에서 Nginx 웹서버 구동

    nginx -g "daemon off;"
  2. 두번째 SSH 세션에 /tmp/ubuntu를 루트 디렉토리로 사용하는 sh 프로세스 실행

    sudo chroot /tmp/ubuntu /bin/sh
  3. 새로운 SSH 세션을 열고 첫번째 세션에서 구동한 nginx 프로세스의 PID를 확인

    ps aux | grep nginx
  4. 두번째 SSH 세션에서 kill 명령어를 통해서 모든 nginx 프로세스 종료를 시도

    kill -9 NGINX_PID
  5. 첫번째 SSH 세션으로 이동해서 실제로 nginx 프로세스가 종료되었는지 확인

  6. 첫번째 SSH 세션으로 이동해서 종료된 nginx 프로세스를 재기동

    nginx -g "daemon off;"
  7. 세번째 SSH 세션으로 이동해서 리눅스 네임스페이스 목록 확인

    lsns
  8. nginx 프로세스의 PID를 확인

    ps aux | grep nginx
  9. nginx 프로세스가 실행되는 PID 네임스페이스 확인

    sudo ls -Li /proc/NGINX_PID/ns/pid
  10. sh 프로세스의 PID를 확인

    ps aux | grep /bin/sh
  11. sh 프로세스가 실행되는 PID 네임스페이스 확인

    sudo ls -Li /proc/SH_PID/ns/pid
  12. 두번째 SSH 세션으로 이동해서 /proc 파일시스템 마운트

    mount -t proc proc /proc
  13. nginx 프로세스의 PID를 확인

    ps aux | grep nginx
  14. /tmp/ubuntu 디렉토리로 루트 디렉토리로 실행되고 있는 sh 프로세스 종료

    exit 
  15. 새로운 PID 네임스페이스를 생성하고 /tmp/ubuntu 디렉토리로 루트 디렉토리로 하는 sh 프로세스 실행

    sudo unshare --pid --fork chroot /tmp/ubuntu /bin/sh
  16. /proc 파일시스템 마운트

    mount -t proc proc /proc
  17. 실행중인 프로세스 목록 확인

    ps aux
  18. 세번째 세션으로 이동해서 첫번째 세션에서 구동중인 nginx 프로세스의 PID 확인

    ps aux | grep nginx
  19. 두번째 SSH 세션으로 (PID가 격리된 ubuntu 환경) 이동해서 nginx 프로세스 종료 시도

    kill -9 NGINX_PID
  20. 세번째 SSH 세션으로 이동해서 PID 네임스페이스 목록 확인

    sudo lsns -t pid
  21. sh 프로세스의 PID를 확인

    ps aux | grep /bin/sh
  22. sh 프로세스 종료 시도

    sudo kill -9 SH_PID
  23. PID 네임스페이스 목록 확인

    sudo lsns -t pid

Cgroup

  1. 두번째 SSH 세션으로 이동해서 새로운 PID 네임스페이스를 생성하고 /tmp/ubuntu 디렉토리로 루트 디렉토리로 하는 sh 프로세스 실행

    sudo unshare --pid --fork chroot /tmp/ubuntu /bin/sh
  2. 아래와 같은 명령어를 실행해서 CPU 부하를 발생

    mknod /dev/null c 1 3
    yes > /dev/null
  3. 세번째 SSH 세션으로 이동해서 CPU 사용량 확인

    top
  4. cgroup-tools 설치

    sudo apt install cgroup-tools -y
  5. 새로운 cgroup 생성

    sudo cgcreate -g cpu,memory,blkio,devices,freezer:/ubuntu
  6. 격리된 ubuntu 환경에서 실행중인 sh 프로세스의 PID를 확인

    ps aux | grep /bin/sh
  7. sh 프로세스에  위에서 생성한 cgroup 부여

    sudo cgclassify -g cpu,memory,blkio,devices,freezer:ubuntu SH_PID
  8. CPU 사용률을 30%로 제한

    sudo cgset -r cpu.cfs_quota_us=30000 ubuntu
  9. CPU 사용량 확인

    top
  10. 두번째 SSH 세션으로 이동해서 실행중인 프로세스를 종료

  11. CPU 부하를 다시 발생시키고 CPU 사용률이 30%로 제한되는지 확인

    yes > /dev/null
  12. 메모리 사용량을 100M로 제한하고 아래와 같은 명령어를 실행해서 확인

    yes | tr \\n x | head -c 1048576000 | grep n
문제 답안
sudo cgset -r memory.limit_in_bytes=100M ubuntu

메모리 부하를 발생하는 프로세스가 강제 종료될 경우 아래의 명령어로 원인 파악

dmesg | tail -n 100

Last updated