Linux 방화벽 iptables & DDoS 방어?

PF로 DDoS 공격을 어느정도 막으면서, iptables에서도 가능한지 궁금해졌습니다. PF 처럼 쉽게 공격 IP를 완전하게 차단하는 방법은 찾지 못했지만, IP당 시간당 연결수를 제한하는 방법은 있네요.

iptables -N SSHSCAN
iptables -A INPUT -p tcp –dport 22 -m state –state NEW -j SSHSCAN
iptables -A SSHSCAN -m recent –set –name SSH
iptables -A SSHSCAN -m recent –update –seconds 300 –hitcount 3 –name SSH -j DROP

5분동안 22포트에 똑같은 IP가 3번 이상의 TCP 연결이 들어오면 그 이후로는 막습니다. 즉 5분간 3개의 연결만을 허용합니다. SSH 돌리기 신공은 막을수 있겠지만, DDoS 공격을 차단하기는 부족한 기술인듯하네요.

using-iptables-to-block-brute-force-attacks 문서를 보시면, 자세한 설명이 있고, 공격 아이피에 대해서 로깅을 하는 방법도 있습니다. 로그 분석해서 iptables rule을 등록해주면 공격 아이피에 대해서 차단할수 있을듯하네요. 하지만 이 방법 말고 더 쉬운 방법이 없을까 고민중입니다.

여러가지 로그 파일에서 로그인 실패를 모니터링해서 공격을 막아주는 fail2ban 이라는 툴도 있네요. 설명은 여기를 참고하세요. 역시 DDoS 방어용은 아니지만, 로그 분석을 통해서 방화벽에 IP 등록하는걸 응용할수 있을듯합니다.

최근
Linux Firewalls: Attack Detection and Response with iptables, psad, and fwsnort 책을 보기 시작했는데 좀더 좋은 방어 방법 찾으면 다시 글 올리겠습니다.

BSD 방화벽 PF & DDoS 방어

PF(Packet Filter)는 BSD용 방화벽입니다. 원래 IPF라고 개인이 만든 공개 방화벽이 있어서, 여러 BSD 계열 UNIX에서 주로 사용하였었습니다. 다른 개발자들이 라이센스에 대해 명확하지 않은 부분에 대해서 설명 요구에 대한 응답에 만족하지 못한 오픈소스 개발자들이 새롭게 만든 방화벽이 PF입니다. 정말 너무나도 빠르게, IPF와 기능은 유사하게 만들어졌습니다. IPF는 소스는 공개였지만, 소스 분기는 허용하지 않고 원 개발자가 필요한 패치만 해서 하나의 버전을 유지하겠다고 했었습니다…

하여간 PF가 OpenBSD 개발자들에 의해서 순식간에 만들어지고, 여러개발자들이 참여가 가능했기 때문에 금방 여러가지 추가 기능들이 구현됐습니다. 오픈 소스 최고의 방화벽으로 자리 잡았고, 상용 방화벽이 갖추지 못한 좋은 기능들도 많이 가지고 있습니다.

FreeBSD에서 PF 방화벽을 사용하려면 /etc/rc.conf 에

pf_enable=”YES”
pflog_enable=”YES”

를 추가해야합니다.

정책 설정은 /etc/pf.conf 에서 합니다.

설정 파일에서 가장 중요한 부분은 pass/block이죠…

pass/block in/out (log) on 인터페이스
proto 프로토콜
from 주소/마스크비트 port 포트

to 주소/마스크비트 port 포트

1.2.3.x 에서 오는 모든 udp 패킷을 막고 싶다면…

block in proto udp from 1.2.3.0/24 to any

80포트로 오는 걸 열어주고 로그로 남기고 싶으면,

pass in log proto tcp from any to port 80 keep state

keep state하면 생성된 TCP 세션에 대해서 들어오고 나가는 패킷이 방화벽을 통과합니다.

패킷이 왔을때 위에서 부터 비교가 되며… 맨 마지막 매치가 선택됩니다.
단, quick이 설정되어 있으면 그 매치가 선택됩니다.

