2007年12月27日木曜日

ipfw2設定録

2004/10/01作成

ipfwは結構自由な文法なのに、Webに落ちているサンプルはみんなsrc port dst port のようなサンプルばかりです。マニュアルにも『下位互換のために src port dst port のセクションはあります』と書いてあるので、src port dst port を使わない設定をしてみました。加えて、『natdとkeep-stateをあわせて使うのは難しい』ということなので、いろいろ調べてみると、ipfw メーリングリスト に『skiptoを使え』と書いてあったので、skiptoを使った設定も作ってみました。

IPFWではNATを行う位置が入るときと出るときで異なるため、サンプルにあるようなルールではstateのチェックがうまく行えません。ルールを作るために、NATがあるなしでパケットを4つに分けて考えます。

内側から外側へのパケット(NATあり
外側から内側へのパケット(NATあり
外側とBSDのパケット(NATなし
内側とBSDのパケット(NATなし

順番に考えてみると

内側から外側へのパケット(NATあり
この場合は内側IFではNATを行わずルールをチェックします。
外側IFではNATを行いルールをチェックします。

外側から内側へのパケット(NATあり
この場合は外側IFでNATを行いルールをチェックします。
内側IFではNATを行わずルールをチェックします。

ここでの問題は、内側から外側の場合と外側から内側の場合でNATを行う順番が異なるということです。

外側から内側へのパケット(NATあり


外側IF ---- グローバル to グローバル

ipfw 外側----------------------------------------
NATを行う ---- グローバル to ローカル
ルールチェック、state保存---- グローバル to ローカル
ipfw 外側----------------------------------------

BSD

ipfw 内側----------------------------------------
NATを行わない ---- グローバル to ローカル
ルールチェック、state保存 ---- グローバル to ローカル
ipfw 内側----------------------------------------

内側IF ---- グローバル to ローカル


内側から外側へのパケット(NATあり


内側 ---- ローカル to グローバル

ipfw 内側----------------------------------------
NATを行わない ---- ローカル to グローバル
ルールチェック、state保存 ---- ローカル to グローバル
ipfw 内側----------------------------------------

BSD ---- ローカル to グローバル

ipfw 外側----------------------------------------
NATを行う ---- グローバル to グローバル
ルールチェック、state保存 ---- グローバル to グローバル
ipfw 外側----------------------------------------

外側 ---- src-ip グローバル to dst-ip グローバル

外側から内側の場合は、NATで変換した『グローバル to ローカル』てとしてstateをチェックしています。しかし、内側から外側の場合ではNATで変換してからsteteをチェックしてるため『グローバル to グローバル』となります。そのため、外側IFを通る内側と外側の通信はcheck-stateに引っかかりません。色々調べましたがどうしようもないようなので、内側と外側の通信は、内側IFでstateのチェックとフィルタをかけ、外側ではNATとstateのチェックを行わないフィルタをかけるようにします。


外側へのパケット(NATなし
外側でstateのチェックを行うフィルタをかけます。

内側へのパケット(NATなし
基本的に大丈夫だと思うのでstateのチェックを行わないフィルタをかけます。


この考えででルールを作成して行きます。
ルールの作成にあたっては、とりあえず次の4パターンに分けて作成し後でまとめることにします。

1.『内側と外側』の場合のルールの内側IF
2.『内側と外側』の場合のルールの外側IF
3.『内側とBSD』の場合のルール
4.『外側とBSD』の場合のルール

それぞれ実際のルールを作成していきます。

iif="rl0" 内側のIF
inet="192.168.0.0/24" 内側のネットワーク
iip="192.168.0.1" 内側のIFのアドレス
oif="tun0" 外側のIF

1.『内側と外側』の場合のルールの内側IF
このルールの目的はstateのチェックです。

# iifからのローカル宛、iifへのローカル発以外のパケットを拒否します
${fwcmd} add 2000 deny log not src-ip ${inet} in recv ${iif}
${fwcmd} add 2000 deny log not dst-ip ${inet} out xmit ${iif}

# stateをチェックします
${fwcmd} add 1000 check-state src-ip ${inet} in recv ${iif}
${fwcmd} add 1000 check-state dst-ip ${inet} out xmit ${iif}

# それ以外のtcpセッションが確立しているパケットが入ってくるのを拒否します
${fwcmd} add 2000 deny log proto tcp established src-ip ${inet} in recv ${iif}
${fwcmd} add 2000 deny log proto tcp established dst-ip ${inet} out xmit ${iif}

# その他はすべてstateをチェックします
${fwcmd} add 3000 pass setup keep-state in recv ${iif}
${fwcmd} add 3000 pass setup keep-state out xmit ${iif}


2.『内側と外側』の場合のルールの外側IF
このルールの目的はnatと確立していないパケットのチェックです。

# tcpセッションが確立していないパケットが入ってくるのを拒否します
# 内側宛て、BSD宛てかは不明確です。
${fwcmd} add 1000 deny log proto tcp not established in recv ${oif}

# NAT出て行く場合
${fwcmd} add 2000 divert natd out src-ip ${inet} recv ${iif} xmit ${natd_interface}
# NAT入ってくる場合
# 内側宛て、BSD宛てかは不明確です。
${fwcmd} add 2000 divert natd in recv ${natd_interface}

# その他は許可しstateをチェックします※意味があるのかどうか不明
${fwcmd} add 3000 pass out recv ${iif} xmit ${oif}


3.『内側とBSD』の場合のルール
このルールの目的は不要なパケットの拒否です。

# inetからiip宛て、iipからinet宛てのパケットを許可します
${fwcmd} add 1000 pass src-ip ${inet} dst-ip ${iip} in recv ${iif}
${fwcmd} add 1000 pass dst-ip ${iip} dst-ip ${inet} out xmit ${iif}

# それ以外をすべて拒否します
${fwcmd} add 2000 deny log



4.『外側とBSD』の場合のルール
このルールの目的は不要なパケットの拒否とstateのチェックです。

# stateをチェックします
${fwcmd} add 1000 check-state in recv ${oif}

# それ以外のtcpセッションが確立しているパケットが入ってくるのを拒否します
${fwcmd} add 1100 deny log proto tcp established in recv ${oif}

# imapでの接続を許可します
${fwcmd} add 2000 pass proto tcp dst-port 143 in recv ${oif} setup keep-state

# smtpでの接続を許可します
${fwcmd} add 3000 pass proto tcp dst-port 25 in recv ${oif} setup keep-state

# popでの接続を許可します
${fwcmd} add 4000 pass proto tcp dst-port 110 in recv ${oif} setup keep-state

# sshでの接続を許可します
${fwcmd} add 5000 pass proto tcp dst-port 22 in recv ${oif} setup keep-state

# wwwでの接続を許可します
${fwcmd} add 6000 pass proto tcp dst-port 80 in recv ${oif} setup keep-state

# ftpでの接続を許可します
${fwcmd} add 7000 pass proto tcp dst-port 20-21 in recv ${oif} setup keep-state

# BSDから外向けはすべて許可します
${fwcmd} add 8000 pass proto tcp out xmit ${oif} setup keep-state
# UDPも同じくすべて許可します
${fwcmd} add 8000 pass proto udp out xmit ${oif} setup keep-state

# それ以外をすべて拒否します
${fwcmd} add 9000 deny log


これらをマージするのですが、問題なのは、外側IFでの処理です。外側IFでは全てのパケットは外側IFあてのパケットなので、NATを行ってから出ないと内側へのパケットかどうか判断出来ません。そのため、外側IFでは全てNATを行います。NATを行った結果、内向きのパケットについてはstateのチェックが行えないため全てpassさせ内側のIFへ通します。

内側IFと外側IFはskiptoで分岐します。
外側IF用ルールは10000
内側IF用ルールは20000
で設定します。


iif="rl0"
inet="192.168.0.0/24"
ibca="192.168.0.255"
iip="192.168.0.1"
oif="tun0"

setup_loopback

# Allow IP fragments to pass through
${fwcmd} add 1000 pass frag

##############################################################
# 外側IFと内側IFの分岐
##############################################################

# 外側IF
${fwcmd} add 9000 skipto 10000 in recv ${oif}
${fwcmd} add 9000 skipto 10000 out xmit ${oif}
# 内側IF
${fwcmd} add 9000 skipto 30000 in recv ${iif}
${fwcmd} add 9000 skipto 30000 out xmit ${iif}

##############################################################
# 外側IF
##############################################################

# とりあえず色々拒否しておきます
# 外からNATの前に内側宛てパケットはありえないから問題なし
# 内側から内側宛てパケットはありえないから問題なし
# Stop RFC1918 nets on the outside interface
# Stop draft-manning-dsua-03.txt (1 May 2000) nets (includes RESERVED-1,
# DHCP auto-configuration, NET-TEST, MULTICAST (class D), and class E)
# on the outside interface
${fwcmd} add 10000 deny log dst-ip 10.0.0.0/8
${fwcmd} add 10000 deny log dst-ip 172.16.0.0/12
${fwcmd} add 10000 deny log dst-ip 192.168.0.0/16
${fwcmd} add 10000 deny log dst-ip 0.0.0.0/8
${fwcmd} add 10000 deny log dst-ip 169.254.0.0/16
${fwcmd} add 10000 deny log dst-ip 192.0.2.0/24
${fwcmd} add 10000 deny log dst-ip 224.0.0.0/4
${fwcmd} add 10000 deny log dst-ip 240.0.0.0/4

# NetBIOSを拒否します
${fwcmd} add 10100 deny log proto udp src-port 137-139
${fwcmd} add 10100 deny log proto tcp src-port 137-139
${fwcmd} add 10100 deny log proto udp dst-port 137-139
${fwcmd} add 10100 deny log proto tcp dst-port 137-139

##############################################################
# NAT関係

# NATします
${fwcmd} add 11000 divert natd via ${natd_interface}

# NATの結果、内側宛て以外つまり『外側とBSD』の入ってくる場合はskiptoします
${fwcmd} add 11100 skipto 20000 not dst-ip ${inet} in

##############################################################
# 『外側と内側』
# 『外側とBSD』の出て行く場合

# tcpセッションが確立していないパケットが入ってくるのを拒否します
${fwcmd} add 12000 deny log proto tcp not established in

# その他は許可します※NATを信用します
# 出て行くパケットは全部許可です
# 『外側と内側』『外側とBSD』の区別はつかないので
# 『外側とBSD』の出て行く場合のためにkeep-stateをしておきます
${fwcmd} add 13000 pass keep-state

# それ以外をすべて拒否します
${fwcmd} add 19999 deny in recv ${oif}
${fwcmd} add 19999 deny out xmit ${oif}

##############################################################
# 『外側とBSD』の入ってくる場合

# stateをチェックします
${fwcmd} add 20000 check-state
# それ以外のtcpセッションが確立しているパケットが入ってくるのを拒否します
${fwcmd} add 20100 deny log proto tcp established

# imapでの接続を許可します
${fwcmd} add 21000 pass proto tcp dst-port 143 setup keep-state

# smtpでの接続を許可します
${fwcmd} add 22000 pass proto tcp dst-port 25 setup keep-state

# popでの接続を許可します
${fwcmd} add 23000 pass proto tcp dst-port 110 setup keep-state

# sshでの接続を許可します
${fwcmd} add 24000 pass proto tcp dst-port 22 setup keep-state

# wwwでの接続を許可します
${fwcmd} add 25000 pass proto tcp dst-port 80 setup keep-state

# ftpでの接続を許可します
${fwcmd} add 26000 pass proto tcp dst-port 20-21 setup keep-state

# それ以外をすべて拒否します
${fwcmd} add 29999 deny log not dst-ip ${inet} in recv ${oif}

##############################################################
# 内側IF
##############################################################

# iifからのローカル宛、iifへのローカル発以外のパケットを拒否します
${fwcmd} add 30000 deny log not src-ip ${inet} in recv ${iif}
${fwcmd} add 30000 deny log not dst-ip ${inet} out xmit ${iif}

# 内側間での通信の場合すべて許可します
${fwcmd} add 31000 pass src-ip ${inet} dst-ip ${inet}

# ブロードキャストも許可します
${fwcmd} add 31000 pass src-ip ${inet} dst-ip ${ibca}

# それ以外の場合、つまり外側との通信の場合stateをチェックします
${fwcmd} add 32000 check-state
# それ以外のtcpセッションが確立しているパケットが内側にへ出て行くのを拒否します
${fwcmd} add 32100 deny log proto tcp established out

# それ以外をすべて許可します
${fwcmd} add 33000 pass keep-state

# それ以外をすべて拒否します
${fwcmd} add 39999 deny in recv ${iif}
${fwcmd} add 39999 deny out xmit ${iif}
;;


※設定は動作はしますが、keep-stateの確認はしてないので、こちらの思い通りに動いているのかは分かりません。前提となっているkeep-stete考えはあくまで予想です。確認等はしていないので、間違っているかも知れません。


0 件のコメント: