« | »

2012.02.16

FreeBSD 8.xで Bridge / NAT / Firewallの構築

出入り口のゲートウエイ・サーバーを更新した。
FreeBSDでフィルタリング・ブリッジを構成しているところは少ないみたいで、探しても意外と出てこないので忘れないように覚書き。

構成は図の通りで最低限の要件としては

  1. DMZのサーバー群はグローバル IPが直接振れること
  2. 当然 DMZはファイヤウォールの内側でゲートウエイで一括してポリシーを管理できること
  3. LAN側は NATを用いて DMZ や外に出ていけること
  4. 当然 LANもファイヤウォールの内側でゲートウエイで一括してポリシーを管理できること
  5. ゲートウエイ・サーバは多重化できること

後、余力があれば

  1. 経路の管理
  2. DNSの提供
  3. Transparent Proxyで squid経由のコンテンツフィルタへのパケット制御

 今回は手元の QuadCore Xeonのサーバーで構築したので余力の部分も余裕で行けるな。

うちの会社の場合、WAN側回線は ethernet直結なので yamahaの RTX1xxxなどのリモートルーターを使って構成するのは少々やりにくい(RTX1xxxではフィルタリング・ブリッジができないので DMZを NIC1のグローバルと同一のセグメントに配置できない。pppoeを使った接続ならできるけど)。上位の RTや cisco pixならなんとかなるけど、もうコスト的に全然無理なのでいつもの通り FreeBSDでフィルタリング・ブリッジ / NAT を構成することにする。
NTT西日本のフレッツ・プレミアムなどで固定 IP運用しているように CTUを間に挟むタイプの接続方式でも、WAN側は見かけ Ethernet直結になるのでこの方法は使える(実際予備回線はプレミアム+固定 IP + FreeBSDのファイアーウォールで運用している)

FreeBSD 6.xまでは bridge(4)に少々問題があって LAN / DMZ間の通信ができなくて、色々リレーしたりで結構面倒くさかったが 7.x以降の bridgeではこの辺りが改善されてなにも考えなくても期待通りの動作をしてくれる。ipfwもローダブル・モジュールになって GENERIC kernelに組み込まれているので squid transparent proxyをしなければ kernelの練り直しも必要なくなっている。
仮に外側の Globalアドレスを 172.16.0.1/28、LAN側を 192.168.1.254/24としたら transparent proxyをしなければ /etc/rc.confに

cloned_interfaces=\”bridge0\”
ifconfig_bridge0=\”addm NIC1 addm NIC2 up\”
ifconfig_NIC1=\”UP\”
ifconfig_NIC2=\”UP\”
ifconfig_NIC1=\”inet 172.16.0.1 netmask 255.255.255.240\”
ifconfig_NIC3=\”inet 192.168.0.254 netmask 255.255.255.0\”
natd_enable=\”YES\”
natd_interface=\”NIC1\”

を書いて、同じく /etc/sysctl.confに
net.link.bridge.ipfw=1

を追加すれば一応事足りる。firewallは sh rc.firewall SIMPLE でとりあえず必要十分か…(ところで 8.xの /etc/rc.d/ipfw にはちょっと問題があって ipdivertがうまく動かないのだがそれは最後に書いておく)

しかし….
今回はipfwで rule-based forwardingと default to acceptが必要となるので、まずは kernelのカスタマイズから。

GENERICの雛型から不要なデバイスを削除して
options IPFIREWALL
options IPFIREWALL_VERBOSE
options IPFIREWALL_FORWARD
options IPDIVERT
を追加して kernelをコンパイル。ipdivertはなくてもいいかと思ったらこれを入れないと rule-based forwardingが enableにならない。enableにならないまま ‘ipfw add fwd 127.0.0.1,3128 tcp from lan_network to any in recv NIC3’を実行したら
getsockopt(IP_FW_ADD): Invalid argument
が出て fwdできない
GENERIC kernelでは rule-based forwarding enableとなっていたので気がつかずに結構はまってしまった

firewallのルールは雛型が /etc/rc.firewallにあるのでこれの simpleの部分を利用して作る。前半部分の環境設定や flashのルールはそのままに bridgeのルールやローカル部分のルールを付け加えてて行く。

