どぶお/Linuxで遊ぼう!

iptablesで動的アドレスリストを扱う  

2012-02-08 : 作成

iptablesで動的に拒否/許可アドレスを指定したい場合に便利なモジュールrecentですが、ドキュメントがあまり見当たらなかったのでまとめてみました。また、他のスクリプト等からこのリストの内容にアクセスする方法を説明します。

recentモジュール  

一般にiptablesではチェインを複数指定することでIPアドレスのリストを拒否したり許可したりしますが、recentモジュールを使うことでそのリストを動的に扱うことができます。SSHなどへのbrute force attackへの対策としてよく使われているようです(ある時間当たり一定数以上のアクセスがあったら遮断、など)。これはrecentモジュールがアクセス元のリストを保持しており、規定時間以内のアクセス数の条件判定ができるためですが、そのリストは柔軟に扱うことが可能で、スクリプトなどからも操作することができるようです。

そのリストをスクリプトなどから使用する方法を示したサイトが見つからなかったのでメモしておきます。なお、SSHへのbrute force attack対策としてrecentモジュールを使った方法は以下のページなどで詳しく解説されています。以前のバージョンで解説されている場合はipt_recentになっていることがあります(現在はxt_recent)。

apacheへの攻撃を遮断する  

SSHへのbrute force attackはよくある話で対策方法もたくさんの方が解説されているので、ここではapacheと連携した場合(アプリケーション層で内容を判定するってことかな)の遮断方法を考えてみます。

apacheのログを確認しているとphpMyAdminへのアタックが散見されます。phpMyAdminを使っていなければ特に脅威にはなりませんが、ログの量が増える上に、そのログに埋もれて重大な事に気がつかないかもしれません。そんなわけで以下のようなよくあるアタックのURLがリクエストされたらrecentのリストに追加してアクセスを遮断してしまおうという対策です。apacheで拒否してもいいのですが、ログに残るという意味では一緒なので(消すこともできるのかな?)、iptablesの段階でDROPすることにします。

ここで示した例はあくまで動作の説明用です。実際に使う場合はパーミッションの設定やファイルの配置、設定などを状況に合わせる必要がありますので、知識のない方は不用意に行わないようにして下さい。

よくある攻撃  

以下のような一連のアタックが多いですね。

aaa.bbb.ccc.ddd - - [07/Feb/2012:23:43:48 +0900] "GET /w00tw00t.at.blackhats.romanian.anti-sec:) HTTP/1.1" 302 207 "-" "ZmEu"
aaa.bbb.ccc.ddd - - [07/Feb/2012:23:43:48 +0900] "GET /phpMyAdmin/scripts/setup.php HTTP/1.1" 302 207 "-" "ZmEu"
aaa.bbb.ccc.ddd - - [07/Feb/2012:23:43:48 +0900] "GET /phpmyadmin/scripts/setup.php HTTP/1.1" 302 207 "-" "ZmEu"
aaa.bbb.ccc.ddd - - [07/Feb/2012:23:43:48 +0900] "GET /pma/scripts/setup.php HTTP/1.1" 302 207 "-" "ZmEu"
aaa.bbb.ccc.ddd - - [07/Feb/2012:23:43:48 +0900] "GET /myadmin/scripts/setup.php HTTP/1.1" 302 207 "-" "ZmEu"
aaa.bbb.ccc.ddd - - [07/Feb/2012:23:43:49 +0900] "GET /MyAdmin/scripts/setup.php HTTP/1.1" 302 207 "-" "ZmEu"

これを見て思いましたが、同時アクセスっぽいので第一波は遮断できないかも・・・
とりあえずターゲットを上の6つのURLだと仮定してスクリプトを作成します。

スクリプトの作成  

上のURLにアクセスされたときに遮断スクリプトを実行することにします。スクリプトは何でもいいと思いますが、ここは例なのでシェルスクリプトで書きます。あくまでスクリプトの例なのでパフォーマンスなどを考える時はまた別のやり方を試して下さい。

/var/www/cgi-bin/block.sh
#!/bin/bash
echo +$REMOTE_ADDR > /proc/net/xt_recent/apache

