Kakao Tech 유튜브를 통해 학습한 컨테이너 격리 관련 내용을 정리하였습니다.
해당 유튜브의 핵심은 아래와 같습니다.
위 세가지 내용이 감이 오시는 분도 있고, 모호하신 분도 있을 것 같습니다.
해당 내용들을 중점으로 정리하였으니, 참고하시기 바랍니다.
참고 Kakao Tech Blog - https://www.youtube.com/watch?v=mSD88FuST80
서버는 여러 사람들이 사용하게 됩니다.(FTP, SSH, HTTP...)
여러 사람들 중, 악의적으로 서버에 접근하는 사람들이 생겨나며, 서버 내에서 활동을 할 수 있었습니다.
이때, 접근한 사용자 프로세스를 가둘 수는 없을까? 라는 고민에서 착안되어 생겨난 개념이 chroot(change root)입니다.
즉, root diretory 밖으로 나갈 수 없게 만드는 것이 목적입니다.

chroot를 통해 shell을 실행해보겠습니다.
/tmp# mkdir
/tmp# chroot myroot /bin/sh
아래와 같이 /bin/sh를 찾지 못했다는 에러가 발생합니다.
chroot: cannot change root directory to 'myroot': No such file or directory
이유가 뭘까요? 저희는 myroot를 chroot 명령을 통해 새로운 root로 지정 했습니다.
이때, myroot 하위에는 아무것도 존재하지 않기에, 에러가 발생하는 것입니다.
즉, myroot 밑에, /bin/sh를 복사해줘야. shell 실행이 가능한 것입니다.
/tmp# which sh
/tmp# ldd /bin/sh
linux-vdso.so.1 (0x00007ffd6630b000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007d0bdb800000)
/lib64/ld-linux-x86-64.so.2 (0x00007d0bdba72000)
이때, 참고할 점은 /sh 실행 파일만 복사하면 안되며, 바이너리를 함께 복사해줘야 정상 동작이 가능하게 됩니다. (linux-vdso.so.1는 복사 안해도 됨)
mkdir -p myroot/bin;
cp /bin/sh myroot/bin/;
mkdir -p myroot/{lib64,lib/x86_64-linux-gnu};
cp /lib/x86_64-linux-gnu/libc.so.6 myroot/lib/x86_64-linux-gnu/;
cp /lib64/ld-linux-x86-64.so.2 myroot/lib64;
tree 명령으로 아래와 같은 구조가 되어야 합니다.
tree myroot
myroot
├── bin
│ └── sh
├── lib
│ └── x86_64-linux-gnu
│ └── libc.so.6
└── lib64
└── ld-linux-x86-64.so.2
다시 myroot를 통해 shell을 실행시키면, 아래와 같이 정상적으로 실행된 것을 확인 가능합니다.
chroot myroot /bin/sh
#
해당 shell에서 ls 명령을 수행하면 어떨까요?
감을 잡으신 분들도 있을 것 같은데, 당연히 실행이 안됩니다.
# ls 명령어 위치 확인
which ls
ldd /bin/ls
# ls 명령어 바이너리 복붙
cp /bin/ls myroot/bin/;
# 의존성 라이브러리 북붙
cp /lib/x86_64-linux-gnu/{libselinux.so.1,libc.so.6,libpcre.so.3,libdl.so.2,libpthread.so.0} myroot/lib/x86_64-linux-gnu/;
cp /lib64/ld-linux-x86-64.so.2 myroot/lib64/;
cp /lib/x86_64-linux-gnu/libpcre2-8.so.0 /tmp/myroot/lib/x86_64-linux-gnu/
tree myroot
myroot
├── bin
│ ├── ls
│ └── sh
├── lib
│ └── x86_64-linux-gnu
│ ├── libc.so.6
│ ├── libdl.so.2
│ ├── libpcre.so.3
│ ├── libpthread.so.0
│ └── libselinux.so.1
└── lib64
└── ld-linux-x86-64.so.2
바이너리 복사 후, 아래와 같이 ls 명령어 확인 시, 명령어 정상 동작을 확인 가능합니다.
또한, cd 명령을 통해 /를 탈출하려고 시도하지만, 탈출이 되지 않습니다.
/tmp# chroot myroot /bin/sh
# ls /
bin lib lib64
# cd /
# cd ../
# cd ../../
이때, 핵심 개념은 chroot로 새롭게 지정한 myroot와 기존에 존재하는 root가 가지고 있는 리소스가 다르다는 것입니다.
이는 chroot를 사용하여 사용자 프로세스를 격리(jail)한 것으로 Docker의 핵심 개념 중 하나입니다.
두 요소들은 컨테이너에서 활용되는 모습과 굉장히 유사하므로, 컨테이너 작동 이해에 도움이 되었길 바랍니다.
아래 Script가 있으니, 스크립트를 사용하든 이어지는 아래 글을 참고하든, 미션을 해보실 분들은 해보시기 바랍니다.
https://github.com/sam0kim/container-internal/blob/main/scripts/chroot_ps.sh
chroot를 통해 myroot에서 sh 실행 후, ps를 해보니 proc가 mount되지 아 정상 동작하지 않았고, proc를 mount하니 아래와 같이 정상적으로 ps 명령이 동작합니다.
/tmp# chroot /tmp/myroot /bin/sh
# ps
Error, do this: mount -t proc proc /proc
# mount /proc
mount: /proc: can't find in /etc/fstab.
# mount -t proc proc /proc
# ps
PID TTY TIME CMD
1329 ? 00:00:00 sudo
1330 ? 00:00:00 su
1331 ? 00:00:00 bash
1632 ? 00:00:00 sh
1637 ? 00:00:00 ps
/tmp# umount /tmp/myroot/proc
위와 같이 ps 명령을 새롭게 생성하여 격리된 myroot에서 사용하려고 하니, bin/ps 등의 디렉토리 복사며, 라이브러리 복사 등등 손수해야할 작업이 많았습니다.
추후 ps 명령뿐만 아니라, 다른 사항들을 myroot에서 활용하고자 한다면?
더욱 더 해야할 일이 늘어납니다.
Docker와 같은 컨테이너 환경에서는 Image를 사용하여 내가 원하는 환경을 손쉽게 구성이 가능합니다!
/tmp# mkdir nginx-root
/tmp# docker export $(docker create nginx) | tar -C nginx-root -xvf -;
nginx-root 디렉터리 확인 시, 비어있던 디렉토리가 채워져 있음을 확인 가능합니다.
또한, sh 명령 시, 이전에 했던 작업들을 하지 않아도 정상 동작합니다.
root@ip-10-0-1-68:/tmp# tree -L 1 nginx-root/
nginx-root/
├── bin -> usr/bin
├── boot
├── dev
...
20 directories, 1 file
root@ip-10-0-1-68:/tmp# chroot nginx-root/ /bin/sh;
#
nginx를 기동시켜 볼까요?
# nginx -g "daemon off;"
저는 AWS EC2에서 실행 중이기 때문에, 인스턴스의 Public ip를 통해 nginx 작동 확인을 합니다. (터미널 하나 더 띄어서 localhost:80으로 해도 가능함)
curl 43.200.212.219:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
위의 과정들을 통해 생각해볼 수 있는 것은, chroot만으로 docker 컨테이너 활용이 가능하다는 생각이 들 것 같습니다.
그러나, chroot 만으로는 부족합니다.
이유는? 격리된 myroot에서 탈옥이 가능하기 때문입니다!
탈옥과, 탈옥을 막는 방법은 도커 없이 컨테이너 만들기 - (2)에서 이어 작성하도록 하겠습니다.
읽어주셔서 감사합니다.