2014년 11월 23일 일요일

실시간 120만개의 패킷분석 - OpenSOC 프로젝트

올해 들어서 블로그에 글 쓰기가 힘들어 졌는데, 오랜만에 포스팅을 해 보려고 합니다.  오늘의 주제는 오픈소스 기반의 시큐리티 분석 프레임웍인 OpenSOC 입니다. OpenSOC 는 여러 보안 문제의 이슈를 빅데이터라는 관점에서 접근해 볼 수 있어, 보안 침해 사고에 대한 조사, Anonmaly 탐지 등 이용범위는 사용방법에 따라 다양해 질 수 있습니다.  오픈소스 기반인 만큼 여러가지 오픈소스를 이용하고 있는데요 Storm, Kafka, Elasticsearch 와 같은 하둡의 에코시스템 MySQL 등을 사용하고 있습니다.  이를 통해 패킷캡쳐 인덱스, 저장, 데이터 스트리밍 처리, 배치 처리, 실시간 검색, 집계등을 이용할 수 있게 됩니다. 


프레임웍에서 제공하는 주요 기능은 아래와 같습니다.

  • Extensible spouts and parsers for attaching OpenSOC to monitor any telemetry source
  • Extensible enrichment framework for any telemetry stream
  • Anomaly detection and real-time rules-based alerts for any telemetry stream
  • Hadoop-backed storage for telemetry stream with a customizable retention time
  • Automated real-time indexin for telemetry streams backed by Elastic Search
  • Telemetry correlation and SQL query capability for data stored in Hadoop backed by Hive
  • ODBC/JDBC compatibility and integration with existing analytics tools

OpenSOC 를 사용하기 위해서 필요한 것은 다음과 같습니다.
  • 2 개의 네트워크 패킷 캡쳐 카드 (Napatech 사의 NT20E2-CAP 추천)
  • 아파치 Flume 1.4.0 +
  • 아파치 Kafka 0.8.1+
  • 아파치 Storm 0.9+
  • 아파치 하둡 2.X 
  • 아파치 하이브 12+ (13 권장)
  • 아파치 Hbase 0.94+
  • Elastic Search 1.1+
  • MySQL 5.6+
OpenSOC 컴포넌트는 크게 데이터처리를 위한 스트리밍 쪽과 실제 분석을 하기위한 UI 화면으로 나뉘어져 있습니다.  OpenSOC 를 구현하기 위해 테스트된 환경은 시스코에서 제공한 것으로 14개의 데이터 노드와 3개의 클러스트 제어 노드, 2개의 ESX 호스트, 라우터, 스위치등이 있습니다. 

과거에 제가 진행해 보았던 패킷 분석 프로젝트와 비슷한 개념이 되기도 하는데, 더욱 큰 관점에서 프레임웍이 만들어져 있다는 점에서 매력이 있습니다. 오픈소스로 공개되어 있는 만큼 사용도 자유롭고 실시간으로 120 만개의 패킷을 처리할 수 있다는 것은 성능면에서도 만족스러워 보입니다.  관련 프로젝트를 진행해 보신 분들은 아시겠지만 패킷분석 처리가 만만치는 않은 작업입니다. 제대로 구현하면 얻어낼 수 있는 데이터도 많지만 이 엄청난 패킷 데이터를 저장하고 처리하고 의미있는 데이터로 얻어내기까지는 상당히 많은 작업들이 필요합니다. 

이걸 오픈소스로 이용할 수 있다는 측면에서는 아주 매력적이죠. 물론, 다양한 오픈소스를 사용해야 하기 때문에 어느정도의 경험이 필요로 할듯 해 보입니다.  

더 상세한 정보는 다음 사이트에서 얻을 수 있습니다.


혹시 OpenSOC 로 구현을 진행하였거나 진행할 계획이 있으신 분들은 알려주세요~ 

//Rigel   



2014년 4월 9일 수요일

[긴급] OpenSSL Heartbleed 버그 취약점, 주의 필요

