'telnet'에 해당되는 글 1건

  1. 2007/07/23 리눅스 서버 보안 설정
2007/07/23 15:59

리눅스 서버 보안 설정

작은 회사다보니 본업인 개발 뿐 아니라 서버 관리도 제가 맡아 하고 있습니다.
최근에 서버 하나의 OS가 워낙 오래 되어(리눅스 커널 2.4) 우분투 서버 에디션으로 새로 설치하면서 보안 설정도 새롭게 하게 되어 이 기회를 빌어 리눅스 서버 보안에 대한 내용을 간략히 정리할까 합니다.


1. ssh 사용

서버 관리를 위한 인터페이스를 따로 지원하지 않거나(예를 들면 웹기반 admin 페이지나 관리자 툴 같은 것) 개발용으로 쓰는 리눅스 서버라면 터미널 접속은 필수적일 것입니다.
하지만 터미널은 쉘을 제공한다는 점에서 다른 서비스에 비해 양날의 검이기도 합니다. 프로그램 버그를 이용한 해킹 기법들은 쉘을 실행시키는 것이 그 목적인만큼 쉘이 허용되면 뚫릴 가능성은 아주 높아집니다.

이를 위해 가장 많이 쓰는 것은 telnet과 ssh라고 할 수 있습니다. rsh는 요즘 잘 안 쓰죠... rsh나 telnet이나 위험하기는 도토리 키재기입니다만 telnet은 아직도 많이 쓰이고 있죠. rsh나 ssh에 비해서 설정하기가 쉬워서 그런지...

하지만 telnet을 통한 접속은 암호화 되지 않은 패킷이 네트웍을 통해 돌아다니게 되므로 맘만 먹으면 쉽게 뚫리게 됩니다. telnet을 통해 패스워드를 입력할 때는 화면에 보이지 않으므로 보안에 의심을 가지지 않을 수도 있지만, 패킷 모니터링 툴로 telnet 패킷들을 검사하면 id와 패스워드가 '그대로' 보이는 것을 확인할 수 있습니다. 접속 후의 입출력도 마찬가지고요.
ssh는 패킷을 보내기 전에 암호화 시키므로 패킷 자체를 캡춰하여 암호화된 데이터를 풀어내는 것은 수학적으로 불가능에 가깝습니다.

하지만 이 또한 사용자의 부주의에 의해 뚫릴 가능성은 있습니다.
실례로 작년에 회사 서버가 침입 당한 적이 있는데 원인은 직원 한 명이 외부 협력 회사에 계정을 제공한답시고 guest/guest로 계정을 만들어 놓은 탓이었습니다. 이런 식이라면 백날 ssh나 보안 설정 같은 거 해봤자 필요가 없지요 -_-;;

잠시 딴 얘기... 해킹 당한 걸 어떻게 알았냐면... 저는 그 서버를 잘 안 쓰는데 다른 분이 'ps가 안돼요'라고 해서 접속해 봤더니 ps 명령 실행파일이 지워져 있더군요. 그냥 그러려니 할 수도 있었지만 의심스러워서 ps 파일을 다른 서버에서 카피해 놓고 프로세스 목록을 유심히 살펴 보았더니 의심스러운 데몬이 하나 떠 있더군요 -_-; 로그를 보고 root 접속 시간 등을 역추적한 결과 guest 계정에서 뚫고 들어왔음을 확인했습니다. 그래서 회사 전체에 guest 계정 히스토리에 대해 알고 보니 패스워드도 guest였던 겁니다.

따지고 보면 제대로 된 해커라고 할 수도 없지요. 이런 류의 해커들은 ssh나 텔넷 포트가 열려 있는 서버를 찾은 뒤에 프로그램을 돌려서 계정별로 사전 공격을 해보는게 전부입니다. 그러다가 guest/guest같은 계정이 걸리면 접속 한 뒤에 서버를 놀이터 삼는 거지요. 그 때는 커널이 2.4.18이었기 때문에(버그가 있는 걸로 알고 있습니다) 쉽게 root 권한을 획득했을테고 루트킷 데몬을 띄워놓고는 혹시 들킬까봐 ps를 지운거지요(제가 보기엔 이게 더 바보짓이었던 듯...).


