pfはOpenBSD由来のfirewallです。
FreeBSD8.2Rには現在、ipfw(ipfw2)、ipf(4.1.28)、pf(4.1系)と三種類のfirewallが組み込まれていますが、それらについて詳しくはFreeBSDのFirewallについてを参考にしていただくとして以下はpfの設定解説です。
pfはipfのような構文が使え、ipfより簡単な構文で様々な事柄をサポートしています。
その前に以下Firewallを使う上での基本事項をまとめておきました。
pfは構文及び機能がいろいろ複雑なため方針にあわせて参考にしてください。
今現在あるのは一般的なポート解放編&nat編、マルチホーム(複数経路)編の二つです。
pfの基本的な許可・拒否ルール以外の設定についてです。
pfのマニュアルにはいくつかの設定事項について書かれています。
日本語訳もされており、詳細は参考サイトを参照してみてください。
pfでは、setと呼ばれる構文があります。
これは、pfの動作設定の基本となります。
詳細はマニュアルを参考にしていただくとしてマニュアルではわかりにくかった部分を記述しています。
これは、パケットがどのような流れをするか決めることができます。
if-boundとfloatingがあり、初期設定はfloatingです。
floatingの状態では、Interfaceを超えたパケットの許可ができます。
反対にif-boundではinterface内だけでパケットが流れることを許可します。
つまりnic1からnic2へパケットがいかないようにするには標準のfloatingではなくif-boundを設定すべきです。
ファイアーウォール作成時のポリシーの問題だとは思いますが、個人的にはif-boundで設定しておき、InterfaceからInterfaceへ超えたいルールのみ個別にfloatingを設定する方針をとって説明しています。
pfを使ってSSHポート及びWebポート、そしてDNSポートのみ、そしてICMPのReplyを許可するだけに思います。
SSHは許諾したホストからのみにします。
# ネットワークカード名のエリアス
ext_if = "em0" # 外部と接続しているI/F名
#許可するポート
tcp_services = "{ 22, 80, 53}"
udp_services = "{ 53 }"
#許可するICMPパケットの種類
icmp_types = "{echoreq }"
icmp6_types = "{ echoreq, unreach, timex, toobig, neighbrsol, neighbradv }"
#プライベートネットワークの定数
table <priv_nets> const { 0/8, 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, \
169.254.0.0/16, 0.0.0.0/8, 192.0.2.0/24, 244.0.0.0/4, 240.0.0.0/4 }
#ICMPを許可するIPアドレス
table <icmp_allows> const { 198.51.100.2 }
# オプション
set timeout { interval 10, frag 30 }
set timeout { tcp.closing 900, tcp.finwait 45, tcp.closed 90 }
set timeout { adaptive.start 6000, adaptive.end 12000 }
# ブロックしたパケットは破棄
set block-policy drop
# 統計情報をとるインターフェース
set loginterface $ext_if
set fingerprints "/etc/pf.os"
# IF内にパケットを制約
set state-policy if-bound
# ルール開始時のおまじない
scrub in all
# lo0はすべて許可
pass quick on lo0 all
# すべて拒否したパケットは記録する(ルールにマッチしないパケットは拒否)
block in log all
block out log all
# *priv_netsからのパケットは拒否
block drop in log quick on $ext_if from <priv_nets> to any
block drop out log quick on $ext_if from any to <priv_nets>
# tcp_servicesで許可したポートへ接続を保持して許可
pass in quick on $ext_if inet proto tcp from any to ($ext_if) port $tcp_services flags S/SA keep state
# udp_servicesで許可したポートへ接続を保持して許可
pass in quick on $ext_if inet proto udp from any to ($ext_if) port $udp_services keep state
# 上記ルールにマッチしなかったTCP・UDPパケットはすぐに拒否
block drop in log quick on $ext_if proto tcp all
block drop in log quick on $ext_if proto udp all
# 特定のホストから特定のICMPパケットを接続を保持して許可
pass in quick on $ext_if inet proto icmp from <icmp_allows> to ($ext_if) icmp-type $icmp_types keep state
# マッチしなかったICMPパケットはすべて拒否
block drop in log quick on $ext_if proto icmp all
# 自らでていくTCPでかつS/SAパケットは接続を保持して許可
pass out quick on $ext_if proto tcp all modulate state flags S/SA
# 自らでていくUDP及びICMPパケットは接続を保持して許可
pass out quick on $ext_if proto { udp, icmp } all keep state
このように、pfは自らのIPアドレスを含めない書き方も可能です。
pf 4.1ではnat及びrdrはpass・block構文中に使うことはできません。
このため、設定としてnat及びrdrを先に書いておきそのあと該当の通信をpassしてあげないとうまく通信ができない場合があります。
IPv4でならIPFWのfwdを使って実現するのが比較的簡単だと思いますが、Forwardを使うにはIPFWはカーネルの再構築も必要なのでそれらも必要ないpfをおすすめします。
以下設定
reply-toとroute-toの設定がpass in及びpass outと異なることが要注意です。
応用で複数経路下で特定のNICから入ってきたパケットを別のサーバに転送(ポートリダイレクト)してしまう+NAT付きな設定方法です。
ちょっと複数経路にポートリダイレクト絡んでくると設定が複雑になります。
なぜかは、ポートリダイレクト先のIPアドレス宛のパケットも許容してあげないとうまく通信できません。
また、ポートリダイレクト先のIPアドレスから届いた応答パケットもif-boundを基本設定としている場合
パケットがblockされてしまう場合があるので例外でその規則だけfloatingとすべきなようです。
ちなみにOpenBSD最新版付属のpf 4.9系以降だと書式も変わってrdr-toなるものがpassでも使えるのでrdrを個別に書かなくてもさらに便利かもしれないです。
pfはログの見方がちょっと特殊です。なぜならtcpdumpを使ってログを閲覧するからです。
しかしこのことは、非常に強力な構文をサポートしていることになるのです。
ipfなどでは、ログをとった場合、tailやgrepなどを使って特定のホストやポートのみをリアルタイム表示ができましたが、tcpdumpの構文に従うためそれらを使うことなく見ることができます。