OpenSSL 라이브러리에서 중대한 보안 취약점이 발견되었습니다.  이 버그는 Heartbleed 로 불리고 있는데요, 이 취약점으로 인해 누구나 OpenSSL 취약점을 내포하고 있는 버전의 시스템으로부터 메모리 정보를 읽어들일 수 있습니다. 이 메모리 정보는 64K바이트 청크 형태로 읽어볼 수 있으며 이 정보로 부터 비밀키도 얻어낼 수 있습니다. 이 뜻은 이 비밀키를 이용하면 암호화된 정보를 볼 수 있다는 것이며 서비스와 사용자 사이에서 모든 정보를 엿볼수가 있게 됩니다.  공식적으로 이 버그는 CVE-2014-0160 입니다.

이번 취약점이 SSL/TLS 프로토콜상의 구조적인 문제는 아닙니다. OpenSSL 라이브러리의 취약점이며 다음과 같은 버전이 영향을 받습니다 :

- OpenSSL 1.0.1 - 1.0.1f 사이의 버전은 취약합니다.
- OpenSSL 1.0.1g 는 취약하지 않습니다.
- OpenSSL 1.0.0 관련 버전은 취약하지 않습니다.
- OpenSSL 0.9.8 관련 버전은 취약하지 않습니다.

현재 많은 버전들이 취약한 버전으로 이용되고 있는 것으로 판단되는 만큼 OpenSSL 을 사용중인 사용자라면 바로 버전 정보를 확인하여 패치하기를 권장합니다. 만약 지금 시점에서 새로운 버전으로 적용이 힘든경우라면 기존 버전을 다음 옵션으로 재 컴파일 해 사용하여 문제를 일시적으로 해결할 수 있습니다.

-DOPENSSL_NO_HEARTBEATS

또한 이 취약점의 또 다른 문제점은 로그상으로 어떤 추적할 만한 정보를 하나도 남기지 않습니다. 즉, 해당 취약점을 통해 악용되었는지 알기 힘들다는 점입니다.

현 시점에서 이 취약점을 이용한 공격이 많이 퍼지고 있는지는 확인하기 어렵지만 OpenSSL 사용자는 빠른 시일내에 패치할 것을 강력히 권고 합니다.

이 취약점의 세부정보는 다음 사이트에서 더 자세히 얻을 수 있습니다.

http://heartbleed.com/
https://www.openssl.org/news/secadv_20140407.txt

중요한 문제이다 보니 급하게 포스팅을 남깁니다.

[참고] OpenSSL 버전 확인 방법
version 옵션을 사용하면 현재 설치되어 있는 OpenSSL 의 버전을 쉽게 확인할 수 있습니다.
# openssl version
OpenSSL 0.9.8y-fips 5 Feb 2013

from Rigel

2014년 3월 23일 일요일

스노트(Snort)에서 발표한 OpenAppID - 애플리케이션 인지

정말 오랜만에 써 보는 포스팅입니다. 개인적인 이유로 너무 정신없이 지내다 보니 블로그에 포스팅할 시간이 없었는데, 오늘은 스노트에 관련한 글을 잠깐 적어볼까 합니다. 최근에 (정확히 말하면 한달전이 되겠네요) 스노트에서 OpenAppID 라는 것을 발표했습니다. 이름에서 알 수 있는 것과 같이 애플리케이션을 인지하기 위한 기술입니다. 
 
스노트 사이트에서는 해당 기능이 반영된 스노트를 다운로드 받을 수 있습니다.

http://snort.org/snort-downloads

Snort 2.9.7.0 알파 버전부터 해당 기능을 제공하고 있고 이 기능은 스노트에서 전처리기 형태로 제공되는 기능으로 Snort 를 컴파일시에 --enable-oepn-appid 와 같이 build 할때 기능을 제공하도록 설정되어야 사용할 수 있습니다.

주요기능은 다음과 같습니다.

- 네트워크 상에서 어플리케이션 탐지
- 애플리케이션의 사용 트래픽 보고 기능 
- 정책에 의한 애플리케이션 차단
- 스노트 룰 언어의 확장판으로 OpenAppID 사용 
- IPS 이벤트에서 어플리케이션 이름으로 보고 가능 

약 1,000 여개 이상의 어플리케이션 탐지를 제공한다고 합니다. 참고로 스노트에서 룰에 적용하여 사용하기 위해서는 다음과 같은 형태로 사용됩니다.

