OpenSSH chroot 설정

by 조쉬 posted Feb 27, 2014
?

단축키

Prev이전 문서

Next다음 문서

ESC닫기

크게 작게 위로 아래로 댓글로 가기 인쇄
무료 웹호스팅을 하면서 늘 아쉬웠던 점이 일반사용자들에게 SSH/SFTP를 개방하기 힘들다는 점이었습니다.
SSH/SFTP를 통해 일반사용자 권한으로 쉘에 들어오면 '/' 디렉토리 하단으로 디렉토리 이동이 자유롭기에 서버관리자만이 드나들며 관리해야 할 영역이 너무 쉽게 노출이 된다는 점 때문이죠.
 vsftpd, proftpd 등과 같이 ftp 서버 데몬의 경우는 환경설정파일에 간단하게 옵션 몇 줄만 추가해 주면 데몬 차원에서 상위 디렉토리 이동을 막을 수 있기에 FTP 서비스를 하는 경우는 크게 문제될 점이 없지만 일반사용자에게 쉘 접근권한을 주는 것은 안전장치를 마련해 놓지 않는 이상은 구현하기 어렵지는 않다 하더라도 다소 복잡한 설정이 필요하게 됩니다.  SSH서버 데몬으로 널리 쓰이는 오픈소스인 OpenSSH의 경우 종전까지는 기본으로 chroot 기능을 지원하지 않아 chroot 프로젝트를 통해 나온 소스포지(http://sourceforge.net)에서 제공하는 패치 버전을 통해서만이 가능하였으나 OpenSSH 4.8p1 버전부터는 chroot 기능을 자체적으로 지원하기 시작하였습니다. 본 문서에서는 OpenSSH 5.x 버전을 이용한 chroot 구현 방법에 대해 소개합니다.
 CentOS 5.x 64bit에서의 설치 및 설정 방법에 대해 설명합니다.
 1. telnet-server 설치 및 설정, 기존 OpenSSH 삭제
 우선 chroot를 지원하지 않는 기존의 OpenSSH를 삭제해야 합니다. OpenSSH를 삭제하게 되므로  원격지에서 작업할 때엔 반드시 telnet과 같은 예비 접속 통로를 미리 마련해 놓고 작업을 시작하도록 합니다.
 OpenSSH를 삭제하기 전에 telnet으로 원격 접속 수단을 먼저 마련합니다.
 telnet-server가 설치되어 있는지 확인합니다.
[root@localhost /usr/local/src]# rpm -qa | grep telnet-server

 없으면 설치하고 xinetd 수퍼데몬으로 구동, iptables에 tcp 23번 포트를 열어줍니다.
[root@localhost /usr/local/src]# yum -y install telnet-server
[root@localhost /usr/local/src]# cat /etc/xinetd.d/telnet
# default: on
# description: The telnet server serves telnet sessions; it uses \
#       unencrypted username/password pairs for authentication.
service telnet
{
        flags           = REUSE
        socket_type     = stream
        wait            = no
        user            = root
        server          = /usr/sbin/in.telnetd
        log_on_failure  += USERID
        disable         = no       <- 기존 yes를 no로 수정합니다.
}
[root@localhost /usr/local/src]# /etc/init.d/xinetd restart
xinetd 를 정지 중:                                         [  OK  ]
xinetd (을)를 시작 중:                                     [  OK  ]
[root@localhost /usr/local/src]# netstat -ntlp | grep xinetd
tcp        0      0 0.0.0.0:23                  0.0.0.0:*                   LISTEN      1685/xinetd
[root@localhost ~]#iptables -I INPUT -p tcp --dport 23 -j ACCEPT
[root@localhost /usr/local/src]# iptables -nL | grep 23
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:23
[root@localhost /usr/local/src]#

 이제 telnet으로 접속한 뒤 기존의 OpenSSH를 삭제합니다.
[root@localhost ~]# yum -y remove openssh


2. OpenSSH 5.x 설치

 이제 새로운 OpenSSH를 설치해 보도록 하겠습니다.
소스컴파일로 설치하는 방법은 인터넷에 수두룩하니 여기선 rpm으로 설치하는 방법을 다루겠습니다.
srpm 파일을 받습니다. CentOS 5.x의 경우 2009년 11월 5일 현재 미러서버에서 아직까지도 5.x 버전의 rpm을 제공하지 않고 있군요. 하는 수 없이 페도라용 srpm을 받아 CentOS용으로 리빌드하여 설치해 보겠습니다.
다행히 페도라10의 OpenSSH 5.1p1-4 srpm은 CentOS 5.4와 라이브러리 충돌 없이 궁합이 맞는군요.
[root@localhost ~]# wget ftp://rpmfind.net/linux/fedora/updates/testing/10/SRPMS/openssh-5.1p1-4.fc10.src.rpm
[root@localhost ~]# rpm -ivh openssh-5.1p1-4.fc10.src.rpm
[root@localhost ~]# cd /usr/src/redhat/SPECS/
[root@localhost SPECS]# ls -lh
합계 108K
-rw-r--r-- 1 mockbuild mockbuild  24K 11월  2 19:31 dovecot.spec
-rw-r--r-- 1 mockbuild mockbuild  17K  5월 19 21:22 iptables.spec
-rw-r--r-- 1 root      root         0 10월 30 00:02 kernel.spec
-rw-r--r-- 1 mockbuild mockbuild  50K  6월 30 20:50 openssh.spec
-rw-rw-r-- 1 root      root      6.6K  5월 19  2008 pptp.spec
[root@localhost SPECS]#

 리빌드해 보니 아래와 같이 의존성 에러를 내뿜는군요.
[root@localhost SPECS]# rpmbuild -ba openssh.spec
오류: Failed build dependencies:
        gtk2-devel is needed by openssh-5.1p1-4.i386
        audit-libs-devel is needed by openssh-5.1p1-4.i386
        tcp_wrappers-devel is needed by openssh-5.1p1-4.i386
        libedit-devel is needed by openssh-5.1p1-4.i386
[root@localhost SPECS]#

 위 의존성 에러 결과는 시스템에 따라 다를 수 있습니다. 어쨌든 gtk2-devel, audit-libs, tcp_wrappers-devel, libedit-devel 패키지를 yum으로 설치합니다.
 tcp_wrappers-devel, libedit-devel는 yum으로 설치 안 되는군요. 어쩔 수 없이 spec 파일을 직접 수정하여 관련된 설정 부분을 주석처리하거나 삭제하도록 합니다.
 vi 편집기로 tcp_wrappers-devel, libedit-devel 라는 문자열이 들어가는 부분을 모두 삭제하고 페도라10에 포함된 pam-1.0.1-3 버전을 찾는 문제를 해결하기 위해 CentOS 5.x의 pam-0.99.6.2을 찾도록 수정한 spec 파일 견본을 첨부합니다. spec 파일 편집이 어려운 분들은 그대로 가져다 쓰시면 되겠습니다.



 
 빌드하고 /usr/src/redhat/RPMS/x86_64 디렉토리 안으로 들어가면(32bit의 경우 /usr/src/redhat/RPMS/i386) rpm 파일이 생성되어 있는 것을 확인할 수 있습니다.
[root@localhost SPECS]# rpmbuild -ba openssh.spec
[root@localhost /usr/src/redhat/SPECS]# cd /usr/src/redhat/RPMS/x86_64/
[root@localhost /usr/src/redhat/RPMS/x86_64]# ls -lh
합계 8920
drwxr-xr-x 2 root root    4096 11월  5 09:29 ./
drwxr-xr-x 4 root root    4096  9월  4 09:25 ../
-rw-r--r-- 1 root root 1765164 11월  2 19:14 dovecot-1.0.7-7.x86_64.rpm
-rw-r--r-- 1 root root 4037311 11월  2 19:14 dovecot-debuginfo-1.0.7-7.x86_64.rpm
-rw-r--r-- 1 root root  316357 11월  5 09:29 openssh-5.1p1-4.x86_64.rpm
-rw-r--r-- 1 root root   41130 11월  5 09:29 openssh-askpass-5.1p1-4.x86_64.rpm
-rw-r--r-- 1 root root  510589 11월  5 09:29 openssh-clients-5.1p1-4.x86_64.rpm
-rw-r--r-- 1 root root 2100357 11월  5 09:29 openssh-debuginfo-5.1p1-4.x86_64.rpm
-rw-r--r-- 1 root root  307443 11월  5 09:29 openssh-server-5.1p1-4.x86_64.rpm

[root@localhost /usr/src/redhat/RPMS/x86_64]#

설치합니다.
[root@localhost /usr/src/redhat/RPMS/x86_64]# rpm -Uvh openssh-*


3. chroot 설정을 위한 /etc/ssh/sshd_config 수정

 /etc/ssh/sshd_config 파일 최하단에 아래 설정을 추가하거나 기존 설정을 수정하고 sshd를 시작합니다.
Subsystem sftp  internal-sftp
Match Group chrootgroup
ChrootDirectory /chroot
※ Match Group과 ChrootDirectory 옵션의 순서가 서로 뒤바뀌지 않도록 합니다.

위 설정에 의하면 시스템 최상위 경로에 /chroot 라는 디렉토리를 만들어 일반 계정의 쉘환경을 /chroot 에 국한시키는 겁니다. /chroot 디렉토리 하단에 bin, dev, usr, etc 등의 디렉토리를 동일하게 복사해 집어 넣고 home 디렉토리도 만들어 넣어 기존 home 사용자들은 /chroot/home에 위치시키면 됩니다.
그러나 /chroot/home으로 몽땅 이동시키려면 아파치 버추얼호스트 설정도 모두 변경해 주어야 하고 /etc/passwd 파일에 기록된 계정 홈디렉토리 정보도 모두 수정해야 하는 번거로움이 있기 때문에 아래와 같은 방법으로 간단하게 해결할 수 있습니다.

 기존 /home 디렉토리를 /chroot/home 디렉토리에 바인딩 마운트시키면 간단하게 해결됩니다.
[root@localhost ~]# mkdir -p /chroot/home
[root@localhost ~]# mount --bind /home /chroot/home


 시스템 시작시 자동으로 마운트되도록 /etc/rc.d/rc.local 파일에 마운트 커맨드를 추가해 줍니다.
[root@localhost ~]# echo "mount --bind /home /chroot/home" >> /etc/rc.d/rc.local


 이제 chroot를 적용할 시스템 계정의 그룹 소유권을 chrootgroup으로 변경합니다. chrootgroup이라는 이름의 그룹은 아까 /etc/ssh/sshd_config 파일에 추가한 Match Group 옵션에 지정해 준 그룹명으로 지정하는 것입니다.
 /etc/passwd 파일을 열어 GID 부분을 chrootgroup 그룹의 GID로 수정하면 됩니다. asdf 라는 계정이 있다고 가정하고 작업해 보겠습니다.
[root@localhost ~]# groupadd -g 499 chrootgroup
[root@localhost ~]# cat /etc/passwd | grep asdf
asdf:x:998:499::/asdf:/bin/bash
[root@localhost ~]#



4. 시스템 바이너리, 라이브러리 및 환경설정 파일 복사

 이제 제한된 쉘에서 사용을 허용할 시스템 커맨드 세팅을 위해 앞서 언급했던 /chroot 디렉토리 하단에 bin, dev, usr, etc 관련 시스템 디렉토리 및 파일을 복사해 넣는 작업을 해주면 됩니다.
[root@localhost ~]# cd /chroot
[root@localhost ~]# mkdir bin dev etc lib tmp usr


 우선 null, zero와 같은 특수파일을 만들어 줍니다.
[root@localhost ~]# mknod dev/null c 1 3
[root@localhost ~]# mknod dev/zero c 1 5


 실행 바이너리와 이에 연관된 각종 라이브러리를 복사해 넣어줍니다. 작업 요령은 간단합니다.
 우선 시스템 커맨드 중 하나인 cp를 예로 들어보겠습니다.
 cp 커맨드의 절대 경로는 아래와 같네요.
[root@localhost ~]# which cp
/bin/cp
[root@localhost ~]#


 ldd 커맨드로 바이너리 실행에 필요한 라이브러리를 파악할 수 있습니다.
[root@localhost ~]# ldd /bin/cp
        linux-gate.so.1 =>  (0xb7fa8000)
        libacl.so.1 => /lib/libacl.so.1 (0x48d0d000)
        libselinux.so.1 => /lib/libselinux.so.1 (0x48968000)
        libc.so.6 => /lib/libc.so.6 (0x477c0000)
        libattr.so.1 => /lib/libattr.so.1 (0x48d06000)
        libdl.so.2 => /lib/libdl.so.2 (0x47906000)
        libsepol.so.1 => /lib/libsepol.so.1 (0x48982000)
        /lib/ld-linux.so.2 (0x477a2000)
[root@localhost ~]#
 위에 출력된 내용을 참고하여 관련 라이브러리 원본 및 심볼릭링크 파일을 복사해 넣어주면 됩니다.
/lib/libacl.so.1 은 /chroot/lib/libacl.so.1 로 복사하면 되는 것이죠.


5. 실서비스를 위한 세부 설정

 그러나 여기까지 작업을 마쳐도 실질적으로 chroot 환경의 ssh 서비스를 하기에는 미흡합니다.

 지금까지의 설정만으로는 제한된 쉘을 이용하는 일반 사용자들이 /chroot 로 가두어진 쉘을 이용한다 하더라도 다른 계정 사용자들의 홈디렉토리로는 마음대로 이동이 가능하다는 단점이 남게 됩니다. 이는 chroot 설정으로 가두어진 쉘을 이용하는 모든 계정들을 하나의 그룹으로 묶고 각 계정 홈디렉토리의 그룹 퍼미션을 0으로 만들면 해결할 수 있습니다.
 그러므로 /etc/ssh/sshd_config 파일에 정의했던 Match Group chrootgroup 옵션에 따라 /chroot/home 안의 사용자들은 모두 chrootgroup에 속하게 하고 각 계정 홈디렉토리 안의 모든 디렉토리와 파일의 그룹 퍼미션을 0으로 만들면 되겠죠. 이렇게 하면 chrootgroup 그룹에 속한 모든 계정은 오직 자신의 계정 디렉토리 내용만 읽기 및 쓰기, 실행이 가능하고 타 계정 사용자의 홈디렉토리는 접근 자체가 불가능하게 됩니다.

 우선 /chroot/home 하단의 모든 디렉토리 및 파일의 그룹 소유권을 chrootgroup으로 변경합니다. 아파치 웹서버의 경우 웹게시판 등을 통해 웹상에서 첨부파일로 업로드된 데이터나 nobody 소유권이어야 정상 작동하는 웹소스의 경우 nobody 소유권을 그대로 유지하여야 하므로 무작정 일괄 변경해서는 안됩니다.
[root@localhost ~]# find /chroot/home \( ! -user nobody -o ! -group nobody \) -exec chown .chrootgroup {} \;
 그리고 아래와 같이 명령을 내려 /chroot/home 하단의 chrootgroup에 속한 모든 계정의 디렉토리와 파일의 그룹퍼미션을 0으로 만듭니다.
[root@localhost ~]# find /chroot/home -group chrootgroup -exec chmod g-w-r-x {} \;

그렇게 해서 설정한 /chroot 하단의 디렉토리 및 파일의 소유권 및 퍼미션 구성도를 정리하면 아래와 같습니다.
[root@localhost /]# ls -lh / | grep chroot
drwx--x--x  9 root chrootgroup 4.0K 11월  6 01:07 chroot
drwx--x--x  3 root chrootgroup 4.0K 11월  6 01:15 home
[root@localhost /]# ls -lh /chroot
drwx--x--x 2 root root        4.0K  7월 30 12:43 bin
drwx--x--x 2 root root        4.0K  6월  3 18:07 dev
drwx--x--x 4 root root        4.0K 11월  6 01:25 etc
drwx--x--x 3 root root        4.0K 11월  6 01:15 home
drwx--x--x 2 root root        4.0K  7월 28 11:20 lib
drwxrwxrwt 4 root root        1.0K 11월  6 01:14 tmp
drwx--x--x 9 root root        4.0K  6월  8 01:56 usr
[root@localhost /]# ls -lh /chroot/home
drwx---r-x 3 one chrootgroup 4.0K 11월  6 01:22 one
drwx---r-x 3 two chrootgroup 4.0K 11월  6 01:22 two
drwx---r-x 3 three chrootgroup 4.0K 11월  6 01:22 three
[root@localhost /]#


 이제 타 계정 홈디렉토리로 이동하려 하면 퍼미션 금지 에러가 뜨는 것을 확인할 수 있을 것입니다.

 ※ 위에 소개한 소유권/퍼미션 일괄 변경 방법은 시스템에 따라 돌이킬 수 없는 사태를 야기할 수 있으므로 맹목적으로 무작정 따라하지 마시길 바랍니다. 될 수 있으면 find 커맨드 바로 뒤의 경로 설정을 최소한도로 국한시켜가며 차근차근 테스트하며 진행하시길 바랍니다.

 이제 root가 아닌 일반계정으로 ssh에 접속하여 시스템 최상위 경로로 이동해 보면 /가 아닌 /chroot 경로에 가두어진 쉘환경임을 확인할 수 있습니다.

 그리고 기존의 데이터 뿐만이 아닌 앞으로 쉘상에서 새롭게 생성할 계정도 그룹 퍼미션 환경을 0으로 만들어줄 필요가 있겠죠.
 새롭게 생성할 계정 GID를 모두 chrootgroup으로 지정하고 UMASK 설정을 통해 그룹퍼미션을 제거합니다.
[root@localhost ~]# cat /etc/default/useradd
# useradd defaults file
GROUP=499
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes
[root@localhost ~]# cat /etc/login.defs  | grep UMASK
UMASK           072


 이젠 일반사용자들이 쉘에 접속했을 때 처음 만나게 되는 프롬프트 모양을 결정지으며 원활한 커맨드 및 편집기 사용을 위한 일반사용자 전용의 쉘 환경변수를 새롭게 정의해 줄 필요가 있습니다. chroot 환경의 쉘에 접속하는 일반 사용자는 /chroot/etc/bashrc 파일의 설정을 지배받게 되므로 이를 적절히 수정하면 됩니다. /etc/bashrc 파일을 복사해와 편집기로 열어 상단 부분을 한 번 보겠습니다.
[root@localhost ~]# cp /etc/bashrc /chroot/etc/
[root@localhost ~]# head -n 13 /chroot/etc/bashrc
# /etc/bashrc
# System wide functions and aliases
# Environment stuff goes in /etc/profile
# By default, we want this to get set.
# Even for non-interactive, non-login shells.
if [ $UID -gt 99 ] && [ "`id -gn`" = "`id -un`" ]; then
        umask 002
else
        umask 022
fi

 붉은색으로 표시한 스크립트 조건문에 의하면 UID가 99보다 크면서 UID와 GID 값이 같은 경우 umask 002를 적용하고 그 외의 경우는 022를 적용하라는 의미입니다.
 /etc/passwd 파일에서 UID가 99인 사용자를 확인하면 nobody이죠.
 nobody보다 UID가 크면서 UID와 GID값이 같은 계정은 useradd 커맨드로 생성하는 /home 아래의 일반사용자들의 경우가 대표적입니다 결국 root나 일반계정으로 bash 쉘에 로그인 후 디렉토리나 파일을 생성하면 별다른 이유가 없는 한 umask 002를 적용하게 되는 것입니다.

 그러나 본 문서의 ssh chroot 적용 방법이라면 /etc/passwd, /etc/group, /etc/shadow 파일을 /chroot/etc 안에 복사해 넣을 필요가 없고 쉘상에서 UID나 GID를 확인하는 데에 관계되는 커맨드나 환경설정 파일도 없기 때문에 /chroot/etc/bashrc 파일 상에서의 위 설정 내용은 수정이 필요합니다.

 답은 간단합니다. 위 붉은색 부분을 삭제하고 아래 한 줄로 대신하면 됩니다.
umask 072
 어차피 chrootgroup으로 묶은 일반사용자들만 chroot로 제한된 쉘에 접속할 것이기 때문에 쉘스크립트의 조건문 등으로 예외 상황을 가정할 필요 없이 위의 단 한 줄 만으로 umask를 선언하면 되는 것입니다.
 
 
 하단부의 불필요한 부분도 모두 제거하여 최적화시킨 bashrc 파일의 내용은 아래와 같습니다.
[root@localhost:/chroot/etc]# cat /chroot/etc/bashrc
# /etc/bashrc
# System wide functions and aliases
# Environment stuff goes in /etc/profile
# By default, we want this to get set.
# Even for non-interactive, non-login shells.
        umask 072
# vim:ts=4:sw=4
alias vi='vim'
alias grep='grep --color=auto'
alias ll='ls -alhF --color=tty --show-control-chars'
alias mysqldump='/usr/local/mysql/bin/mysqldump'
alias mysql='/usr/local/mysql/bin/mysql'
PS1="\[\033[1;31m\][SSH:\w]$ \[\033[0;37m\]"
붉은색으로 굵게 표시한 부분은 프롬프트 형태를 결정짓는 변수값으로 딱히 정해진 방법이 있는게 아니니 입맛에 맞게 수정해서 사용하면 됩니다.


ssh 로그인 시 뜰 안내문은 /chroot/etc/motd 파일로 설정하면 됩니다.
[root@localhost:/chroot/etc]# cat motd
▒▒▒▒▒  Welcom to foobar.com !!! ▒▒▒▒▒
지금 접속하신 쉘은 실제 사용할 수 있는 커맨드가 대폭 제한된 환경입니다.
디렉토리 생성 및 삭제, 계정 백업 및 복구를 위한 기능에만 초점이 맞추어진
환경이므로 리눅스/유닉스 학습을 위한 수단으로는 사용이 사실상 불가능합니다.
※ 사용 가능 커맨드
cat chmod cp grep gunzip gzip ls ll mkdir mv mysql mysqldump rm tar vi zip unzip pwd

 
 mysql을 운영하는 경우 일부 mysql 쉘커맨드 중 /tmp/mysql.sock을 참고하게 되는데 chroot 환경의 쉘에서도 사용하려면 /chroot/tmp에도 mysql.sock를 위치시켜야 합니다. 바인딩 마운트로 해결 가능합니다.
[root@localhost:~]# chmod 1777 /chroot/tmp
[root@localhost:~]# mount --bind /tmp /chroot/tmp


시스템 시작시 자동으로 적용되도록 /etc/rc.d/rc.local 파일에 커맨드를 추가합니다.
[root@localhost:~]# echo "mount --bind /tmp /chroot/tmp" >> /etc/rc.d/rc.local


이렇게 설정한 각종 시스템 디렉토리 및 파일들의 변조를 원천 봉쇄하기 위해 chattr 커맨드로 수정/복사/삭제 속성을 제거합니다. 이렇게 하면 그나마 chroot 환경의 쉘에 들어와서 뻘짓을 감행하려는 시도조차 불가능해집니다.
[root@localhost:~]# chattr +i -R /chroot/lib
[root@localhost:~]# chattr +i -R /chroot/usr
[root@localhost:~]# chattr +i -R /chroot/etc
[root@localhost:~]# chattr +i -R /chroot/bin
[root@localhost:~]# chattr +i -R /chroot/dev



 이 외에도 세밀하게 설정해야 할 부분들이 여럿 있을 수 있습니다. 특히 vi 편집기와 관련된 환경설정파일과 라이브러리와 관련된 디렉토리 및 파일 설정이 처음 시도하는 분들께는 다소 까다로운 작업이 될 수 있겠습니다. 검색 신공만이 답이겠죠. 저 같은 경우는 chroot 설정을 해보면서 쉘을 구성하는 환경설정 파일들에 대해 좀 더 깊은 이해를 할 수 있었습니다. 쉘스크립트에 대해서도 기초적인 이해의 필요성을 새삼 깨닫게 해준 보람된 작업이었다고나 할까요? 암튼 이 문서가 chroot 설정을 시도하려는 분들께 조금이나마 도움이 되었음 하는 바입니다.