아래는 현재 사용중인 설정입니다. 추가적인 보안 관련 설정이 여러가지 있습니다..

ext_if=”em0″

set limit { states 80000, frags 5000 }

set block-policy drop

set skip on lo0

scrub in all
antispoof for $ext_if

block in all
block out all

table <bruteforce> persist
table <sshbruteforce> persist

block in quick log proto tcp from <bruteforce> to port 80
block in quick log proto tcp from <sshbruteforce> to port 22

pass in on $ext_if proto tcp from any to $ext_if port 22 \
            flags S/SA keep state \
            (max-src-conn-rate 10/30, overload <sshbruteforce> flush global)
pass in on $ext_if proto tcp from any to $ext_if port 80 \
        flags S/SA synproxy state
pass in on $ext_if proto tcp from any to $ext_if port 80 \
        flags S/SA keep state \
        (max-src-conn 100, max-src-conn-rate 300/10, \
        overload <bruteforce> flush global)

위에 설정에서 ssh나 http로 하나의 IP에서 여러개의 요청이 한번에 오면 막는 기능이 동작합니다. HTTP의 경우 최대 100개의 동시 연결, 10초간 300개 이상의 연결이 발생하게 되면 공격자로 인식하고 기존 연결을 모두 끊어버리고 연결을 막습니다.

한번 등록되면 방화벽 재시작 전에는 해당 IP에서 연결이 안됩니다. 일정 기간이 지나면 풀리게 하는 방법은 expiretable를 사용하면 가능합니다.

FreeBSD에서는

# cd /usr/ports/security/expiretable
# make install

# /usr/local/sbin/expiretable -t 2h bruteforce

2시간 이상 지난 IP를 풀어주는 명령으로 crontab에 등록해서 사용하면 됩니다.

최신 버전 PF를 사용하면 pfctl로 같은 기능을 할수 있다고 합니다.

# pfctl -t bruteforce -T expire 7200

방화벽 시작, 재시작, 설정 파일 다시 읽기, 종료 방법:

# /etc/rc.d/pf start
# /etc/rc.d/pf restart
# /etc/rc.d/pf reload
# /etc/rc.d/pf stop
# /etc/rc.d/pflog start
# /etc/rc.d/pflog stop

방화벽 로그를 실시간으로 보는 방법

# tcpdump -i pflog0

쌓인 로그를 보는 방법

# tcpdump -r /var/log/pflog

방화벽 상태 보기

# pfctl -s all
# pfctl -vvs all (보다 자세한 정보 보기)

방화벽 공격 IP 보기

# pfctl -t bruteforce -T show

마지막으로… 원격에서 방화벽 잘못 설정하면 IDC로 튀어가야합니다.
신중을 기해야합니다.

한가지 터득한 방법은…

# /etc/rc.d/pf reload; sleep 15; /etc/rc.d/pf stop

restart보다는 reload가 그나마 안전합니다. reload할 때는 세션 테이블이 초기화 되지 않습니다.

DDoS의 공격의 종류가 많고 어디가 bottleneck이 되느냐에 따라 해결방법이 틀려집니다. 서버 자체의 부하를 일으켜 서비스 거부 공격을 받는 경우 PF로 많은 공격을 막을수 있습니다.

pfstat을 설치하면, PF의 여러가지 상황을 모니터링 할수 있습니다.

# cd /usr/ports/sysutils/pfstat
# make install

pfstat 홈페이지의 설정파일을 복사하고, 인터페이스명과 파일 저장 위치들만 바꿔주고
crontab에 아래처럼 등록했습니다.

# crontab -l
* * * * * /usr/local/bin/pfstat -q
*/5 * * * * /usr/local/bin/pfstat -p

아이리버 스토리 질렀습니다 ^^

꽤 오래전부터 ebook reader에 관심을 두고 있었는데 아이리버에서 나온 스토리를 구매했습니다.

정가가 35만원정도인데 아래 링크 통해서 10% 할인해서 구입했습니다 :)