사용예:

    alert tcp any any -> any any  (msg:"test for app HTTP";     appid: http;     sid:18759; rev:4; )
    alert tcp any any -> any any  (msg:"test for app FTP";      appid: ftp;      sid:18760; rev:4; )
    alert tcp any any -> any any  (msg:"test for app FTP Data"; appid: ftp-data; sid:18761; rev:4; )
    alert tcp any any -> any any  (msg:"test for app CNN zappos"; appid: cnn.com zappos;  sid:18762; rev:4; )

기존 스노트룰에 appid 라는 태그를 사용하여 쉽게 사용이 가능합니다.

다만 어플리케이션 탐지 모듈은 기본적으로 Lua 로 만들어져 있습니다. Lua 는 많은 분들에게 생소한 언어이기도 한데요, 이로 인해서 어플리케이션 모듈을 만드는 것이 스노트 룰 만드는것만큼 쉽지는 않을것같습니다. 

하지만, OpenAppID 가 활성화되어 많은 모듈들이 만들어지면 Snort 를 통해서도 쉽게 어플리케이션 인지를 할 수 있어 상당히 유용해 집니다. 저도 이러한 부분에 동참하고자 OpenAppID 관련한 글을 지속적으로 적어볼까 합니다. 오늘은 오랜만에 쓰는 글이다 보니 가볍게만 적어보고 다음번을 기대해 볼께요 

2014년 1월 15일 수요일

패킷 저장시 용량을 줄이기 위한 고민

패킷 저장시 늘어만 가는 크기에 고민을 해본적이 있을것입니다. 무작정 큰 파일로 저장하기에도 용량 측면에서 문제가 있고 해당 파일을 열어보기에도 쉽지가 않습니다. 그러므로 패킷을 덤프하는 경우 그 이유가 명확해야 범위를 결정지을 수 있고, 얼마나 덤프를 해야하고 용량은 어느정도 까지 해야 하는지 대략 산출이 됩니다.

무조건 많이 패킷을 저장한다고 해서 얻을 수 있는 것이 많아진다고도 볼 수 없습니다.  패킷을 덤프하는 범위와 목적 그리고 환경적 요인에 따라 이것은 다 달라지기 때문에 어떻게 하시라고 딱 정해드리기는 어렵습니다.  다만 다음과 같은 부분을 고려한다면 패킷파일의 크기를 줄일 수 있으리라 생각됩니다.

1. 패킷 파일 저장시 범위를 명확히 하기

특정IP , 포트번호와 같이 무엇인가 범위를 잡을 수 있는 것이 있다면 범위 지정이 필요합니다.

2. 얼마나 오랜시간동안 저장할 것인가?

패킷 분석 목적에 따라서 달라지지만 얼마동안 저장하느냐에 따라 패킷크기가 크게 달라집니다.  무조건 오래하는 것이 좋은것은 아닙니다. 우선 짧은 시간 패킷을 저장해 보고 대략 패킷파일이 늘어나는 속도와 그 내용을 한번 보고서 시간을 결정하는 것도 좋습니다.  패킷덤프할 시간이 결정되면 파일의 적정크기도 판단하셔서 특정 크기 이상이 되면 다른 파일로 저장되도록 하는 것이 분석시에 편리합니다. 물론 하나의 파일로 모으는 경우가 필요할 경우도 있을것 같습니다. 만약 파일을 저장시에 분할한다면 와이어샤크에서는 패킷 덤프시 옵션을 지정할 수 가 있고, 큰 패킷로 저장을 하였다면 추후에 Split 를 하여 적정 크기로 만들어 분석을 할 수 있습니다. 패킷 분할과 관련해서는 블로그에서 검색해 보시면 몇 가지 방법들이 나옵니다.

3. 패킷저장 포맷 - Pcap vs Pcap-ng

