Linux에서 chroot 명령을 사용하는 방법

chroot 명령어는 마치 감옥과 같은 격리된 환경을 만들어 개발, 테스트, 시스템 보안 강화 등 다양한 목적으로 활용될 수 있습니다. 가장 간단하게 사용하는 방법을 안내해 드립니다.

chroot란 무엇인가?

명령어의 유용성을 평가할 때, 그 기능과 사용 편의성은 필수적으로 고려해야 할 요소입니다. 아무리 강력한 기능을 제공하더라도 사용하기 너무 복잡하거나 설정하는 데 너무 많은 시간이 소요된다면, 그 명령어는 실제로는 아무런 기능을 하지 못하는 것과 같습니다. 아무도 사용하지 않으면, 어떤 기능도 발휘할 수 없기 때문입니다.

리눅스 사용자들과 직접 또는 포럼에서 나눈 의견들을 종합해 볼 때, chroot 명령어는 사용하기 어렵거나 설정 과정이 지나치게 복잡하고 번거로운 것으로 인식되는 경향이 있습니다. 이러한 이유로, 뛰어난 잠재력을 가진 이 유틸리티가 제대로 활용되지 못하는 경우가 많은 것 같습니다.

chroot 명령어를 사용하면, 마치 격리된 환경에서 프로그램이나 대화형 쉘을 실행하는 것과 같습니다. 이것은 일반 파일 시스템과는 격리된, 캡슐화된 파일 시스템 안에서 Bash와 같은 쉘을 실행하는 것입니다. chroot 환경 내의 모든 것은 그 안에 완전히 포함되어 있으며, chroot 환경 내에서는 루트 권한으로 상승하지 않고는 자신의 특수한 루트 디렉토리를 벗어날 수 없습니다. 이러한 이유로 chroot 환경은 종종 ‘chroot 감옥’이라고 불리기도 합니다. 다만 여기서 ‘감옥’이라는 용어는 FreeBSD의 jail 명령어와는 혼동하지 않아야 합니다. FreeBSD jail은 chroot 환경보다 더욱 강력한 보안 기능을 제공합니다.

하지만 실제로 chroot를 사용하는 것은 생각보다 훨씬 간단할 수 있습니다. 이제 그 방법을 단계별로 알아보겠습니다. 여기서는 모든 리눅스 배포판에서 작동하는 일반적인 리눅스 명령어를 사용할 것입니다. 일부 리눅스 배포판, 예를 들어 Ubuntu에서는 debootstrap 같은 chroot 환경 설정 전용 도구를 제공하지만, 여기서는 특정 배포판에 종속되지 않는 방법을 소개할 것입니다.

chroot는 언제 사용해야 할까?

chroot 환경은 가상 머신과 유사한 기능을 제공하지만, 더 가벼운 해결책입니다. 하이퍼바이저를 설치하고 설정하는 과정이 필요하지 않으므로, VirtualBox가상 머신 관리자와 같은 무거운 가상화 소프트웨어의 대안으로 고려할 수 있습니다. 또한 종속 시스템에 별도의 커널을 설치할 필요도 없습니다. chroot 환경은 기존 커널을 공유하기 때문입니다.

어떤 면에서 chroot 환경은 가상 머신보다는 LXC와 같은 컨테이너에 더 가깝다고 볼 수 있습니다. 가볍고 배포 속도가 빠르며, 생성과 실행을 자동화하기에도 용이합니다. 컨테이너와 마찬가지로, chroot 환경을 구성하는 효과적인 방법 중 하나는 필요한 작업만을 수행할 수 있을 만큼 최소한의 운영 체제 환경을 설치하는 것입니다. ‘필요한 것’이 무엇인지는 chroot 환경을 어떻게 활용할지에 따라 결정됩니다.

chroot 환경의 일반적인 활용 예시는 다음과 같습니다.

소프트웨어 개발 및 제품 검증: 개발자는 소프트웨어를 만들고, 제품 검증(PV) 팀은 이를 테스트합니다. 개발자의 컴퓨터에서 재현되지 않는 문제가 PV 환경에서 발견되는 경우가 종종 있습니다. 개발자는 개발 환경에 다양한 도구와 라이브러리를 설치해 놓았지만, 일반 사용자와 PV 환경에는 이러한 것들이 없을 수 있습니다. 소프트웨어가 개발자의 PC에서는 정상적으로 작동하지만 다른 환경에서는 문제가 발생하는 이유는, 개발 PC에 설치된 특정 리소스에 의존하기 때문인 경우가 많습니다. chroot 환경을 사용하면, 개발자는 PV 환경에 소프트웨어를 배포하기 전에 표준화된 최소 종속성 환경에서 소프트웨어를 충분히 테스트해 볼 수 있습니다.