http://www.dnshop.com/front/product/ProductDetail?PID=C960_story_1&CID=R2100028&Sid=0101_10070000_01_01&szSearchKeyword=%BE%C6%C0%CC%B8%AE%B9%F6%BD%BA%C5%E4%B8%AE

어제 받자마자 1.04 펌웨어로 업그레이드하고 테스트해보았습니다.

일단 스크린 자체에서 빛이 안나니 처음에는 좀 허전하더군요 –; 오래보니까 눈에 주는 피로는 훨씬 덜한거 같네요. 주로 원서 컴퓨터 관련 기술 서적을 PDF로 보려고 하는데 세로보기로는 글씨가 너무 작아서 보기 힘듭니다. 펌웨어 1.04에서 제공하는 PDF reflow 기능을 사용하거나, 가로보기를 보면 볼만합니다. 특히 영문 폰트는 가독성이 상당히 좋네요. 종이 같아요 :)

하지만 몇가지 단점을 찾았는데 펌웨어 업그레이드로 점차 나아지리라 생각되네요.

1. PDF reflow off 상태에서 zoom 했을때 페이지내 보는곳 이동이 원활하지 않습니다.
   다음페이지 넘어갈때 페이지 안보였던 부분으로 이동은 하는데 어떤 원리로 이동하는지 모르겠네요. 그리고 절대로 못보는 위치도 있는듯하네요.

2. PDF reflow off 상태에서 가로보기일때는 zoom이 안됩니다.

3. 다른 문서 고를때 페이지 위치 등은 기억되지만, reflow 상태나 zoom은 기억이 안됩니다. 이게 좀 불편한데… 책 본격적으로 보면 여러책 왔다갔다하면서 보는 경우가 적기 때문에 아주 불편하진 않을듯합니다.

4. 세벌식 지원이 안됩니다. 메모와 다이어리 기능을 저는 거의 사용못하겠네요.

5. 한글 PDF는 reflow 기능이 안되는 파일이 있습니다. (글자가 겹쳐서 못봄)

6. 사전이 내장 안되어있는게 아쉽네요.

7. PDF 내 링크 이동과 검색이 지원되면 좋겠네요.

그리고 많은 분들이 옆면의 페이지 이동 버튼이 너무 아래쪽으로 치우져있다고 하시는데, 저는 페이지 넘길때 두손 써도 상관 없어서 별로 불편함은 못 느끼겠네요.

하여간… 이제부터 책 많이 보려고 합니다.

memcached를 이용한 grace 기능 구현

Varnish Cache의 grace 기능을 PHP/memcached로 구현했습니다.

grace 기능은 백엔드 서버가 죽더라도, 캐싱되어 있는 정보로 지속적인 서비스가 가능하도록 하는 기능입니다. 캐싱할때 여분의 시간(grace 시간)동안 더 캐시를 유지하며, 캐싱 시간이 지났을때 백엔드에 요청이 한번만 전달되게 하고, 나머지 요청에 대해서는 우선적으로 그 전에 있던 캐시로 서비스를 하도록 하여 백엔드 서버의 부하를 줄일수 있습니다.

현재는 memcached에서 expire 되면서 동시에 요청이 발생하는데, grace 기능을 구현하면 오래걸리는 페이지 생성할때 큰 효과를 발휘할수 있을거라 기대됩니다.

PHP 구현은 간단합니다. 원래 key,value 항목의 expire
시간을 grace 시간 만큼 늘리고, 다른 키(key+”_s”)에 expire 시간동안 상태값을 입력해두면, 어렵지 않게 grace 기능을
구현할수 있습니다.

초기에 캐시에 없을때는 백엔드에 동시에 여러 요청이 들어갈수 있습니다. 이미 요청이 처리되고 있을때(state=”G”), 일정시간동안 주기적으로 memcached를 조회해서 key가 있을때까지 대기하는 방법으로 어느정도 해결 가능할 거라 생각하지만 구현은 하지 않았습니다.

[CODE type=php]
array(‘127.0.0.1:11211′), ‘debug’ => false, ‘persistant’ => false));