오랜시간동안 패킷을 저장한다면 저장 포맷도 고려해볼 필요가 있습니다. 와이어샤크 최근 버전에서는 기본 포맷이 pcap 에서 pcap-ng 로 변경이 되었습니다. 비교적 최신 버전의 와이어샤크를 사용하신다면 본인도 모르게 패킷파일이 pcap-ng 로 저장되고 있는 것입니다. 가끔 전달받는 패킷의 포맷을 보면 과거보다 확실히 pcap-ng 포맷이 증가된걸 느낄 수 있습니다. pcap-ng 포맷은 pcap 보다 포맷구조가 더 유연해 졌으며 저장되는 내용도 더 많습니다.  최근 포스팅한 pcap-ng 포맷 구조 글을 참고해 보시면 이유를 아실 수 있습니다. 이러한 이유로 부가적인 정보가 더 기록이 되기 때문에 특별히 pcap-ng 포맷이 필요하지 않다면 용량적인 측면에서도 pcap 포맷으로 저장하는 것이 더 유리합니다.

다음의 경우를 한번 살펴보겠습니다. test.pcap 파일은 pcap-ng 포맷파일입니다.
$ file test.pcap
test.pcap: pcap-ng capture file - version 1.0

이 test.pcap 파일은 대략 크기가 2.6 기가 정도가 되는데 pcap 포맷으로 변경하여 한번 비교해 보도록 하겠습니다.

$ editcap -F libpcap -T ether  test.pcap convert.pcap

아래와 같이 pcap 포맷으로 변경하여 보면 약 300 메가 정도의 차이를 보입니다.

-rwxr-xr-x 1 root    root    2.6G Dec 12 16:09 test.pcap
-rw-rw-r-- 1 root     root    2.3G Dec 20 18:00 convert.pcap

만약 여러분이 상당히 오랜시간 패킷파일을 저장한다면 저장되는 패킷 포맷 방식에 따라 파일 크기가 커질 수 있다는 점입니다.

4. 패킷을 다 들여다 볼 것인가?

보통 패킷 덤프시 큰 용량을 차지하는 부분이 패킷의 페이로드 부분입니다. 헤더만 놓고 본다면 그 자체는 크지 않지만 데이터가 들어가면 이게 큰 부분을 차지합니다.  전체 패킷을 다 볼 필요가 없고 일부만 저장해도 된다고 할 경우에는 snaplen 크기를 지정하여 제한할 수가 있습니다. tcpdump 를 사용한다면 -s 를 통해 이 크기를 제한할 수 있습니다. 만약 -s 100 이 된다고 가정하면 패킷당 100 바이트 까지만 저장이 된다는 것이죠. 그렇다면 헤더 정보와 일부 페이로드만이 약간 포함될 것입니다. 물론 데이터를 전체 못 본다는 것은 있지만 여러분들의 필요에 따라 제한하여 저장해도 괜챦다면 이 방법은 패킷파일 크기를 줄이는데 큰 도움을 줄 것입니다.

와이어샤크에서는 캡쳐옵션에서 Limit each packet to "xxx" bytes 옵션을 통해 조정할 수 있습니다.

5. 압축으로 크기를 줄이자

패킷파일은 압축을 할 경우 용량 절약을 크게 볼 수 있는 경우가 많습니다. 저장해서 일정기간 동안 보관해야 하는 경우라면 압축을 해서 보관하는 것이 좋습니다. 물론, 패킷파일이 큰 경우에는 압축을 하고 해제하는데 많은 시간이 소요될 수 있다는 점도 알아두세요!

이상으로 패킷파일 크기를 줄이기 위한 몇 가지 방법을 적어 보았습니다.  패킷파일 저장시 한번쯤 참고해 보셨으면 합니다.

From Rigel

2013년 12월 27일 금요일

어느덧 패킷인사이드 4주년, 새해 복 많이 받으세요.

패킷인사이드 주인장 입니다. 12월쯤에 제가 블로그를 처음 시작했는데, 미쳐 잊고 있다 찾아보니 12월10일이 오픈일 이었네요. 어느덧 벌써 4년이라는 시간이 흘렀습니다. 2013년은 여느해 보다 개인적으로는 바쁘기도 하면서 어딘가 한 구석이 비워있는 듯한 느낌이었던것 같습니다. 2014년은 저도 다시 재 정비를 하고 더욱 많은 내용을 여러분들과 공유할 수 있도록 하겠습니다. 아직도 소개해 드리고 싶은 내용이 너무나도 많거든요.