개발 위험 감소: 개발자는 chroot 환경을 통해 실제 PC에 영향을 주지 않는 격리된 개발 환경을 만들 수 있습니다. 실수로 시스템 설정을 잘못 변경하거나 소프트웨어 충돌을 일으키는 것을 방지할 수 있습니다.

구형 소프트웨어 실행: 때로는 이전 버전의 소프트웨어를 실행해야 할 때가 있습니다. 하지만 이전 버전의 소프트웨어가 현재 사용 중인 리눅스 버전과 충돌하거나 호환되지 않을 수 있습니다. 이러한 경우, chroot 환경을 사용하여 해당 소프트웨어에 맞는 격리된 환경을 제공할 수 있습니다.

시스템 복구 및 파일 시스템 업그레이드: 리눅스 설치가 제대로 작동하지 않을 때, chroot를 사용하여 Live CD에서 손상된 파일 시스템을 마운트할 수 있습니다. 이를 통해 손상된 시스템에서 작업을 수행하고, 마치 해당 시스템의 루트 디렉터리에서 작업하는 것처럼 복구 작업을 시도할 수 있습니다. 손상된 시스템의 파일 경로가 Live CD 마운트 지점이 아닌 실제 루트 디렉터리를 참조하게 되므로, 보다 직관적으로 복구 작업을 수행할 수 있습니다. 이러한 기술은 ext2나 ext3 파일 시스템을 ext4로 마이그레이션하는 방법에 대한 글에서도 유사하게 활용되었습니다.

애플리케이션 격리: chroot 환경 내에서 FTP 서버나 다른 인터넷 연결 장치를 실행하면 외부 공격자가 입힐 수 있는 피해를 제한할 수 있습니다. 이는 시스템 보안을 강화하는 데 중요한 단계가 될 수 있습니다.

chroot 환경 생성하기

먼저 chroot 환경의 루트 디렉토리로 사용할 디렉토리가 필요합니다. 디렉토리 경로를 변수에 저장하여 간편하게 참조할 수 있도록 하겠습니다. 여기서는 “testroot”라는 디렉토리의 경로를 저장할 변수를 설정하겠습니다. 이 디렉토리가 아직 존재하지 않아도 괜찮습니다. 곧 만들 것이니까요. 이미 디렉토리가 있다면, 반드시 비어 있어야 합니다.

chr=/home/dave/testroot

디렉토리가 없다면 생성해야 합니다. 다음 명령어를 사용하여 디렉토리를 만들 수 있습니다. 여기서 -p(parents) 옵션은 누락된 상위 디렉토리도 함께 생성하도록 해줍니다.

mkdir -p $chr

다음으로, chroot 환경에 필요한 운영 체제 구성 요소를 저장할 디렉토리를 만들어야 합니다. 여기서는 대화형 쉘로 Bash를 사용하는 최소한의 Linux 환경을 설정할 것입니다. touch, rm, ls 명령어 또한 포함시킬 것입니다. 이렇게 하면 Bash 내장 명령어와 touch, rm, ls 명령어를 사용할 수 있게 됩니다. 즉, 파일을 생성, 나열, 제거하고 Bash를 사용할 수 있게 됩니다. 이번 간단한 예시에서는 이것이 전부입니다.

중괄호 안의 디렉토리 목록은 중괄호 확장 기능을 사용하여 생성됩니다.

mkdir -p $chr/{bin,lib,lib64}

이제 현재 디렉토리를 새로 만든 루트 디렉토리로 변경합니다.

cd $chr

미니멀 Linux 환경에 필요한 실행 파일들을 일반적인 “/bin” 디렉토리에서 chroot 환경의 “/bin” 디렉토리로 복사해 보겠습니다. -v(verbose) 옵션은 cp 명령어에서 각 복사 작업이 수행될 때 어떤 작업을 하는지 알려줍니다.

cp -v /bin/{bash,touch,ls,rm} $chr

파일들이 복사됩니다.

이러한 실행 파일들은 의존성(dependency)을 가지고 있습니다. 어떤 의존성이 있는지 확인하고, 해당 파일들을 우리 환경에도 복사해야 합니다. 그렇지 않으면 bash, touch, rm, ls 명령어들이 작동하지 않을 것입니다. 각 명령어에 대해 이 작업을 차례대로 수행해야 합니다. 먼저 Bash부터 진행하겠습니다. ldd 명령어는 의존성 목록을 보여줍니다.

ldd /bin/bash

의존성이 확인되어 터미널 창에 출력됩니다.

해당 파일들을 새로운 환경에 복사해야 합니다. 목록에서 하나하나 세부 정보를 확인하고 복사하는 것은 시간이 오래 걸리고 오류가 발생하기 쉽습니다.

다행히, 이 과정을 반자동화할 수 있습니다. 다시 의존성 목록을 얻고, 그 목록을 활용하여 파일을 복사하는 루프를 만들 것입니다.