function generate_value($key) {
sleep(5);
return “VALUE”. $key;
}

function get_value($key) {
global $mc;
$expire = 60;
$grace_timeout = 600;
$generation_timeout = 10;

$state = $mc->get($key.”_s”);
//echo(“state=$state
\n”);
if ($state == “C” || $state == “G”) {
$value = $mc->get($key);
if ($value) {
//echo(“retreive key=$key
\n”);
return $value;
}
}

$mc->set($key.”_s”, “G”, $generation_timeout); // state: Generating
$value = generate_value($key);
$mc->set($key, $value, $expire + $grace_timeout);
$mc->set($key.”_s”, “C”, $expire); // state: Cached
//echo(“insert key=$key
\n”);

return $value;
}

get_value(“hahaha”);
?>

[/CODE]

Varnish 2.0 (웹캐시, 리버스프록시)

Varnish Cache에 대해서 글을 올린지 얼마 안된 듯한데 찾아보니 3년정도가 지났네요. 아직까지 실제 서비스 환경에서는 Varnish를 도입하지 못했었는데… 다시 필요에 의해서 로드밸랜서(+서버단 캐시)를 고려하게 되었습니다.

원래 수동으로 서브도메인별로 나누어서 로드밸런싱을 하고 있었는데, 서브도메인간 자바스크립트 연동의 문제들이 여러곳에서 발생하기 시작해서, 로드밸런서를 도입하려고 합니다. 그외에도 여러가지 장점이 있을듯 하네요.

Varnish가 이제 버전 2.0이 나왔고(나온지 1년 됐네요 –), 여러가지 변화가 있었네요.
현재 버전은 2.0.4 입니다.

1. VCL 언어가 좀더 프로그래밍 언어 다워졌습니다.

VCL 언어가 약간 언어답지 않은 면이 있었는데.. return 등의 도입으로 좀더 설정파일(소스) 보기가 편해졌습니다. 그리고 C언어를 embedding 할수 있는 방법이 있네요.

2. grace 지원

개체(요청결과)를 캐싱할때 expire 시간과 함께 grace 시간을 설정할수 있습니다. expire 시간이 지나고 웹서버(backend)들이 요청 처리를 하지 못할때 grace 시간 동안 캐시에 있는 내용으로 서비스가 됩니다. 또한 expire 시간이 지나는 순간 하나의 웹서버에 하나의 요청만으로 캐시를 업데이트하며 그 동안은 캐시에서 서비스가 되도록 설정할수 있습니다. 예를 들면 메인 페이지가 1초에 100번 요청되고 메인 페이지 처리하는데 2초가 걸린다고 하면, 서버에 원래 200번 요청 들어갈것이 1번만 가면 되는거죠. 웹서버와 디비 입장에서 아주 고마운 일이죠.

3. ESI (Edge Side Script) 일부 지원

 페이지에서 특정 부분은 사용자 정보가 포함되기 때문에 전체 페이지 캐싱이 어려운 경우가 많습니다. 사용자 별로 캐시를 두면 캐시의 의미가 많이 떨어집니다. 이럴 경우 해당 부분만 별도의 페이지로 저장해두고

<esi:include src=”userinfo.php” />

형태로 원본 페이지에 넣어주면 캐시에 그대로 저장되고, 요청을 처리할때 userinfo.php 부분만 웹서버에 요청하여 varnish에서 조합해서 보내줍니다.

ESI가 아주 유용한 기능이기는 하지만 아쉬운 점은 Varnish가 아직 압축을 지원하지 못한다는 점이네요. 웹서버 측에서 페이지를 압축하면 ESI를 쓸수 없고, Varnish 쪽에서 압축할수 있는 방법을 지원하지 않습니다. ESI와 압축을 조합해서 쓸수 있었으면 정말 좋았을텐데 정말 아쉬운 부분이네요.

현재 회사에서 varnish 도입을 위한  테스트중입니다. ESI는 압축 미지원으로 도입에서 제외되었으며, 로드밴런싱, 캐싱 기능, 서버 죽을때 처리 등만 활용하려고 합니다.