이런 문제를 막기 위해서 패스워드 방식이 아닌 인증키 방식으로 ssh를 운영하는 것도 추천할만 합니다.
이는 인터넷 뱅킹의 공인인증서처럼 개인 키 파일을 가진 사용자만 접속이 가능한 방식입니다. 패스워드를 알아도 개인 키 파일이 없다면 접속이 불가능합니다.

이에 대해서는 다음 링크를 참조하세요.

http://wiki.kldp.org/wiki.php/DocbookSgml/SSH-KLDP

물론 보안이 향상되는 만큼 불편함도 있습니다만... 저희 회사는 제품 소스트리가 저장되는 소스 서버에 한해서만 인증키 방식을 쓰고 있습니다. 이 서버를 쓰는 사람도 제한되어 있기 때문에 중간적인 방식인 셈입니다.


2. iptables

리눅스를 위한 방화벽이라고 할 수 있는 것이 iptables입니다.
방화벽이라 하면 해킹 시도를 알아서 막아주는 인공지능 프로그램으로 이해하는 경우도 있는데 그런 거 아닙니다 ㅡ.ㅡ;; 최소한의 서비스를 제외하고는 원천적으로 막는 것이 오히려 더 목적에 가깝다고 볼 수 있습니다.

iptables는 관리자가 입력한 룰에 따라 서버로 들어오거나 나가는 패킷을 어떻게 처리할 지를 결정합니다.

저희 회사 같은 경우 외부에 물려 있는 리눅스 서버(A)가 하나 있고, 내부에 개발 서버와 웹 서버(B)가 있는 식입니다.
외부에서 직접 접속 가능한 서버는 A 한대지만 웹에도 접속해야 하므로 A에 80포트(http 포트)로 접속할 때는 해당 패킷을 B서버로 포워딩 해주는 것이 필요합니다.

그리고 ssh와 http 포트를 제외하고는 외부에서 오는 패킷은 모두 막도록 되어 있습니다.
즉, 내부에서는 편리를 위해 ftp나 telnet을 쓸 수 있지만 외부에서 telnet이나 ftp로 오는 접속은 차단하게 되는 것입니다. 그리고 관리자가 어떤 포트가 열려 있는지 일일이 확인하기도 힘들기 때문에 자기가 모르는 데몬이 떠 있더라도 방어가 힘드므로 iptables를 사용하는 것이 좋습니다. 예를 들면 리눅스 설치시 mysql이 기본으로 설치되어서 데몬이 뜨는 바람에 외부에서 mysql 취약점을 이용해 공격한다든가 하는 일이 생길 수도 있지요.

다음은 좀 어설프긴 하지만 외부 서버 A의 iptables 설정 내용입니다.

#!/bin/bash


# 이 부분을 해 주지 않으면 패킷 포워딩 자체가 불가능합니다
# (그리고 직접 커널을 커스텀 컴파일해 쓴다면 커널 컴파일시 관련 옵션이 켜져 있어야 합니다)
echo "1" > /proc/sys/net/ipv4/ip_forward


# 설정된 모든 룰을 날려버립니다
iptables -F
iptables -t nat -F

# 기본 정책을 설정합니다. 일단 무조건 받아들입니다.
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -t nat -P PREROUTING ACCEPT
iptables -t nat -P POSTROUTING ACCEPT


# 80 포트로 들어오는 1.1.1.1(외부 ip)에 대한 패킷을 웹 서버인 192.168.0.100으로 보냅니다.
iptables -t nat -A PREROUTING -p tcp -d 1.1.1.1 --dport 80 -j DNAT --to 192.168.0.100:80

# 보내 준 웹 패킷에 대해 192.168.0.100이 응답 패킷을 보내게 되므로 그에 대한 처리를 해줍니다.
iptables -t nat -A POSTROUTING -d 192.168.0.100 -p tcp --dport 80 -j MASQUERADE


# RELATED나 ESTABLISHED에 대해서만 접속을 허용합니다.
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT


