2009年7月28日火曜日

特定の国からのパケットを破棄

自前でCentOS+Postfixにてメールサーバを立てていて月平均で2万通を処理しています。
S25R+Greylistingである程度はスパム対策をしているつもりですが、
それをすり抜けてくるスパムが後を絶たない!
特に韓国、中国がひどく最悪です。また最近ではポーランド、ロシアも多くなっています。
そこでkrfilterというサイトを見つけて韓国、中国からのパケット自体を破棄していました。
(CIDR表記されたフィルターファイルをkrfiler様からダウンロードしてiptablesで読み込み定義する)
しかし最近そのフィルターがダウンロードできずフィルタリングできない状態です。
ということで、自分でAPNIC等からIPアドレス割当リストを取得してフィルターを作ることにしました。
 
とりあえず韓国と中国からのパケット(IPv4)を破棄するよう構築します。
その前に・・・
このフィルター設定で受けた不利益に対しては一切の責任を負えません。
必ず自己責任で行うようにしてください。

また、運用にはそれなりに負荷が掛かりますので非力なマシンでは注意してください。 


1.アジア圏を管轄しているAPNICからIPアドレス割当リストの取得する。
2.リストからCIDR表記に変換してiptablesが読み込める形式でファイルを作成する。
3.iptablesを定義して2で作成したファイルを読み込む。

では実際に行ってみましょう。(尚、作業はrootで行います)
●準備
ファイル保存場所を作成する。
# mkdir /etc/myfilter
# cd /etc/myfilter

●IPアドレス割当リストの取得
# wget -O list_apnic ftp://ftp.apnic.net/pub/apnic/stats/apnic/delegated-apnic-latest
※wgetオプション-Oは別名でファイル保存します。
 ですのでダウンロードするとlist_apnicというファイル名になります。

このリストは以下のような感じで羅列されています。
1列目:管轄先
2列目:国コード
3列目:IPv4またはIPv6の判断
4列目:IPv4なら開始IPアドレス
5列目:割当個数
apnicCNipv4203.128.32.0819220050311allocated
apnicIDipv4203.128.64.0819220000613allocated
apnicCNipv4203.128.96.0819220050314allocated
apnicKRipv4203.128.160.0819220050314allocated

●CIDR表記に変換
「うざい国からのアクセスを全て遮断」で公開されている変換スクリプトを使わせていただきます。
※公開に感謝いたします。
# wget http://www.42ch.net/~shutoff/prog/countryfilter.pl