grace의 개념을 이해하면서, memcached를 웹에서 사용하는 방법을 비슷한 방법으로 변경을 고려하고 있습니다.

PHP 모든 페이지를 캐싱하기는 로직상으로 문제가 발생할 확률이 커서 정해진 HTTP 헤더가 포함된 PHP 페이지만 캐싱 되도록 설정했습니다. 캐싱해도 안전하다고 생각하는 페이지에 특정 헤더를 포함하면 캐싱이 됩니다.

url.purge 명령으로 정규식에 해당하는 캐싱된 페이지를 날릴수 있는 기능도 활용할 생각입니다. 캐싱이 유용한 페이지이지만 사용자가 글을 등록하고 바로 글이 안보인다던지 하는 문제는 url.purge를 써서 해결하려고 합니다.

CentOS PHP에서 원격 연결 안될때

CentOS 5.x에서 웹서버나 PHP에서 원격서버에 연결할때 기본적으로 SELinux 보안 모듈에서
막나보네요. PHP에서 원격 DB에 연결이 안되서, iptables도 열심히 만지고 PHP소스도 컴파일 다시 하고, tcpdump로 패킷 보고 –; 하여간 삽질하다가 겨우 해결했습니다.

웹서버는 lighttpd, mod_fastcgi 모듈로 PHP 사용중입니다.

아래처럼 웹서버에서 외부로 연결가능하도록 설정가능합니다.

# /usr/sbin/setsebool httpd_can_network_connect=1

원격 DB 연결, PHP에서 get_file_content() 함수로 외부의 데이터를 받을 때, 웹서버에서 mod_proxy 사용등을 할때 아마도 이 설정을 풀어줘야할듯 하네요.

재부팅할때도 적용되게 하려면,

# /usr/sbin/setsebool -P httpd_can_network_connect=1

출처:

https://bugzilla.redhat.com/show_bug.cgi?id=164867

Bookr : PSP를 ebook reader로 활용 (PDF, CHM)

처음에는 PSP를 게임기로 사용하다가 요즘은 거의 ebook reader로 활용하고 있습니다. 아마존 킨들 DX나 킨들2를 구입할 생각도 했었는데 가격이 너무 비싸고 좀 과도기적인 제품인듯해서 구매가 망설여지더군요.

PSP가 커펌(–)되어 있다면 bookr라는 어플을 깔면 PDF나 CHM 파일을 읽을수 있습니다.
PDF 파일은 자체 뷰어이고, CHM은 내장 웹브라우저를 사용하도록 개발되어있습니다. CHM은 최근 버전(다른 제작자)에서 지원하기 시작했는데 제가 많이는 사용안해봤습니다.

최신 버전에서 PDF지원이 불안해서 저는 두가지 버전을 사용하고 있습니다.

bookr 0.7.1 : PDF 읽기에 사용.
    다운로드 http://sourceforge.net/projects/bookr/files/

bookr 0.8.0 : CHM 읽을때 사용. PDF 지원은 불안 (제 PSP에서는)

이 글 쓰면서 검색해보니 0.8.0 버전이 업그레이드 되서 V8.1로 나왔네요.

bookr V8.1 : 아직 많이 사용해보지 않아서 확실하지는 않지만 PDF 보기가 큰 무리없이 동작하는 듯하네요. (지원 파일 포맷: PDF, TXT, PalmDoc, DJVU, CHM/HTML)
    릴리즈 게시판: http://forums.qj.net/psp-development-forum/147707-release-bookr-v8-1-view-pdf-txt-palmdoc-djvu-chm-html-files.html
    다운로드: http://www.mediafire.com/download.php?obmaczmkgom

PSP가 생각했던거보다 ebook reader로서의 역할을 충실히하고 있습니다.

다른 제품과 비교해보면…