여기서는 ldd를 사용하여 의존성 목록을 얻고, 그 결과를 파이프를 통해 egrep에 전달합니다. egrep은 grep 명령어에 -E(확장 정규식) 옵션을 추가한 것과 동일합니다. -o(일치하는 부분만) 옵션은 출력 결과를 라인에서 일치하는 부분으로 제한합니다. 여기서는 숫자로 끝나는 라이브러리 파일을 찾고 있습니다. [0-9].

list="$(ldd /bin/bash | egrep -o '/lib.*.[0-9]')"

echo 명령어를 사용하여 목록 내용을 확인할 수 있습니다.

echo $list

이제 목록이 준비되었으므로, 다음 루프를 사용하여 파일을 하나씩 복사할 수 있습니다. 변수 i를 사용하여 목록의 각 항목을 단계별로 살펴볼 것입니다. 목록의 각 항목에 대해, 해당 파일을 chroot 루트 디렉토리인 $chr에 복사합니다.

-v(verbose) 옵션은 cp 명령어에서 각 복사 작업을 할 때 어떤 작업을 하는지 보여줍니다. –parents 옵션은 누락된 상위 디렉토리가 chroot 환경에 자동으로 생성되도록 해줍니다.

for i in $list; do cp -v --parents "$i" "${chr}"; done

다음은 출력 결과입니다.

이 기법을 사용하여 다른 명령어들의 의존성을 수집합니다. 그리고 루프 기술을 사용하여 실제 복사 작업을 수행합니다. 좋은 점은, 의존성을 수집하는 명령어만 약간 수정하면 된다는 것입니다.

위쪽 화살표 키를 몇 번 눌러 명령 기록에서 명령어를 검색한 다음 편집할 수 있습니다. 반복 복사 명령어는 변경할 필요가 없습니다.

여기서 위쪽 화살표 키를 사용하여 명령어를 찾고, bash 대신 touch를 표시하도록 수정했습니다.

list="$(ldd /bin/touch | egrep -o '/lib.*.[0-9]')"

이제 이전과 똑같은 루프 명령어를 반복할 수 있습니다.

for i in $list; do cp -v --parents "$i" "${chr}"; done

파일들이 복사됩니다.

이제 ls 명령어에 대한 목록 명령줄을 편집할 수 있습니다.

list="$(ldd /bin/ls | egrep -o '/lib.*.[0-9]')"

다시 한번, 동일한 루프 명령어를 사용합니다. 목록에 어떤 파일이 있는지는 중요하지 않습니다. 루프는 단순히 목록을 기반으로 파일을 복사할 것입니다.

for i in $list; do cp -v --parents "$i" "${chr}"; done

ls 명령어의 의존성이 복사되었습니다.

마지막으로 list 명령줄을 수정하여 rm 명령어에 대한 의존성을 수집합니다.

list="$(ldd /bin/ls | egrep -o '/lib.*.[0-9]')"

마지막으로 반복 복사 명령어를 실행합니다.

for i in $list; do cp -v --parents "$i" "${chr}"; done

이제 마지막 의존성까지 모두 chroot 환경에 복사되었습니다. 이제 드디어 chroot 명령어를 사용할 준비가 되었습니다. 이 명령어는 chroot 환경의 루트 디렉토리를 설정하고, 쉘로 실행할 애플리케이션을 지정합니다.

sudo chroot $chr /bin/bash

이제 chroot 환경이 활성화되었습니다. 터미널 창의 프롬프트가 변경되었으며, 우리의 환경에 속한 bash 쉘에서 대화형 쉘이 처리되고 있습니다.

이제 환경에 복사한 명령어들을 테스트해 볼 수 있습니다.

ls
ls /home/dave/Documents

ls 명령어는 예상대로 환경 내에서는 잘 작동하지만, 환경 외부의 디렉토리에 접근하려고 하면 실패합니다.

touch 명령어로 파일을 생성하고, ls 명령어로 파일 목록을 확인하고, rm 명령어로 파일을 삭제할 수 있습니다.

touch sample_file.txt
ls
rm sample_file.txt
ls

물론 Bash 쉘이 제공하는 내장 명령어들도 사용할 수 있습니다. 명령줄에 help를 입력하면 Bash가 자동으로 해당 항목들을 나열해 줍니다.

help

exit 명령어를 사용하여 chroot 환경에서 빠져나올 수 있습니다.

exit

chroot 환경을 제거하려면, 해당 디렉토리를 삭제하면 됩니다.

rm -r testroot/

이것은 chroot 환경 내의 파일과 디렉토리를 재귀적으로 삭제합니다.

편의성을 위한 자동화

chroot 환경이 유용하다고 생각하지만, 설정 과정이 조금 복잡하다면, alias, 함수, 스크립트 등을 사용하여 반복적인 작업을 자동화하여 항상 부담과 위험을 줄일 수 있다는 점을 기억하세요.