2011/01/09

IPSetを使ったFirewallルールの設定について

以前家のブロードバンドルータにしているAlixにIPSetを導入するところまでを、「ブロードバンドルータにしているalixをDebian squeezeにして、ipsetを導入する」に投稿していました。

しばらく前にいわゆるボットネットの一部といわれているIPとの接続を弾くためにiptablesルールを設定仕直したので、そのログをまとめておきます。

現状のiptablesの設定状況

IPSetの導入と平行してiptablesルールの見直しを行なって、起動時にはiptables-restoreを使ってルールを設定しています。

設定のメインは/etc/network/if-pre-up.d/iptablesスクリプトで、if-pre-up.dディレクトリにあるスクリプトは起動時などifupによってネットワークデバイスが設定される直前に実行されます。

本当は重複して実行される可能性のある/etc/network/if-*.dに配置するのはスマートじゃないけど、iptables/ipsetの設定はNICが認識されているかどうかに無関係に実行できるので、重複起動は気にしないことにしました。

他には/etc/rc2.d/なんかにスクリプトを配置することもできると思います。 ここら辺はdebianで標準的な場所がないので、ホストの使い方によって変化するかもしれません。

もしWorkstationならネットワークデバイスの初期化云々はそれほど重要ではないので、/etc/rc.local辺りで設定をするかもしれません。

常時接続のルータは意図せずiptables設定なしにネットワークに接続するのは嫌なので、if-pre-up.dを使いました。

iptables設定スクリプトと参照するファイル群

このスクリプトの配置場所はいくつかの候補の中から選択するしかありませんが、ファイル名と内容は自由に書けるので、内部でiptables-restoreコマンド等を実行するようにしました。

スクリプトの中身は次のようになっています。

/etc/network/if-pre-up.d/iptablesファイル全体

#!/bin/bash

PATH=/sbin:/usr/sbin
BASEDIR="$(dirname $0)"

ipset --restore < "${BASEDIR}/../ipset.restored"
iptables-restore < "${BASEDIR}/../iptables.restored"
ip6tables-restore < "${BASEDIR}/../ip6tables.restored"

スクリプトでは/etc/network/直下に3つのファイルがある事を前提にしています。

  • /etc/network/ipset.restored
  • /etc/network/iptables.restored
  • /etc/network/ip6tables.restored

今回はip6tablesは範囲外です。ipsetとiptablesの連携について書いていきます。

iptablesとの連携

直接"iptables.restored"ファイルを書く事もできますが、いろいろ危険なのでiptablesコマンドを使って設定した後に"iptables.restored"ファイルを保存しています。

ipsetとの連携は、まずiptables側で空のipsetルールを作成して、それを参照するiptablesルールを加えます。

ipset,iptablesコマンドを使った空のipsetルールを使ったルール作り

#!/bin/bash
PATH=/sbin:/usr/sbin:/bin:/usr/bin

## delete all rules
iptables -F
iptables -t nat -F
iptables -X
iptables -t nat -X
iptables -Z
ipset -F
ipset -X
...
ipset -N denyip iphash --hashsize 36864
ipset -N denynet nethash --hashsize 36864
iptables -A INPUT -m set --match-set denyip src -j DROP
iptables -A INPUT -m set --match-set denynet src -j DROP
iptables -A FORWARD -m set --match-set denyip src -j DROP
iptables -A FORWARD -m set --match-set denynet src -j DROP
iptables -A OUTPUT -m set --match-set denyip dst -j DROP
iptables -A OUTPUT -m set --match-set denynet dst -j DROP
iptables -A FORWARD -m set --match-set denyip dst -j DROP
iptables -A FORWARD -m set --match-set denynet dst -j DROP
...

このスクリプトを実行して、動きに問題がない事を確認してからiptables.restoredファイルを作成しておきます。

$ sudo /sbin/iptables-save > /etc/network/iptables.restored

次にipsetを使い、定義だけされている空のipsetルールに具体的な設定を加えていきます。

まず用意するのはボットネットに組み込まれていると思われるIPアドレスのリスト。

iptables.deny.outboundファイル抜粋

##
## comment string, this line should begin with the '#' char.
109.10x.23x.1xx
...
109.23x.22x.0/24
...

このファイルを処理する

#!/bin/bash
PATH=/sbin:/usr/sbin:/bin:/usr/bin
BASEDIR="$(dirname $0)"
OUTBOUND_BLOCK_FILE="${BASEDIR}/iptables.deny.outbound"

ipset -N tmpip iphash --hashsize 36864
ipset -N tmpnet nethash --hashsize 36864
if test -f "${OUTBOUND_BLOCK_FILE}"; then
  while read ipaddr
  do
    ( echo ${ipaddr} | egrep ^# > /dev/null ) && continue
    ip="$(echo ${ipaddr}|awk -F/ '{print $1}')"
    mask="$(echo ${ipaddr}|awk -F/ '{print $2}')"
    if test "${ip}" != "" -a "${mask}" = "" ; then
      ipset -A tmpip "${ipaddr}"
    else
      ipset -A tmpnet "${ipaddr}"
    fi
  done < "${OUTBOUND_BLOCK_FILE}"
fi
ipset -W denyip tmpip
ipset -W denynet tmpnet
ipset -X tmpip
ipset -X tmpnet

設定したipsetルールは--saveオプションを使って保存します。

$ sudo ipset --save > etc/network/iptables.restored

こういう形でスクリプトを分けたのにはいくつか理由がありますが、ipset/iptablesコマンドを利用して大量のIPアドレスを処理するルールを追加するのにはかなり時間がかかるので処理を分けたかったのが大きな理由です。

次はipsetを使ってみたかったということ。

iptablesを使う事も可能ですが、空のipsetルールを設定したように、空のchainを作成しておいて、後からそのchainに個別のIPアドレスをDROPするルールを追加していくのが良いでしょう。

そうしないと一連のiptablesコマンドの実行が完了するまでの間は、ネットワークに対して脆弱なまま接続する事になるかもしれません。

もちろん直接iptables-restoreコマンドが解釈するようなファイルを生成して時間を短縮することは可能です。

いずれの方法でも、テストを十分にして、いきなり起動時に読み込まれるファイルとして保存しない事が重要です。

ここでリストの挙げられているIPアドレスからの接続を拒否することよりも、そういったサイトへの接続を拒否する事の方が重要です。 もっとも接続してはいけないマスター系ノードのIPアドレスは変化するでしょうし、知られていないものがあるでしょうから、本当にこういうリストが有用なのかは少し疑問が残ります。

何もしないよりはましかな。

0 件のコメント: