IPFW + NAT

FreeBSD 4.2R に用意されている IPFW(IP Firewall) と natd を使ってみました。

ipfw4.gif nat-config.gif

マシンの設定

FreeBSD 4.2R の Kern-Developer というセットをインストールします
/etc/inetd.conf から不要なものをコメントにします
ネットワークボードを2枚刺し、片方にグローバルの IP アドレスを、もう片方にプライベートの IP アドレスを割り当てます
IPFW と NAT を有効にするためにカーネルを作り直します

   #options        INET6            <= IPv6 を使わないなら不要
   #options        NFS              <= 有効にしてはいけない
   #pseudo-device   bpf             <= 有効にしてはいけない
   options         IPFIREWALL
   options         IPFIREWALL_VERBOSE
   options         IPFIREWALL_VERBOSE_LIMIT=100
   options         IPDIVERT         <= natd のために
   # 以下は必須ではありません(LINT を参照)
   options         ICMP_BANDLIM     <= ICMP パケットの帯域を制限
   options         TCP_RESTRICT_RST <= RST フラグの制限
   options         TCP_DROP_SYNFIN  <= SYN+FIN フラグの TCP パケットを落す

/etc/rc.conf を編集します

   firewall_enable="YES"
   firewall_type="open"
   natd_enable="YES"
   natd_interface="xl0"       <= グローバル側のインターフェイス
   natd_flags="-f /etc/natd.conf"
   gateway_enable="YES"       <= 2つのインターフェイス間にパケットを流す
   portmap_enable="NO"
   tcp_restrict_rst="YES"     <= TCP_RESTRICT_RST を有効にする
   tcp_drop_synfin="YES"      <= TCP_DROP_SYNFIN を有効にする

/etc/natd.conf はこうです

   log no                 <= yes なら /var/log/alias.log に記録される
   verbose no             <= 起動時には no
   deny_incoming no       <= yes なら外部からの接続を拒否
   log_denied yes         <= 拒否したパケットを記録
   log_facility security  <= デフォルトは daemon
   use_sockets yes        <= socket を使って NAT の失敗を回避
   same_ports yes         <= できるだけ同じポート番号を使用
   unregistered_only yes  <= プライベートアドレスだけを変換

作業は全てコンソールで行います

プライベート側のマシンは、ファイアウォールマシンを gateway に指定するだけです

実際に運用する場合には、コンパイラやカーネルのソースなどは全て 削除しておきます

IPFW

IPFW(IP Firewall) はパケットフィルタです
IP 層に出入りするパケットを選択的に破棄することができます
ipfw というコマンドでルールを動的に変更できます
ルールを通過したパケットは通常のネットワークの処理が継続されるだけです
ルールは1セットだけなので、入力パケットも出力パケットも同じルールで検査されます

NAT

IPFW と協調して動作します
natd というプログラムがパケットを書き換えます
パケットを書き換えて、片方の IP アドレス(普通はプラベートアドレス)をもう片方の IP アドレス(普通はグローバルアドレス)に変換します
ipfw ルールの中の divert という命令で実行されます
verbose を有効にすると、アドレスを書き換える様子がモニタできます

ファイアウォール?

以上の設定で、一応それらしい動きはしますが、完成ではありません
firewall_type="open" の場合のルールはこうなっています

   # ipfw -a list    <= ルールの一覧を見る
                    表示は(ルール番号)(パケット数)(バイト数)(ルール)の順
   00050 xxxxx xxxxxx divert 8668 ip from any to any via xl0
   00100 xxxxx xxxxxx allow ip from any to any via lo0
   00200 xxxxx xxxxxx deny ip from any to 127.0.0.0/8
   65000 xxxxx xxxxxx allow ip from any to any
   65535 xxxxx xxxxxx deny ip from any to any

これは、(必要なら)アドレス変換をしてパケットを素通しにするという設定です
実際どうなるかというと、

       * プライベート側から外へはほとんど不自由なくアクセスできる
       * 外側から見ると、全てのアクセスはファイアウォールのマシンから来たように見える
       * 外側からはプライベートのマシンへは直接アクセスできない
       * 外側からファイアウォールのマシンへは自由にアクセスできる 

ファイアウォールのマシンを守るための設定が欠けています

設定のヒント

/etc/rc.firewall を参考にします
natd に -v オプションを付けて起動すると、ルールの divert を通過するパケットが表示されます

   # killall natd
   # /sbin/natd -f /etc/natd.conf -n xl0 -v

表示が多過ぎる場合には、divert の前に deny のルールを追加して不要なパケットを破棄します

   # ipfw add 50 deny all from any to 224.0.0.0/24
   # ipfw add 60 deny udp from any 137-139 to any
   # ipfw add 70 deny udp from any to any 137-139

ルールの allow や deny の後に log を指定すると、そのルールにマッチしたパケットが表示されます
IPFW のルールは動的に変更できます
単純なルールに少しづつルールを追加しましょう
内側のマシンが外部から直接アクセスできるようにするには、redirect_port を natd.conf に追加します
例えば、内部に web サーバを配置する場合(この構成は危険です)は、こう書きます

   redirect_port tcp サーバアドレス:80 80

natd の設定を変更したら、natd を再起動しなければなりません

設定例

これを /etc/ipfw.nat などいう名前で作成します

# IPFW + NAT 用のルール設定スクリプト(静的ルール)

# Suck in the configuration variables.
if [ -r /etc/defaults/rc.conf ]; then
        . /etc/defaults/rc.conf
        source_rc_confs
