iptablesで動的アドレスリストを扱う
2012-02-08 : 作成
iptablesで動的に拒否/許可アドレスを指定したい場合に便利なモジュールrecentですが、ドキュメントがあまり見当たらなかったのでまとめてみました。また、他のスクリプト等からこのリストの内容にアクセスする方法を説明します。
recentモジュール
一般にiptablesではチェインを複数指定することでIPアドレスのリストを拒否したり許可したりしますが、recentモジュールを使うことでそのリストを動的に扱うことができます。SSHなどへのbrute force attackへの対策としてよく使われているようです(ある時間当たり一定数以上のアクセスがあったら遮断、など)。これはrecentモジュールがアクセス元のリストを保持しており、規定時間以内のアクセス数の条件判定ができるためですが、そのリストは柔軟に扱うことが可能で、スクリプトなどからも操作することができるようです。
そのリストをスクリプトなどから使用する方法を示したサイトが見つからなかったのでメモしておきます。なお、SSHへのbrute force attack対策としてrecentモジュールを使った方法は以下のページなどで詳しく解説されています。以前のバージョンで解説されている場合はipt_recentになっていることがあります(現在はxt_recent)。
- iptablesのipt_recentでsshのbrute force attack対策
- sshのbrute forceアタックパケットの制限 -- DOS的パケットをフィルタリングする
- Debian(lenny)でもSSH総当たり防止策をiptablesの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:)にアクセスするとアクセスがブロックされるはずです。
実環境のためのヒント
上の例でも期待される動作は実現可能だと思いますが、実際に使う場合は以下のポイントを自分の環境に合わせるといいと思います。
- apacheの設定
- ここではLocationMatchとErrorDocumentを組み合わせていますが、mod_rewriteを使って遮断スクリプトに飛ばす方法も考えられます。むしろその方がスマートかもしれませんね。
- 遮断スクリプト
- アタックの頻度が低いサーバではパフォーマンスをあまり気にしなくていいかもしれませんが、高負荷サーバの場合は配慮が必要かもしれません。また、スクリプト内部でもう少し細かい条件設定なども可能なはずです。
- xt_recentのオプション
- ここでは/proc/net/xt_recent/apacheファイルのパーミッションを直接変更しましたが、このやり方だとiptablesを再起動したときにいちいち変更する必要があります。解決方法として以下の事が考えられます。
- sudoでroot権限で書き込む
- xt_recentの起動オプションを指定しておく(後述)
- ここでは/proc/net/xt_recent/apacheファイルのパーミッションを直接変更しましたが、このやり方だとiptablesを再起動したときにいちいち変更する必要があります。解決方法として以下の事が考えられます。
- 応用
- 紹介したのは遮断する方法ですが、逆にHTTPで認証してからポートを開く、ということも可能ですね。いわゆるポートノッキングよりは認証などの応用が利くのでセキュアかも。
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.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パラメータで指定された値で、この値を超えて指定するとルールは拒否されます |
--rttl | This 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
リストの編集
- アドレスをDEFAULTリストに追加する
echo +addr > /proc/net/xt_recent/DEFAULT
- アドレスをDEFAULTリストから削除する
echo -addr > /proc/net/xt_recent/DEFAULT
- DEFAULTリストの内容を消去する
echo / > /proc/net/xt_recent/DEFAULT
モジュールパラメータ
(訳注:カーネルモジュールであるxt_recentをロードする時にオプションを渡すことで動作を細かく制御することができます)
このモジュールが使用できるパラメータとディフォルトの値を示します。
パラメータ | 内容 |
ip_list_tot=100 | リストごとに記憶する最大数 |
---|---|
ip_pkt_list_tot=20 | アドレスごとにカウントする最大パケット数 |
ip_list_hash_size=0 | Hash 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番号 |
コメント
コメントなどありましたらお願いします。誤訳の指摘も歓迎です(^^)/