CentOSで使うために一部修正が必要なので変更します。
91行目iptablesのpathを変更します。(事前に# which iptablesでpathを確認)
================================================
print "$CommentChar variables. change these values before run.\n";
if ($FilterType == 1) { # iptables
   #print "IPTABLES=/usr/sbin/iptables\n"; ← コメント
   print "IPTABLES=/sbin/iptables\n"; ← 追記
   print "FILTERNAME=CKFILTER\n";
   print "TARGET=CKFILTERED\n";
} elsif ($FilterType == 2) { # ipfw
================================================

countryfilter.plにオプションを渡して変換します。
# perl countryfilter.pl iptables KR,CN < ist_apnic > filter_apnic.sh
※<>は実際は半角で・・・

変換されたファイルは以下のような感じで羅列されています。
$IPTABLES -A $FILTERNAME -s 222.250.0.0/16 -j $TARGET
$IPTABLES -A $FILTERNAME -s 222.251.0.0/17 -j $TARGET
$IPTABLES -A $FILTERNAME -s 222.251.128.0/17 -j $TARGET

●iptables定義
iptablesを初期化する
# /etc/init.d/iptables restart

チェインを作成する
# iptables -N CKFILTER
# iptables -N CKFILTERED

チェインにルールを追記する(変換したファイル)
# sh filter_apnic.sh

フィルターに引っかからなかったパケットを許可するルールをCKFILTERチェインに追加する
# iptables -A CKFILTER -j ACCEPT

フィルターに引っかかったパケットを破棄するルールをCKFILTEREDチェインに追加する
さらに破棄されたパケットのログを取るようにする
# iptables -A CKFILTERED -j LOG --log-prefix "Reject-TCP "
# iptables -A CKFILTERED -j DROP

TCP接続開始パケットをINPUTからCKFILTERに飛ばすようにする
# iptables -A INPUT -p tcp -m state --state NEW -j CKFILTER

確認
# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
CKFILTER tcp -- anywhere anywhere state NEW

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

Chain CKFILTER (1 references)
target prot opt source destination
CKFILTERED all -- 58.14.0.0/15 anywhere
CKFILTERED all -- 58.16.0.0/16 anywhere
CKFILTERED all -- 58.17.0.0/17 anywhere
CKFILTERED all -- 58.17.128.0/17 anywhere
  ・
  ・
  ・
こんな感じでリストが出てくればOKです。

ログの確認
# cat /var/log/messages
Jul 28 15:08:41 mail01 kernel: Reject-TCP IN=eth1 OUT= MAC=Macアドレス SRC=送信元IP DST=受けIP LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=5926 DF PROTO=TCP
SPT=42975 DPT=25 WINDOW=5840 RES=0x00 SYN URGP=0

こんな感じでログが書き込まれます。

私は一連の流れをスクリプト化して1日1回実行するようcronに登録しています。
※2009/10 1週間に1回に変更した。


アジア圏以外は以下からリストを取得することができます。
AfriNIC管轄:アフリカ地域
ftp://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-latest
ARIN 管轄:アメリカ北部圏
ftp://ftp.arin.net/pub/stats/arin/delegated-arin-latest
LACNIC管轄:ラテンアメリカ及び西インド諸島地域
ftp://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest
RIPE NIC管轄:ヨーロッパ地域
ftp://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-latest


参考にしたサイト
「うざい国からのアクセスを全て遮断」
krfilter - deny accesses from .kr


※2013/05/13 ご要望頂いたので自動化スクリプトを以下に公開しました。




シェルスクリプトを用意して後は適当にcronで実行します。rootのcronや/etc/cron.weekly/の置くなどして定期的に実行します。※実行権限を付けることを忘れずに!

ここでは、7か国からのパケットを破棄する設定となっております。ただし自己責任でご利用頂きたいと思います。

# vi filterupdate.sh

記述内容

##############################################################

#!/bin/sh
#
# KR,CN,TW,PL,BR,CZ,RU,ITに対応
#
# 1.管轄各国別IPの一覧を取得する
# 2.一覧から指定の国をiptable作成用にスクリプト作成する
# 3.最後にそのスクリプトを実行させる
#
# 変換スクリプト
#       APNIC等の各国のIPアドレスリストからCIDR表記に変換する。
#       「うざい国からのアクセスを全て遮断」から
#    countryfilter.plを利用されてもらった。
#
# by stdman http://stdman.blogspot.jp/
##############################################################

cd /etc/myfilter

#################################
# 各管轄から最新のIPリストを取得する
# 取得したら別名にする
#################################

# APNIC(KR,CN,TW)
wget -O list_apnic_`date +%Y%m%d` ftp://ftp.apnic.net/pub/apnic/stats/apnic/delegated-apnic-latest

# ARIN(KR,CZ,IT)
wget -O list_arin_`date +%Y%m%d` ftp://ftp.arin.net/pub/stats/arin/delegated-arin-latest

# LACNIC(BR)
wget -O list_lacnic_`date +%Y%m%d` ftp://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest

# RIPE NIC(PL,CZ,RU,IT)
wget -O list_ripencc_`date +%Y%m%d` ftp://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-latest

####################################
# IPリストからCIDR表記に変換して
# iptableのチェイン追加用スクリプトを作成する
# 書き出したい指定国をカンマ区切りで指定
####################################

# APNIC(KR,CN,TW)
perl countryfilter.pl iptables KR,CN,TW < list_apnic_`date +%Y%m%d` > filter_apnic_`date +%Y%m%d`.sh

# ARIN(KR,CZ,IT)
perl countryfilter.pl iptables KR,CZ,IT < list_arin_`date +%Y%m%d` > filter_arin_`date +%Y%m%d`.sh

# LACNIC(BR)
perl countryfilter.pl iptables BR < list_lacnic_`date +%Y%m%d` > filter_lacnic_`date +%Y%m%d`.sh

# RIPE NIC(PL,CZ,RU,IT)
perl countryfilter.pl iptables PL,CZ,RU,IT < list_ripencc_`date +%Y%m%d` > filter_ripencc_`date +%Y%m%d`.sh

################################
# iptableの再構成
################################

/etc/init.d/iptables restart

# チェインを作成
# countryfilter.pl内でチェイン名が決められているので
# 以下のチェイン名になります。
iptables -N CKFILTER
iptables -N CKFILTERED

# チェインにルールを追記する。
sh filter_apnic_`date +%Y%m%d`.sh
sh filter_arin_`date +%Y%m%d`.sh
sh filter_lacnic_`date +%Y%m%d`.sh
sh filter_ripencc_`date +%Y%m%d`.sh

# フィルターに引っかからなかったパケットを許可するルールを
# CKFILTERチェインに追加する。
iptables -A CKFILTER -j ACCEPT

# フィルターに引っかかったパケットを破棄するルールを
# CKFILTEREDチェインに追加する。
# 破棄されたパケットのログを取るようにする。
iptables -A CKFILTERED -j LOG --log-prefix "Reject-TCP "
iptables -A CKFILTERED -j DROP

# TCP接続開始パケットをINPUTからCKFILTERに飛ばすようにする。
iptables -A INPUT -p tcp -m state --state NEW -j CKFILTER

# 古いファイルを削除
# 過去6日間アクセスのないもの
find . -name 'list_*' -ctime +6 -exec rm {} \;
find . -name 'filter_*' -ctime +6 -exec rm {} \;


3 件のコメント:

UTATA さんのコメント...

ありがとうございます。

見よう見まねでやってみました。
サーバーの高負荷がなくなったのには驚きました。

できましたら、cron用のシェルも 

STDMAN さんのコメント...

UTATA様、ご参考にして頂きありがとうございます。

cronで実行できるシェルスクリプトを掲載しました。シェルスクリプトと呼べるものではありませんがご参考にどうぞ(^^;)

IPリスト取得のwgetはリトライオプションを付けた方がいいかもしれません。またサーバ再起動時にも実行する必要があります。

Unknown さんのコメント...

ありがとうございます。

Google検索