국가별 접속 차단 방법
일반적으로 파이어월의 기본 정책은 허용할 것을 제외한 나머지는 모두 차단하는 것이다. 그런데 필요에 따라 국가별로 접속을 제한해야 할 필요가 있다. 이를테면 특정 국가에서 비정상적인 접속 요청이 들어온다거나, 국내에서만 telnet이나 ftp 접속을 허용해야 할 때는 이같은 국가별 접속 차단을 사용해야 한다. 하지만 국가별 IP 현황을 조사해 모든 IP 대역에 대해 일일이 접근 설정을 한다는 것은 불가능한 일이다. 하지만 리눅스 파이어월인 iptables를 이용하면 이같은 기능을 쉽게 구현할 수 있다.
먼저 ‘people.netfilter.org/peejix/geoip/database/’에서 geoipdb.bin과 geoipdb.idx 파일을 다운로드한다. 그리고 iptables가 설치된 파이어월에 ‘/var/geoip’ 디렉토리를 생성하고, 이 디렉토리에 두 파일을 옮긴다. 여기에서 geoipdb.bin은 국가별 IP 대역에 대한 바이너리 포맷의 데이터이고, geoipdb.idx는 인덱스 파일이다.
geoip 라이브러리가 ‘/var/geoip’ 디렉토리를 참조하기 때문에 디렉토리명은 반드시 정해진 명칭을 사용해야한다. 이제 iptables에서 geoip를 사용하기 위해 커널을 패치할 차례다.
먼저 고급 기능을 이용할 수 있도록 커널을 패치하는 p-o-m을 다운로드하면 되는데 ‘ftp.netfilter.org/pub/patch-o-matic-ng/snapshot/’에서 최신 버전을 다운로드받을 수 있다.
이제 p-o-m을 압축해제하고, 다음과 같이 실행한다.
# tar xfz patch-o-matic-ng-XXXXXX.tar.gz
# cd patch-o-matic-ng
# IPTABLES_DIR=/usr/srt/iptables KERNEL_DIR=/usr/srt/linux ./runme geoip
다음은 kernel 메뉴에서 geoip를 사용하도록 실행할 차례다. ‘make menuconfig’ 실행 화면에서 다음과 같이 선택해 들어간 상태에서 최종적으로 ‘[*] geoip match support’를 선택하면 된다.
Device Drivers
-> Networking support
-> Networking support
-> Networking options
-> Network packet filtering (replaces ipchains)
-> IP: Netfilter Configuration
-> [*] geoip match support
이후 iptables 역시 ‘ftp.netfilter.org/pub/iptables/’에서 최신 버전을 다운로드받아 설치한다.
커널 컴파일을 완료한 후 새로운 버전의 커널로 부팅하면 이제 iptables의 geoip를 사용할 준비가 된 것이다. 사용할 수 있는 옵션은 --srt-cc와 --dst-cc가 있는데, 각각 뒤에 국가 코드명을 넣으면 된다. 예를 들어 확인해 보자.
- 일본과 미국에서의 웹 접속을 차단하고 다른 곳에서의 접속은 허용할 때
iptables -A INPUT -p tcp --dport 80 -m geoip --srt-cc JP,US -j DROP
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
- 한국에서의 ftp만 허용하고 나머지 국가에서의 접속은 차단하고자 할 때
iptables -A INPUT -p tcp --dport 21 -m geoip --srt-cc KR -j ACCEPT
iptables -A INPUT -p tcp --dport 21 -j DROP
또는 iptables -A INPUT -p tcp --dport 21 -m geoip ! --srt-cc KR -j DROP
실제로 운영중인 서버에 설정한 결과 서버가 느려지거나 부하가 늘어나는 일은 없었다. 이를 적절히 활용한다면 다양한 응용이 가능할 것이다.
리눅스에서도 윈도우처럼 자동 업데이트하기
리눅스에 비해 윈도우의 장점중 하나는 업데이트가 쉽고 편리하다는 것이다. 특히 최근처럼 보안 문제가 이슈화되는 시기에 이는 더욱 민감한 문제가 아닐 수 없다.
더구나 윈도우의 경우 자동으로 현재의 환경을 체크해 업데이트할 항목을 바로 보여주지만, 리눅스 서버를 운영하다보면 어떤 패키지를 업데이트해야 할지도 모르겠고, 일일이 찾아서 업데이트하는 것 역시 여간 불편한 일이 아닐 수 없다.
특히 rpm 등 패키지 형태로 많이 사용하는 리눅스의 경우 업데이트 때문에 의존성 등 다른 문제가 발생하지 않을까 하는 막연한 두려움이 있는 것도 사실이다. 그러나 그 중에서도 가장 큰 문제는 리눅스 서버가 여러대가 있을 경우, 버전이 각기 다른 서버에 로그인해 버전 확인 후 일일이 패치하는 것은 시간과 인력이 많이 소요되는 일이라는 점이다.
그런데 이제 이 문제를 시원하게 해결해 줄 수 있는 솔루션이 나왔고 점차로 사용자 층도 넓어지고 있다.
이는 yum이라는 것으로, ‘Yellow dog Updater, Modified’의 약자로 미국의 듀크대학에서 파이썬(python) 언어로 개발되고 있다. yum에 대한 자세한 내용은 홈페이지(linux.duke.edu/projects/yum/)를 참고하기 바란다.
[그림1] yum 사이트
yum 사이트에서 제공되는 yum rpm 또는 소스 파일을 설치한 후 간단히 yum을 실행하면 현재 시스템에 설치돼 있는 rpm 버전 정보와 최신 업데이트 버전 정보를 비교한다. 좀 더 정확히 얘기하자면 rpm의 헤더 정보를 비교해 업데이트 여부를 확인하고 이에 따라 업데이트를 시작한다.
yum의 주 설정 파일인 /etc/yum.conf 파일에는 업데이트하는 서버의 목록이 지정돼 있는데, 여러 곳에서 업데이트 서버를 제공하고 있으므로 신뢰할 수 있으며, 빠른 서버를 직접 지정해 사용할 수 있다. 만약 수십 대 이상의 많은 서버를 운영하는 상황에서 해외 서버로부터 직접 업데이트하려면 속도 저하 등이 발생할 수 있으므로 자체적으로 yum 서버를 구축해 운영할 수도 있다. 필자의 회사에서도 yum 업데이트 서버를 한 대 운영하고 있어, 초기 리눅스 설치 후에는 반드시 yum을 실행해 자동 패치를 하고 있는데, CD로 설치 후 약 5분 정도만 실행하면 패치작업이 완료될 정도로 빠르다.
rpm 등으로 yum을 설치한 후 아무런 옵션 없이 yum을 실행하면 다음과 같이 현재 사용할 수 있는 옵션을 보여주는데, 일반적으로 update와 install을 많이 사용하므로 이 옵션 정도만 알고 있으면 된다.
# yum
Usage: yum [options]
Options:
-c [config file] - specify the config file to use
-e [error level] - set the error logging level
-d [debug level] - set the debugging level
-y answer yes to all questions
-t be tolerant about errors in package commands
-R [time in minutes] - set the max amount of time to randomly run in.
-C run from cache only - do not update the cache
--installroot=[path] - set the install root (default '/')
--version - output the version of yum
--exclude=some_pkg_name - packagename to exclude - you can use this more than once
--download-only - only download packages - do not run the transaction
-h, --help this screen
이 중에서 가장 많이 사용하는 것은 바로 ‘yum -y update’인데, 이를 실행하면 현재 시스템에 설치돼 있는 rpm의 헤더 정보와 업데이트 서버에 있는 헤더 정보를 비교해 최신 rpm으로 자동 패치를 시작한다. 여기에서 -y는 일일이 설치 여부를 묻지 않고 업데이트한다는 의미로 만약 -y를 지정하지 않으면 매번 업데이트할 패키지마다 업데이트할 것인지 yes or no로 묻게 된다. 따라서 ‘yum -y update’를 cron에 걸어두면 매 일정 시각마다 자동으로 업데이트하므로 일일이 패치를 하지 않아도 언제나 시스템을 최신 패치된 상태로 유지할 수 있다.
rpm 업그레이드나 설치시 무엇보다도 문제될 수 있는 것이 바로 의존성(dependency) 문제다. yum은 이 문제를 자동으로 해결하므로 의존성 관련 문제는 걱정하지 않아도 된다. 다음은 yum을 실행한 결과인데, 현재 모든 패키지가 업데이트돼 있다는 것을 알 수 있다.
# yum -y update
Gathering package information from servers
Server: Red Hat Linux 3 base
Server: Red Hat Linux 3 updates
Finding updated packages
Downloading needed headers
No Packages Available for Update
No actions to take
다음에서는 lsof라는 rpm이 설치돼 있지 않을 때, install을 실행해 자동으로 설치하는 예를 보여주고 있다.
# yum install lsof
Gathering package information from servers
Server: Red Hat Linux 3 base
Server: Red Hat Linux 3 updates
Finding updated packages
Downloading needed headers
Resolving dependencies
Dependencies resolved
I will do the following:
[install: lsof.i386]
Is this ok [y/N]: y
Calculating available disk space - this could take a bit
lsof 100 % done
Installed: lsof.i386
Transaction(s) Complete
특정 포트를 점유하는 프로그램 확인
외부에서 포트스캔 등을 통해 확인해 보면, http(80)나 ftp(21) 외에 본인도 모르는 포트가 사용중인 경우가 있다. 이를테면 tcp 5000번 포트가 열려있는 것을 확인하고 텔넷으로 접속할 경우 접속까지도 가능한 경우도 있다. 이같은 경우 이 포트를 점유하고 있는 프로세스가 무엇인지 알아볼 필요가 있다.
윈도우 시스템의 경우 전통적으로 fport.exe라는 프로그램을 이용하지만, 커맨드 방식이고 몇 가지 불편한 점이 있어 최근에는 GUI 방식이면서 프로세스 삭제 등도 제공되는 cports라는 프로그램이 자주 이용되고 있다. 이 프로그램은 'www.nirsoft.net/utils/cports.html'에서 자유롭게 다운받을 수 있으며, [그림 2]와 같이 현재의 프로세스와 연결상태, 특정 포트를 사용하는 프로세스 정보 등을 직관적으로 알 수 있게 보여준다. 또한 특정 프로세스의 상태도 보여주고, 오른쪽 마우스를 클릭하면 직접 프로세스를 죽일 수도 있다.
[그림2] cports 프로그램
반면 리눅스의 경우 다소 번거로운 것이 사실인데, fuser나 netstat 명령을 이용하면 확인할 수 있다. 이 명령어들은 대부분의 시스템에서 기본적으로 이용할 수 있는데, 혹 명령어가 실행되지 않으면 해당 패키지를 찾아 설치하면 된다.
- fuser
fuser는 특정한 파일이나 파일 시스템을 사용하고 있는 프로세스의 pid를 보여주는 명령어로 ‘fuser ?n tcp 10101’과 같이 실행하면 10101번 포트를 사용 중인 프로세스 id를 보여준다. 물론 udp 포트일 경우에는 ‘fuser ?n udp 53’과 같이 사용하면 된다.
이때 프로세스 id가 570번이라는 것을 확인했다면 pid 570번을 사용하는 프로세스를 확인해야 하는데, 이는 ‘ps aux’로 확인하거나 ‘ls ?la /proc/570/’의 결과 중 ‘exe->’가 가리키는 부분을 확인하면 된다. ‘exe->’가 가리키는 경로는 현재 실행 파일을 의미하며, ‘cmd->’는 실행 파일이 참조하는 파일이나 디렉토리의 경로를 알려준다.
만약 ‘exe -> /home/user1/hacking/hacking*’이라고 표시돼 있으면, 이 경로에 있는 파일이 10101번 포트를 바인딩하고 있는 프로세스라는 것을 확인할 수 있다.
그런데, 가끔 백도어 포트 등을 점검하다 보면 ‘ls -la /proc/pid’와 같이 확인했을 때 ‘exe -> /home/user1/hacking/hacking (deleted)’와 같이 보이는 경우가 있다. 실제로 ‘/home/user1/hacking/’ 디렉토리에 가서 확인해 보면 hacking이라는 파일이 없는데, 프로세스에는 떠 있게 된다. 이는 공격자가 hacking이라는 백도어 파일을 실행해 커널 메모리에 올린 후에는 해당 파일을 삭제해, 백도어로서 작동은 하지만(이미 커널 메모리에 올라가 있으므로) 흔적은 남기지 않기 위한 방법으로 많이 사용되는 방법이다. 이같은 경우 파일은 삭제됐어도 메모리에는 남아있기 때문에 ‘cp /proc/pid/exe /root/test’라고 실행하면 삭제된 원본 파일을 ‘/root/test’라는 파일로 복구할 수 있다.
- netstat
간단히 사용할 수 있는 다른 명령어로서 다음과 같이 터미널 상에서 netstat을 실행해 보는 방법도 있다. netstat는 많은 옵션을 제공하고 있는데, 현재 시스템에서 특정 포트를 점유해 소켓 데몬 형태로 대기 중인 서비스에 대한 사항은 listen의 의미인 -l 옵션을 주면된다. 더불어서 -p 옵션을 주면 해당 포트를 사용 중인 프로그램 이름도 확인할 수 있다.
[그림3] 리눅스에서..
[그림3]의 경우를 보면 좌측에 현재 열려있는 포트를 알 수 있으며, 우측에 있는 정보를 보면 해당 포트가 어떤 PID(Process ID)며 어떤 프로그램인지 확인할 수 있다. 만약 포트 2265/tcp을 점유하고 있는 프로세스 정보를 알고자 한다면 pid가 587이고 프로그램 이름이 osirisd인 것을 알 수 있는데, 좀 더 상세히 알고자 한다면 앞에서 살펴본 바와 같이 ‘ls -la /proc/pid’를 실행하면 된다.
그리고, 만약 특정 프로세스가 정상적인 것이 아니라면 ‘kill ?9 570’(570은 pid) 또는 ‘killall ?9 ?ev hacking’(hacking은 ps auxc 실행 시 보이는 프로세스 이름)으로 해당 프로세스를 강제로 죽이면 포트도 닫힌다.
DNS Zone 전송 차단하기
DNS zone 전송(transfer)은 마스터(master)와 슬레이브(slave) 간에 zone 파일을 동기화하기 위한 용도로 사용되는 기술이다. 그런데, 이 zone 전송 보안 설정을 제대로 하지 않아 심각한 보안문제를 유발하는 경우가 많다. 이로 인해서 직접 시스템이 다운되거나 장애가 유발되는 것은 아니지만, 문제는 중요한 정보를 외부에 유출할 가능성이 있다는 것이다.
특히 대규모 사이트 일수록 그 피해가 큰데, 앞에서 언급한 것처럼 zone 전송은 마스터와 슬레이브 간의 zone 파일 전송이므로 마스터와 슬레이브 사이에만 허용돼야 하지만, 실제로는 접근 통제가 거의 이뤄지지 않고 있다. 국내에서도 손꼽히는 한 대형 포털의 예를 들어 이의 문제점을 살펴보도록 하자.
① 먼저 whois 질의를 통해 해당 도메인의 DNS 정보를 확인한다.
whois xxxx.com
NS1.xxxx.COM 211.xx.xx.xx
NS2.xxxx.COM 211.xx.xx.xx
(정보를 일부 수정하였음)
② 해당 DNS 서버로 zone 전송을 시도해 본다. Zone 전송은 nslookup이나 host, dig 등으로 모두 확인할 수 있다. 여기서는 dig를 사용해 보겠다.
# dig @NS1.xxxx.COM xxxx.com axfr
; <<>> DiG 9.2.1 <<>> @NS1.xxxx.COM xxxx.com axfr
;; global options: printcmd
; Transfer failed.
참고로 이 명령어는 NS1.xxxx.COM DNS에 xxxx.com이라는 도메인에 대해 zone 전송을 한다는 의미다. 마스터 DNS인 NS1.xxxx.COM에 질의하면 이같이 전송이 실패했다는 에러 메시지(Transfer failed)가 표시된다. 이는 해당 IP에서의 zone 전송이 거부됐다는 메시지로 NS1.xxxx.COM에서 정상적으로 차단하고 있다는 것을 알 수 있다. 이번에는 슬레이브 DNS인 NS2.xxxx.COM에 질의를 해 봤다.
# dig @NS2.xxxx.COM xxxx.com axfr
xxxx.com. 1800 IN SOA ns1.xxxx.com. hostmaster.ns1.xxxx.com.
xxxx.com. 1800 IN MX 5 mail4.xxxx.com.
xxxx.com. 1800 IN MX 5 mail5.xxxx.com.
xxxx.com. 1800 IN MX 5 mail5.xxxx.com.
xxxx.com. 1800 IN MX 5 mail5.xxxx.com.
xxxx.com. 1800 IN A 211.xx.xx.xx
xxxx.com. 1800 IN A 211.xx.xx.xx
xxxx.com. 1800 IN NS ns1.xxxx.com.
xxxx.com. 1800 IN NS ns2.xxxx.com.
09.xxxx.com. 3000 IN A 211.115.xx.xx
100.xxxx.com. 1800 IN CNAME dic.xxxx.com.
account.xxxx.com. 1800 IN A 211.xx.xx.xxx
admin.xxxx.com. 1800 IN A 211.xx.xx.xx
......
확인 결과 이 도메인의 경우 800여 줄이 넘는 zone 정보가 확인됐고, 그 중에는 dev.xxx와 같이 개발 서버로 사용되는 것으로 추측되는 서버의 목록도 눈에 띄었다. 이는 대단한 해킹 기법도 아니고 정상적인 DNS zone 전송을 자체에서 제공되는 명령어로 실행해 본 것에 불과함에도, 이처럼 많은 정보를 제공하고 있는 것을 알 수 있으며, 이로 인해 불필요한 정보가 유출될 수 있다. 실제 많은 기업에서 외부에 노출돼 보이는 웹 서버나 메일 서버뿐 아니라 내부의 개발 서버, 인트라넷 등도 ‘xxxx.도메인명’ 등으로 사용하고 있는데, zone 전송을 시도해 보면 이 정보가 그대로 보이기 때문에 공격자 입장에서 굳이 해킹을 통해 알 필요 없이 내부의 시스템/네트워크 구조를 쉽게 파악할 수 있다.
또한 사용하고 있는 IP 대역 등도 노출돼 IDC 등에서 사용하는 IP 대역뿐만 아니라 사내의 IP 대역이나 파이어월 등 보안 장비의 IP까지도 노출될 수 있다.
그럼 zone 전송의 정보유출에 대해 어떻게 대응해야 하는지 알아보자. 너무나 당연한 이야기이지만 원칙에 충실하면 된다. bind DNS를 사용할 경우에는 ‘/etc/named.conf’에 다음과 같이 설정하면 된다.
- 마스터 서버에서의 설정
options {
allow-transfer {192.168.1.3; };
};
여기에서 192.168.1.3은 슬레이브 DNS의 IP이며 당연히 슬레이브에서의 zone 전송만 허용한다는 의미다.
- 슬레이브 서버에서의 설정
options {
allow-transfer {none; };
};
당연히 슬레이브에서는 zone 전송을 제공하지 않으므로 허용할 필요가 없다.
이같이 설정한 후 DNS를 재가동하면 외부에서의 불법적인 zone 전송은 거부한다.