# ssh 포트인 22번에 대해 외부에서 들어오는 패킷은 모두 받아들입니다(eth1이 외부 접속 랜카드)
iptables -A INPUT -i eth1 -p tcp --destination-port 22 -j ACCEPT


# 이외의 eth1에 대한 패킷은 모두 버립니다.
iptables -A INPUT -i eth1 -p udp -j DROP
iptables -A INPUT -i eth1 -p tcp -m tcp --syn -j DROP


# 내부에서 오는 패킷(eth0)과 localhost에서 오는 패킷은 모두 받아들입니다.
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -i eth0 -j ACCEPT


위와 같이 설정하면 내부 네트웍에서 오는 접속은 모두 받아들이지만, 외부에서 오는 접속은 22번 포트에 대해서만 허용하며 80포트에 대한 것은 웹 서버로 포워딩하게 됩니다.



3. fail2ban

마지막으로 fail2ban에 관한 것입니다.

위처럼 하더라도 22번 포트는 열어야 되므로 ssh에 대한 공격 자체는 피할 수가 없습니다. ssh 포트를 바꿔 버리는 것도 효과적이지만 이렇게 되면 다른 사람들이 불편하고...-_-;;

그리고 외부에 접속된 서버는 공격 시도가 늘 있습니다. 외부 서버의 auth.log의 일부입니다.

Jul 22 10:51:36 rubicon sshd[7181]: Invalid user adam from 210.212.79.38
Jul 22 10:51:36 rubicon sshd[7181]: (pam_unix) check pass; user unknown
Jul 22 10:51:36 rubicon sshd[7181]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=210.212.79.38
Jul 22 10:51:37 rubicon sshd[7181]: Failed password for invalid user adam from 210.212.79.38 port 59887 ssh2
Jul 22 10:51:40 rubicon sshd[7183]: Invalid user denise from 210.212.79.38
Jul 22 10:51:40 rubicon sshd[7183]: (pam_unix) check pass; user unknown
Jul 22 10:51:40 rubicon sshd[7183]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=210.212.79.38
Jul 22 10:51:42 rubicon sshd[7183]: Failed password for invalid user denise from 210.212.79.38 port 60133 ssh2
Jul 22 10:51:45 rubicon sshd[7185]: Invalid user denys from 210.212.79.38
Jul 22 10:51:45 rubicon sshd[7185]: (pam_unix) check pass; user unknown
Jul 22 10:51:45 rubicon sshd[7185]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=210.212.79.38
Jul 22 10:51:47 rubicon sshd[7185]: Failed password for invalid user denys from 210.212.79.38 port 60402 ssh2
Jul 22 10:51:50 rubicon sshd[7187]: Invalid user dolores from 210.212.79.38
Jul 22 10:51:50 rubicon sshd[7187]: (pam_unix) check pass; user unknown
Jul 22 10:51:50 rubicon sshd[7187]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=210.212.79.38
Jul 22 10:51:52 rubicon sshd[7187]: Failed password for invalid user dolores from 210.212.79.38 port 60687 ssh2
Jul 22 10:51:55 rubicon sshd[7189]: Invalid user donald from 210.212.79.38
Jul 22 10:51:55 rubicon sshd[7189]: (pam_unix) check pass; user unknown
Jul 22 10:51:55 rubicon sshd[7189]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=210.212.79.38
Jul 22 10:51:56 rubicon sshd[7189]: Failed password for invalid user donald from 210.212.79.38 port 60968 ssh2
Jul 22 10:51:59 rubicon sshd[7191]: Invalid user donovan from 210.212.79.38
Jul 22 10:51:59 rubicon sshd[7191]: (pam_unix) check pass; user unknown
Jul 22 10:51:59 rubicon sshd[7191]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=210.212.79.38
Jul 22 10:52:01 rubicon sshd[7191]: Failed password for invalid user donovan from 210.212.79.38 port 32971 ssh2
Jul 22 10:52:04 rubicon sshd[7193]: Invalid user dorin from 210.212.79.38
Jul 22 10:52:04 rubicon sshd[7193]: (pam_unix) check pass; user unknown
Jul 22 10:52:04 rubicon sshd[7193]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=210.212.79.38
Jul 22 10:52:05 rubicon sshd[7193]: Failed password for invalid user dorin from 210.212.79.38 port 33260 ssh2
Jul 22 10:52:08 rubicon sshd[7195]: Invalid user elite from 210.212.79.38
Jul 22 10:52:08 rubicon sshd[7195]: (pam_unix) check pass; user unknown
Jul 22 10:52:08 rubicon sshd[7195]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=210.212.79.38
Jul 22 10:52:10 rubicon sshd[7195]: Failed password for invalid user elite from 210.212.79.38 port 33499 ssh2