소니의 reader나 아마존의 킨들 등의 e-ink 기반 제품은 책같이 가독성이 높고, 밝은 곳에서 읽을수 있지만, 느린 CPU와 PDF 미지원으로 구매가 망설여지는 제품들입니다. 아직 대중화가 안되서 가격도 비싸죠. 예외적으로 킨들DX에서 PDF를 지원하긴 하지만 크기가 좀 크고, 가격이 정말 너무 비쌉니다.

아이팟터치에서 PDF를 볼수 있는 어플들이 많이 존재하기는 하지만 아이팟터치는 손에 들고 있기 편하지 않고 화면도 PSP에 비하면 작습니다.

전에 VGA(640×480)기반 Dell Axim을 잠깐 썼었는데… ebook reader로써 꽤 쓸만했던거 같네요. 다른거보다 acrobat reader에서 reflow mode를 지원해서 원하는 텍스트 크기로 볼수 있어서 좋았습니다. 하지만, 그림이 많은 책들은 reflow mode로 보기에는 좀 힘들더군요.

킨들2 정도의 크기에 해상도가 좀 높아지고, CPU가 빨라져서 PDF 파일을 지원하는 제품정도가 적당한 가격에 나오면 바로 지를듯 하네요.

애플이 준비중인 tablet 컴퓨터도 ebook reader 시장을 보고 준비중인것으로 들었는데, 기기가 어떻게 나올지 정말 궁금하네요…

꽤 오래전부터 ebook reader에 관심을 가져왔지만… 국내 나온 제품들 소식에는 대부분 실망했고, 비싼 돈 주고 살만큼 매력적인 제품도 나오지 않는 상황이네요. 현재는 PSP로 만족하면서 책을 보고 있습니다.

Flash로 아이폰, 아이팟터치 어플 개발

우리나라 시간으로 오늘 새벽에 Adobe MAX 2009가 열렸습니다.

먼저 Flash 10.1AIR 2.0 에 대한 내용이 소개되었습니다. Flash 10의 기능이 대부분의 스마트폰 플렛폼에 포팅이 되었거나 진행중이라고 합니다. Palm, Windows Mobile, Google Android, Symbian 등의 플렛폼을 지원한다고 하네요. 아쉬운건 아이폰에 대한 지원이 없다는 것이네요.

애플과 어도비의 줄다리기로 아이폰에서 Flash 지원이 언제될지 모르는 상황에서, Flash CS5에서 아이폰 용 응용프로그램으로 출력할수 있는 기능을 제공한다고 합니다. SWF를 기기에서 지원하는것이 아니라 아이폰용 native application으로 컴파일하는거라 하네요. 아이폰용 어플 따로 개발하는 거 자체가 큰 투자였는데 이렇게 되면 많은 회사에서 큰 투자없이 아이폰 플랫폼을 지원할수 있을거라 생각되네요.

아이폰/터치를 지원하는 Flash CS5 베타 버전이 나오면 공지 메일을 받을수 있습니다. http://www.adobe.com/go/flashprobetanotify

관련 URL:
1. http://blog.digitalbackcountry.com/2009/10/rundown-of-the-max-news/
2. http://www.mikechambers.com/blog/2009/10/05/building-applications-for-the-iphone-with-flash/

referer 유지하며 페이지 리다이렉트

페이지 리다이렉트(포워딩)하는 방법이 여러가지 있지만, 대부분의 경우 워래 referer가 사라집니다.

javascript의 href.location 할당 방법, meta tag를 이용한 포워딩 방법, PHP에서 header(“Location: url”) 등 모두 referer가 유지되지 않죠.

HTTP 응답으로 301 Moved Permanently 코드를 사용하면, 원래 referer가 리다이렉트된
페이지에서 잡힙니다.

웹서버에서 특정 페이지에 대해 설정할수도 있지만, PHP에서 아래와 같이 header를 추가한 다음에 Location: 을 설정하면 됩니다.

[CODE type=php]
<?
  header (‘HTTP/1.1 301 Moved Permanently’);
  header (‘Location: newurl’);
?>
[/CODE]

referer는 아래처럼 출력해볼수 있습니다.

[code type=php]
<?
  echo($_SERVER[‘HTTP_REFERER’]);
?>
[/code]