elif [ -r /etc/rc.conf ]; then
        . /etc/rc.conf
fi

fwcmd="/sbin/ipfw -q"

# ルールを全部捨てる
${fwcmd} -f flush

############################
# Internet (外)側のインターフェイス
oif="xl0"
onet="1.2.3.0/24"
oip="1.2.3.4"

# プライベート(内)側のインターフェイス
iif="xl1"
inet="192.168.0.0/24"
iip="192.168.0.1"

############################
# ループバックへのルール
${fwcmd} add pass all from any to any via lo0
${fwcmd} add deny all from any to 127.0.0.0/8

# 断片化されたパケットを破棄
${fwcmd} add deny ip from any to any via ${oif} frag

# NetBIOS を破棄
${fwcmd} add deny udp from any 137-139 to any
${fwcmd} add deny tcp from any 137-139 to any
${fwcmd} add deny udp from any to any 137-139
${fwcmd} add deny tcp from any to any 137-139

# 偽装されたパケットを破棄
${fwcmd} add deny all from ${inet} to any in via ${oif}
${fwcmd} add deny all from ${onet} to any in via ${iif}

# プライベートアドレスやマルチキャストなどの破棄
${fwcmd} add deny all from any to 10.0.0.0/8 via ${oif}
${fwcmd} add deny all from any to 172.16.0.0/12 via ${oif}
${fwcmd} add deny all from any to 192.168.0.0/16 via ${oif}
${fwcmd} add deny all from any to 0.0.0.0/8 via ${oif}
${fwcmd} add deny all from any to 169.254.0.0/16 via ${oif}
${fwcmd} add deny all from any to 192.0.2.0/24 via ${oif}
${fwcmd} add deny all from any to 224.0.0.0/4 via ${oif}
${fwcmd} add deny all from any to 240.0.0.0/4 via ${oif}

############################
# NAT 
${fwcmd} add divert natd all from any to any via ${natd_interface}

# プライベートアドレスやマルチキャストなどの破棄
${fwcmd} add deny all from 10.0.0.0/8 to any via ${oif}
${fwcmd} add deny all from 172.16.0.0/12 to any via ${oif}
${fwcmd} add deny all from 192.168.0.0/16 to any via ${oif}
${fwcmd} add deny all from 0.0.0.0/8 to any via ${oif}
${fwcmd} add deny all from 169.254.0.0/16 to any via ${oif}
${fwcmd} add deny all from 192.0.2.0/24 to any via ${oif}
${fwcmd} add deny all from 224.0.0.0/4 to any via ${oif}
${fwcmd} add deny all from 240.0.0.0/4 to any via ${oif}

# 接続された TCP パケットを許可
${fwcmd} add pass tcp from any to any established

# 外部からの SSH の接続開始を許可
${fwcmd} add pass tcp from any to ${oip} 22 setup

# 外部からの SMTP の接続開始を許可
${fwcmd} add pass tcp from any to ${oip} 25 setup
# IDENT には答えない
${fwcmd} add reset tcp from any to any 113

# DNS サーバを立てている場合
${fwcmd} add pass tcp from any to ${oip} 53 setup
${fwcmd} add pass udp from any to ${oip} 53
${fwcmd} add pass udp from ${oip} 53 to any

# WWW サーバを立てている場合
${fwcmd} add pass tcp from any to ${oip} 80 setup

# それ以外の外側からの TCP 接続を拒否し、ログに残す
${fwcmd} add deny log tcp from any to any in via ${oif} setup

# 上記以外(つまり内側から)の TCP の接続開始を許可
${fwcmd} add pass tcp from any to any setup

# 外部への DNS の問い合わせとその応答を許可
${fwcmd} add pass udp from any to any 53
${fwcmd} add pass udp from any 53 to any

# 外部の NTP サーバへの参照
${fwcmd} add pass udp from any 123 to ${oip}
${fwcmd} add pass udp from ${oip} to any 123

# 内部から外部への ping とその応答のみ許可
${fwcmd} add pass icmp from any to any via ${iif}
${fwcmd} add pass icmp from any to any out via ${oif} icmptypes 8
${fwcmd} add pass icmp from any to any in via ${oif} icmptypes 0

# RFC2979
${fwcmd} add pass icmp from any to any in via ${oif} icmptypes 3

# それ以外の ICMP は破棄し、ログを取る
${fwcmd} add deny log icmp from any to any

# それ以外は拒否

ファイルを修正したら、設定してみます

   # sh ipfw.nat
   # ipfw -a list

動作を確認したら、/etc/rc.conf に追加します

   firewall_script="/etc/ipfw.nat"

プロキシとの併用

外部からのアクセスを受け付ける場合、FWTK などと併用すると良いでしょう

まとめ

   [good] IPFW の設定は、内部から外部へのアクセスだけなら簡単 
   [nogood] 外部からのアクセスを受け付ける場合の設定は難しい 
   [nogood] 内部と外部の間でどういう接続がされているかがファイアウォールのマシンでモニタできない 
   [good] 内部のユーザにはルータの様に見えるので、壁を(ほとんど)意識しないで済む 
   [good] マシンのリソースをほとんど消費しない(カーネル+natd(1プロセス)) 
   [good] パケット処理が高速 
   [good] 内部に DNS サーバがなくても良い(外部のサーバを直接参照できる)

添付ファイル: fileipfw4.gif 2468件 [詳細] filenat-config.gif 2359件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2004-10-29 (金) 14:31:12 (5494d)