루비콘은 서버 이름입니다(제가 붙인 이름입니다 -_-).
보다시피 거의 2-3초에 한 번씩 유저 이름을 바꿔가며 접속 시도를 합니다. 아까 말했듯이 프로그램을 돌려서 접속 시도를 하는거지요. 뚫릴 가능성이 적다고 해도 신경은 매우 쓰이는게 사실입니다.

이런 또라이들을 -_- 막기 위해 fail2ban이란 프로그램이 있습니다. 파이썬으로 만들어진 간단한 데몬인데 ssh, ftp, mail 서버의 로그를 파싱하여 일정 횟수 이상 연속으로 실패하는 시도가 있다면 해당 ip를 iptables에 추가해 아예 막아버려 주는 편리한 프로그램입니다.

예를 들면 ssh의 경우 설정파에,

failregex = (?:Authentication failure|Failed [-/\w+]+) for(?: [iI](?:llegal|nvalid))? user .*(?: from|FROM) <HOST>
            ROOT LOGIN REFUSED .* FROM <HOST>
            [iI](?:llegal|nvalid) user .* from <HOST>
            Failed password for .* from <HOST> port [0-9]* ssh[12]
            Invalid user [a-z]* from <HOST>


위와 같은 식으로 접속 에러 메시지에 대해 정규 표현식으로 패턴을 작성해 놓으면 설정해 놓은 횟수가 지난 후 ip를 차단해 버립니다.

저 같은 경우 3회로 설정했는데 fail2ban 작동 이후에는 접속 시도에 대한 로그가 다음과 같이 남습니다.

Jul 23 04:08:32 rubicon sshd[8369]: Failed password for root from 202.101.187.102 port 38386 ssh2
Jul 23 04:08:36 rubicon sshd[8371]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=202.101.187.102  user=root
Jul 23 04:08:39 rubicon sshd[8371]: Failed password for root from 202.101.187.102 port 38752 ssh2
Jul 23 04:08:43 rubicon sshd[8373]: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=202.101.187.102  user=root
Jul 23 04:08:44 rubicon sshd[8373]: Failed password for root from 202.101.187.102 port 39039 ssh2
Jul 23 04:10:39 rubicon sshd[8373]: fatal: Timeout before authentication for 202.101.187.102


마지막에 Timeout 메시지를 볼 수 있습니다. 이 경우에는 아예 해킹 시도하는 쪽에서 ssh 포트가 닫힌 것으로 보이므로 더 이상 공격 시도를 하지 않는 듯합니다. 자세한 사용법은 위에 링크한 홈페이지를 참조하시기 바랍니다. 아니면 저에게 뻐꾸기를...-_-;


이상으로 간략히 리눅스 보안설정에 대한 글을 접습니다.
말 그대로 기술적인 부분을 간략하게 적었으나 위에 썼던 guest/guest 사건처럼 -_- 사용자의 기본적인 의식이 제일 중요하지 않을까요....라는 진부한 결론으로 끝맺습니다 ㅡㅡ;;

이올린에 북마크하기(0) 이올린에 추천하기(0)

'Programming Story' 카테고리의 다른 글

하드디스크 용량의 허와 실  (0) 2007/09/13
리눅스 서버 보안 설정  (0) 2007/07/23
Thread-safe(2)  (0) 2007/05/28
Thread-safe  (0) 2007/05/27
Trackback 0 Comment 0