이제 얼마 남지 않은 2013년 마무리 잘 하시고요, 2014년에 뵙겠습니다.

새해 복 많이 받으세요 그리고 모두 행복하세요 ^^

/Rigel

2013년 11월 13일 수요일

CaseStudy, FTP 접속 지연 원인 - 10초의 흔적을 찾아라!

오래간만에 쓰는 포스팅인것 같습니다. 이런저런 일들이 많다보니 블로깅도 게을러지고 있네요. :-)

오늘은 CaseStudy 형태로 애플리케이션의 접속 지연에 대해서 실제 사례를 한번 다뤄볼까 합니다. FTP 접속을 언급하려고 하는데, 이전에도 한번 사례로 FTP 를 다룬적이 있습니다. 약간 비슷할 수도 있지만 관점이 조금 다르긴 합니다. 일단 분석하게 된 이유는 이렇습니다.

FTP 를 통해서 특정 시스템에 접속하는데 접속이 바로 이뤄지지 않고 지연이 발생하는 것입니다.

C:\>ftp 192.168.0.1
Connected to 192.168.0.1.

여기서 10 초정도의 지연이 발생하고 다음과 같이 접속이 이뤄집니다.

C:\>ftp 192.168.0.1
Connected to 192.168.0.1.
220 TEST FTP server (Version 6.4/OpenBSD/Linux-ftpd-0.17) ready.
User (192.168.0.1:(none)):

일단 시스템에서 패킷 덤프를 해 보았습니다. 일반적인 3way Handshake 과정이 이뤄지고 있습니다. 클라이언트인 192.168.98.128 이 comm-gw(192.168.0.1) FTP 포트에 SYN 패킷을 보내면서 연결 시도를 합니다.

11:15:31.363275 IP 192.168.98.128.1087 > comm-gw.ftp: S 1725038219:1725038219(0) win 65535 <mss 1460,nop,nop,sackOK>
11:15:31.363305 IP comm-gw.ftp > 192.168.98.128.1087: S 3794060333:3794060333(0) ack 1725038220 win 5840 <mss 1460,nop,nop,sackOK>
11:15:31.363554 IP 192.168.98.128.1087 > comm-gw.ftp: . ack 1 win 65535



11:15:37.309023 IP 192.168.98.128.netbios-dgm > 192.168.255.255.netbios-dgm: NBT UDP PACKET(138)
11:15:42.577429 IP comm-gw.ftp > 192.168.98.128.1087: P 1:72(71) ack 1 win 5840
11:15:42.714480 IP 192.168.98.128.1087 > comm-gw.ftp: . ack 72 win 65464


그런데 ACK 까지 전달이 되고 통신을 하는 과정에서 지연이 보입니다. 11:15:31초 후에 42초 쯤 관련된 트래픽이 나타납니다. 클라이언트 관점에서 봤을때는 연결이 이뤄지고 서버에서 응답이 오기까지 지연이 보이는 것으로 추정됩니다.


일반적으로 이런 지연이 발생되는 원인중에 하나로 Name Lookup 이 있습니다. 그래서 /etc/hosts 파일에 해당 클라이언트 IP 를 넣어주고 패킷 덤프를 다시 해 봅니다.

# tcpdump -i eth4 host 192.168.98.128
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth4, link-type EN10MB (Ethernet), capture size 96 bytes


11:18:11.836148 IP aa-05-01.proofd > comm-gw.ftp: S 3515309442:3515309442(0) win 65535 <mss 1460,nop,nop,sackOK>
11:18:11.836173 IP comm-gw.ftp > aa-05-01.proofd: S 2006125397:2006125397(0) ack 3515309443 win 5840 <mss 1460,nop,nop,sackOK>
11:18:11.836340 IP aa-05-01.proofd > comm-gw.ftp: . ack 1 win 65535
11:18:11.837724 IP comm-gw.ftp > aa-05-01.proofd: P 1:72(71) ack 1 win 5840
11:18:11.955346 IP aa-05-01.proofd > comm-gw.ftp: . ack 72 win 65464
^C