REMOTE_ADDRの出力先である/proc/net/xt_recent/apacheについては下の「/proc/net/xt_recent/*について」を参照して下さい。ここでは簡単のためapacheサーバの動作ユーザーで/proc/net/xt_recent/apacheにアクセス可能なものとします(実際はsudoなどを使う方がセキュリティ的にはいいのかな?)。
上のスクリプトではblock.shが実行されたときに/proc/net/xt_recent/apacheにREMOTE_ADDRを追加する、という動作を行っています。

apacheの設定  

上のアタックを想定していますのでLocationMatchのErrorDocumentで飛ばします。

Alias /block /var/www/cgi-bin/block.sh
<Location /block>
  AddHandler cgi-script .sh
</Location>
<LocationMatch ^/+w00tw00t>
  ErrorDocument 404 /block
</Location>

この辺りは環境に合わせて修正して下さい。

iptablesの設定  

iptablesでrecentモジュールを使ったルールを作成します。

# iptables -I INPUT -m recent --name apache --update -j DROP

これでapacheという名前のリストにあるアドレスのパケットをドロップします。必要ならポートを特定するといいでしょう。これで/proc/net/xt_recent/apacheというファイルが作成されているはずです。ただし、このままではrootしか書き込めないのでパーミッションを変更します。

# chown apache:apache /proc/net/xt_recent/apache

ここまできたらapacheを再起動させます。

動作確認  

注意!不用意に自分のアドレスをブロックするとサーバにアクセスできなくなる場合があります。アクセス手段はきちんと確保してから試すようにして下さい。
例えば自分のブラウザでhttp://myserver/w00tw00t.at.blackhats.romanian.anti-sec:)にアクセスするとアクセスがブロックされるはずです。

実環境のためのヒント  

上の例でも期待される動作は実現可能だと思いますが、実際に使う場合は以下のポイントを自分の環境に合わせるといいと思います。

xt_recentの起動オプション  

後半のドキュメント部にもありますが、カーネルモジュールxt_recentロード時にオプションを指定することでパーミッションなどの設定を変更することができます。手動でロードするときはiptablesで-m recentが指定される前に、

# insmod xt_recent ip_list_uid=500 ip_list_gid=500

などとすれば/proc/net/xt_recent以下のファイルの所有者を変更することができます(ここではUID=500、GID=500)。
毎回指定するのが面倒なときは/etc/modprobe.d以下に設定ファイルを作成しておきます。

/etc/modprobe.d/iptables-recent.conf
options xt_recent ip_list_uid=500 ip_list_gid=500
これでinsmod指定しなくてもパーミッションなどが設定されます。ただし、/proc/net/xt_recent以下のファイル全てに適用されるので注意して下さい。

recentモジュールのドキュメント (抄訳)  

iptablesのrecentモジュールのリファレンスはソース中にはあるのですが、Web上で見当たらなかったので整形&ついでに抄訳しておきます。内容の正確性は保証しませんので必ず原文も参照して下さい。原文ファイルはiptables-1.4.10/extensions/libxt_recent.manです。

libxt_recent  

recentモジュールはIPアドレスリストを動的に生成し、そのリストへの一致を確認することができます。例えば、ファイアウォール上のポート139に接続しようと試みた相手をbadguyというリスト上に書き出し、それ以降のパケットをすべてドロップするようなことができます。

オプション  

--set, --rcheck, --update, --removeはそれぞれ同時に使う事はできません。

オプション内容
--name nameコマンドで使用するリスト名を指定します。指定されていない時はDEFAULTが使用されます
[!] --setリストにアドレスを追加します。アドレスがすでにリストに存在する時はそのエントリを更新し、これは常にTrueを返します(!を指定した場合はFalse)
--rsourceリストの保存/一致に接続元アドレスを使用します(ディフォルト)
--rdestリストの保存/一致に接続先アドレスを使用します
[!] --rcheckアドレスがリストに存在するかどうかをチェックします
[!] --update--rcheckと同様ですが、一致した場合"最終到達(last seen)"タイムスタンプを更新します
[!] --removeアドレスがリストに存在していた場合、Trueを返してリストから削除します。存在していなかった場合はFalseを返します
--seconds seconds--rcheckまたは--updateと共に使い、対象を指定された秒以内の"last seen"に絞り込みます
--hitcount hits--rcheckまたは--updateと共に使い、対象を指定された数以上のパケットが到達しているものに絞り込みます。--secondsと併用することで指定された秒数以内のヒット数で絞り込むことができます。最大値はip_pkt_list_totパラメータで指定された値で、この値を超えて指定するとルールは拒否されます
--rttlThis option may only be used in conjunction with one of --rcheck or --update. When used, this will narrow the match to only happen when the address is in the list and the TTL of the current packet matches that of the packet which hit the --set rule. This may be useful if you have problems with people faking their source address in order to DoS you via this module by disallowing others access to your site by sending bogus packets to you.

 

iptables -A FORWARD -m recent --name badguy --rcheck --seconds 60 -j DROP
iptables -A FORWARD -p tcp -i eth0 --dport 139 -m recent --name badguy --set -j DROP

Steve's ipt_recent website ( http://snowman.net/projects/ipt_recent/ ) also has some examples of usage.

/proc/net/xt_recent/*について  

アドレスリストは/proc/net/xt_recent/*に保持されています。それぞれのリストは各ファイルに対応します。Each file in /proc/net/xt_recent/ can be read from to see the current list or written two using the following commands

リストの編集  

モジュールパラメータ  

(訳注:カーネルモジュールであるxt_recentをロードする時にオプションを渡すことで動作を細かく制御することができます)
このモジュールが使用できるパラメータとディフォルトの値を示します。

パラメータ内容
ip_list_tot=100リストごとに記憶する最大数
ip_pkt_list_tot=20アドレスごとにカウントする最大パケット数
ip_list_hash_size=0Hash table size. 0 means to calculate it based on ip_list_tot, default: 512.
ip_list_perms=0644/proc/net/xt_recent/以下のファイルのパーミッション
ip_list_uid=0/proc/net/xt_recent/以下のファイルの所有者UID番号
ip_list_gid=0/proc/net/xt_recent/以下のファイルの所有者GID番号

コメント  

コメントなどありましたらお願いします。誤訳の指摘も歓迎です(^^)/