${fwcmd} -f flush
# まず最初に、rc.firewallから以下の各項目をコピー
# loopbackや spoofingの禁止などなど
# set these to your outside interface network
# set these to your inside interface network
# Stop spoofing
# Stop RFC1918 nets on the outside interface
# Stop draft-manning-dsua-03.txt

# divertを有効にする
 ${fwcmd} add divert natd ip4 from any to any via ${natd_interface}

# ここからは、再び rc.firewallから以下の各項目をコピー
# 外から入ってくる private領域などのアドレスを持ったものを廃棄したり
# dhcpやマルチキャストの廃棄
# Stop RFC1918 nets on the outside interface
# Stop draft-manning-dsua-03.txt

# 次に LAN→GLOBALへのルール
# squid transparent proxy
 # LANから80ポートへの接続は全て squidに転送
 ${fwcmd} add fwd 127.0.0.1,3128 tcp from ${inet} to any dst-port 80 in recv ${iif}
 # 禁止のルール
 # たとえば icmpの禁止
 ${fwcmd} add deny icmp form  ${inet} to any in recv ${iif}

# 続いて WAN側の設定
# Allow TCP through if setup succeeded
# Allow IP fragments to pass through

# ここから Bridgeした DMZのフィルタルール
 # 最後に layer2(bridged) と入れるのが肝まずは GLOBAL→DMZ
 # たとえば
 # sshdへの接続は許可
 ${fwcmd} add pass all from ${onet} to any 22 setup layer2
 # httpdへの接続は許可
 ${fwcmd} add pass all from any to any 80 setup layer2
 # と言うように必要なものを許可していく

# DMZ→GLOBAL
 ${fwcmd} add pass ip from ${onet} to any layer2

# 上記許可したもの以外の DMZ [へ|から] 接続は全て拒否
 ${fwcmd} add deny log tcp from any to any layer2
 ${fwcmd} add deny log udp from any to any layer2
 # deny log ip from any to any layer2でも良いけど
 # ICMPなんかも考えないと外から pingすら通らなくなる

# 続いてこのサーバーに対する接続の [許可|拒否]
# たとえば DNS
 ${fwcmd} add pass tcp from any to me 53 setup
 ${fwcmd} add pass udp from any to me 53
 ${fwcmd} add pass udp from me 53 to any
 # と言うように必要なものを許可していく
# 必要なものを許可したら、それ以外の外からの接続を一括して拒否する
 ${fwcmd} add deny log ip4 from any to any in via ${oif} setup proto tcp

    ${fwcmd} add pass tcp from any to any setup
    ${fwcmd} add pass udp from me to any 53 keep-state
    ${fwcmd} add pass icmp from any to any

# default denyで kernelを作っているのでルールの最後、ルール番号 65535で自動的に
# ${fwcmd} add deny ip from any to any
# が付け加えられるけど、ログが取れないので
 ${fwcmd} add deny log ip from any to any
# を最後のルールとして書いておく。

だいたいこんな感じかな。


さて、前の方に書いたけど、FreeBSD 8.xでファイアーウォールを組んでいる時に ipfw組み込みの natなら何もしなくてもちゃんと divertしてくれるけど、natdを使おうとしたら途端に起動される前に firewall ruleを読みこんでしまい divert socketが使えなくなってしまう問題がある。修正は FreeBSDのサイトにあるように /etc/rc.d/ipfwのスタートアップ・スクリプトを少々書き換える。
しかし、non-criticalって。修正は簡単だけどリモートサイトで運用している時には結構クリティカルな問題のような気がするんだけどなぁ。少なくとも 8.2ではまだ直って無い….

 

No tags for this post.

Trackback URL

Comment & Trackback

だいたいこれでもかなり強固なファイヤーウォールができるけど、攻撃検出と warnning、allow/denyリストのメンテナンス、監視体制なんかも組み合わせて設計しておかないと駄目ですよ (N)

Comment feed

Comment





XHTML: You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*
:-[ (B) (^) (P) (@) (O) (D) :-S ;-( (C) (&) :-$ (E) (~) (K) (I) (L) (8) :-O (T) (G) (F) :-( (H) :-) (*) :-D (N) (Y) :-P (U) (W) ;-)

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)