그런데, 앞서 본 것과 달리 속도 지연이 없어졌습니다. 11:18:11 안에 모든 과정들이 끝나버렸습니다. 10초 이상 지연이 발생한 것과는 대조적입니다. 조금 더 자세히 살펴보기 위해 ftp 데몬을 추적해 보기로 했습니다. 다만, ftpd 데몬이 지속적으로 떠 있는 것이 아니라 inetd 데몬에 의해서 접속시 마다 프로세스가 생성되므로 다음과 같이 해 보았습니다.

1초마다 프로세스를 살펴봐서 in.ftp 문자열이 검색되면 해당 프로세스 ID 를 넘겨받아 strace -p PID 로 하는 것입니다.

# while true; do ps -ef | grep in.ftp | grep -v grep | gawk -F' ' '{system("strace -p "$2)}'; sleep 1; done

Process 16864 attached - interrupt to quit
restart_syscall(<... resuming interrupted call ...>) = 1
ioctl(4, FIONREAD, [43])                = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, [16]) = 43
close(4)                                = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 28) = 0
fcntl64(4, F_GETFL)                     = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK)  = 0
gettimeofday({1382065750, 265867}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0)    = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 3000)  = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [43])                = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [16]) = 43
close(4)                                = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, 28) = 0
fcntl64(4, F_GETFL)                     = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK)  = 0
gettimeofday({1382065752, 399158}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0)    = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 6000)  = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [43])                = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, [16]) = 43
close(4)                                = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, 28) = 0
fcntl64(4, F_GETFL)                     = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK)  = 0
gettimeofday({1382065753, 199402}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0)    = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 5000)  = 0 (Timeout)
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 28) = 0
fcntl64(5, F_GETFL)                     = 0x2 (flags O_RDWR)
fcntl64(5, F_SETFL, O_RDWR|O_NONBLOCK)  = 0
gettimeofday({1382065758, 201686}, NULL) = 0
poll([{fd=5, events=POLLOUT}], 1, 0)    = 1 ([{fd=5, revents=POLLOUT}])
send(5, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=5, events=POLLIN}], 1, 3000)  = 1 ([{fd=5, revents=POLLIN}])
ioctl(5, FIONREAD, [43])                = 0
recvfrom(5, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [16]) = 43
close(4)                                = 0
close(5)                                = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, 28) = 0
fcntl64(4, F_GETFL)                     = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK)  = 0
gettimeofday({1382065758, 888660}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0)    = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 6000)  = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [43])                = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, [16]) = 43
close(4)                                = 0
open("/etc/nologin", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/etc/ftpwelcome", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
uname({sys="Linux", node="TEST", ...}) = 0
stat64("/etc/resolv.conf", {st_mode=S_IFREG|0644, st_size=119, ...}) = 0
stat64("/etc/resolv.conf", {st_mode=S_IFREG|0644, st_size=119, ...}) = 0
open("/etc/hosts", O_RDONLY|O_CLOEXEC)  = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=336, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f4f000
read(4, "127.0.0.1\tlocalhost\n127.0.1.1\tTES"..., 4096) = 336
read(4, ""..., 4096)                    = 0
close(4)                                = 0
munmap(0xb7f4f000, 4096)                = 0
fstat64(1, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f4f000
write(1, "220 TEST FTP server (Version"..., 71) = 71
rt_sigaction(SIGALRM, {0x804ef40, [ALRM], SA_RESTART}, {SIG_DFL}, 8) = 0
alarm(900)                              = 0
fstat64(0, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f4e000
read(0, ^C <unfinished ...>
Process 16864 detached
Process 16864 attached - interrupt to quit
read(0, ^C <unfinished ...>
Process 16864 detached
Process 16864 attached - interrupt to quit
read(0, quit
^\ <unfinished ...>
Process 16864 detached

Process 16864 attached - interrupt to quit
read(0, ^C <unfinished ...>
Process 16864 detached
Process 16864 attached - interrupt to quit
read(0,
^C <unfinished ...>
Process 16864 detached
^\Quit
Process 16864 attached - interrupt to quit
read(0, ^Z


여기서는 실제로 보여주는 것이 한계가 있지만, strace 로 해서 보면 name lookup 하는 부분에서 딜레이가 발생하는 것이 보입니다. 즉, 이런 형태를 검증해 보는 하나의 방법으로 보시면 될것 같습니다. 하지만, 여기에 문제가 있습니다. 접속하는 클라이언트가 수백대가 넘게 있습니다. 그 IP 들을 일일이 넣어주기도 쉽지가 않습니다. 분명히 또 다른 방법이 있을것입니다. 'man in.ftpd' 를 통해서 세부적인 옵션을 확인해 보았더니 -n 이라는 것이 있습니다.

 -n      Use numeric IP addresses in logs instead of doing hostname lookup.

ftpd 데몬을 많이 써보았지만 굳이 옵션을 찾아볼 일이 없었는데 -n 이 호스트이름 대신 IP 주소로 사용하여 로그를 기록하게 되어 있네요. 자 그래서 -n 옵션을 사용해 반영했더니, 동일하게 모든 클라이언트에서 접속해도 빠른 속도로 접속이 이뤄집니다.

/etc/hosts 파일에 개별적으로 사용할 수도 있지만 좀더 광범위하고 효율적인 작업면에서는 -n 옵션이 더욱 적절했던 것입니다.

패킷덤프를 통해서 패킷의 흐름을 살펴보고, 서버에서의 응답에 지연이 있는 것으로 판단되어 서버단에서 문제를 찾아들어가며 해결한 경우입니다. 문제가 발생할 경우 해결하기 위한 방법에는 메뉴얼 처럼 딱 정해진것이 없습니다. 기본 해결 방법은 같을 수 있지만 그것을 찾아들어가기 위한 것은 다양합니다. 이것또한 그 중에 하나입니다.

어떤이는 접속이 되니까 그냥 그대로 사용할 수도 있고 또 다른 사람은 왜 지연이 있을까 하고 의문을 갖게 됩니다. 물론 이런 지연이 영향을 미칠만큼이냐 또는 아니냐에 따라서 판단은 달라지게 됩니다. 하지만 이런것에 Question 을 가지고 계속 질문해 보면 그 답을 찾는 과정에서 많은 것을 얻고 배우게 됩니다. 바로 이런것이 Experience 입니다. 저 또한 블로그를 하는 이유가 이러한 경험을 여러분들과 함께 공유하기 위함입니다.

자, 그럼 오늘은 여기까지 하고 마무리 짓겠습니다.

문제가 발생했을때 실마리를 어떻게 풀어나갈것인지 참고가 되었으면 좋겠습니다.

/Rigel

2013년 10월 23일 수요일

구글에서 제공하는 무료 DDoS 방어 서비스, 프로젝트 쉴드(Project Shield)

구글에서 최근 프로젝트 쉴드라는 것을 발표했습니다. 구글의 인프라스트럭처를 이용해 분산서비스거부 공격(DDoS)을 막을 수 있는 서비스 입니다. 서비스 받고 있는 호스팅을 이동한다든지의 작업 없이 구글의 서비스를 통해 DDoS 를 완하시키겠다는 것입니다.

현재 이 서비스는 무료로 이용할 수 있으나, 초대된 웹 사이트만 이용이 가능합니다. 지원을 하게 되면 구글이 말하는 'Trusted Tester' 로 선정되고 사용하게 됩니다. 가입을 하고 선정되면 설정할 수 있는 정보를 이메일을 통해 전달받는것 같습니다.

아마도 이 서비스는 DNS 설정을 통해 구글쪽으로 변경하고 거기서 구글의 인프라를 통해 DDoS 와 같은 이벤트가 급격하게 증가시 보호 역할을 하게 되는것으로 보입니다. 다른 외부에서 제공하는 DDoS 차단 서비스를 유사하게 무료로 제공하는 것으로 중.소기업의 웹 사이트들이 이용하기에 괜챦아 보입니다. DDoS 공격을 받고 있는 사이트가 있다면 지금 한번 신청해 보세요.

[참고]
1. 프로젝트 쉴드
http://projectshield.withgoogle.com/
2. 구글 아이디어 프로젝트
http://www.google.com/ideas/projects/