FIREWALL(7)

名称
firewall - FreeBSD で動作する簡単なファイアウォール

ファイアウォールの基礎
ファイアウォールは一般に、外部から行われるネットワーク内部への不正なアクセス を防ぐために使われます。また、LAN 内だけでサービスされるべき NFS や SMBFS のようなサービスに対して、内部の IP アドレスを偽装して外部から行わ れる攻撃を防ぐためにも用いられます。

また、 FreeBSD のファイアウォール機構は、 dummynet(4) を用いた帯域制限を行うこともできます。この機能は特に重要な目的のために 帯域幅を保証したい場合などに有効でしょう。たとえば、オフィスの T1 (1.5MBits/s) を用いてビデオ会議を行う場合に、他の通信を 1MBits/s までに押えて、 ビデオ会議用のコネクションに最低でも 0.5MBits/s を確保することができます。 また同様に、共用機器で有名なウェブサイトや FTP サイトを運用している場合には、 プロバイダからの高額な帯域課金を避けるために使うこともできます。

それから、 FreeBSD のファイアウォール機構はパケットが正しい到達先に行くようにパケットを divert したり、次のホップのアドレスを変更したりすることもできます。 パケットの divert は主に、プライベート IP アドレス空間から外部へのブラウズなど のアクセスを可能にする NAT (ネットワークアドレス変換) を実現するために 用いられます。

ファイアウォールを構築することは簡単なようですが、多くの人が間違いを犯し ています。最も多い間違いは、包括的なファイアウォールでなく、 排他的なファイアウォールを作ってしまうことです。排他的なファイアウォールは、 ルールセットに適合しなかったすべてのパケットを通過させるもので、 包括的なファイアウォールはルールセットにマッチしたパケットだけを通過させます。 包括的なファイアウォールのほうが、排他的なものよりもはるかに安全ですが、 正しく動くものを 作るのが難しくなります。次に多い間違いは、通過させたくないものすべてを 廃棄してしまうことです。TCP/IP が正常に動作するためには、 たとえば MTU ディスカバリの実装のように、 いくつかの ICMP エラーを必要とします。 同様に、多くのデーモンは、コネクションを要求するユーザを認証するために、 auth サービスに逆向きのコネクションを張ります。auth は危険ですが、正しい対応 はただパケットを廃棄するのでなく、TCP reset を返すようにすることです。 以下で示す、ファイアウォールのサンプルではこれらの事項を満たすように してあります。

IPFW を使うためのカーネルの設定
IP ファイアウォールの機能を使用するためには、 カスタムカーネルを作成する必要はありません。 /etc/rc.conf (後述) でファイアウォールを有効にすれば、 ipfw カーネルモジュールが必要な時に自動的にロードされます。 あなたが偏執したいならば、 IPFIREWALL オプションを設定することで、IPFW を直接 FreeBSD カーネルに組み込むこともできます。 このファイアウォールは、何も設定しないとすべてのパケットを通過させないように なっています。 /etc/rc.conf で、再起動時に適切なルールセットを読み込むようになっていないと、 コンソールに触ることができない場合、マシンにアクセスすらできなくなります。 また、新しいリリースのカーネルに更新する時に、 バイナリ (訳注: コマンドやライブラリのこと) を更新する前に リブートを実行してしまうことがよくあります。 この結果 ipfw(8) とカーネルが非互換になってしまい、ブートシーケンスで ipfw(8) が動作しないことにより、マシンにアクセスできなくなってしまいます。 このために、 IPFIREWALL_DEFAULT_TO_ACCEPT というカーネルオプションが用意されており、これによってファイアウォールの 初期状態をすべてのパケットを通過させる設定にすることができます。 しかし、 このオプションを設定することは、システムブート中の短期間、 ファイアウォールが全パケットを通すかもしれないことに注意してください。 それでも本オプションの使用は、 FreeBSD ファイアウォールに十分慣れるまでの期間には有用です。 どのように動作するかすべて分かったら、これを削除して、抜け穴を塞いてください。 第 3 のオプションとして、 IPDIVERT があります。これは、ファイアウォールがパケットをユーザプログラムに divert することができるようにするもので、 natd(8) によって、プライベートネットワークから外部へアクセスできるようにするとき に必要です。 トラフィックタイプによる帯域制限には、 ipfw pipe ルールを有効にするために、 DUMMYNET オプションが必要です。

IPFW によるファイアウォールの例
ここに示すのは、3 つのインタフェースカードをもつマシンで動作している ipfw ベースのファイアウォールの例です。 fxp0 が「外側の」LAN に接続されています。 この LAN 上のマシンは、10. で始まる内部 IP アドレスと、 インターネットにルーティングされる IP アドレスを持ち、 デュアルホームとなっています。 たとえば、 192.100.5.x がインターネットにルーティングされる IP ブロックを指し、 10.x.x.x が内部ネットワークを指します。 例として適切ではないかもしれませんが、 10.0.1.x が fxp0 の接続されている LAN のアドレス、 10.0.2.x が fxp1 の接続されている LAN のアドレス、 そして 10.0.3.x が fxp2 のものであるとします。

この例では、3 つの LAN すべてをインターネットから隔離し、またそれぞれを も隔離したいものとします。同時に、すべての内部アドレスから、この マシンで走っている NAT ゲートウェイを経由してインターネットへアクセスが 可能であるようにします。NAT ゲートウェイを動作させるためには、fxp0 に 内部アドレスの 10. のほかに、インターネットから見えるアドレスを持たせる 必要があります。このアドレス (ここでは示していません) が、このマシンの 公式なアドレスであり、もうひとつの外部から見えるアドレス (この例では 192.100.5.5 です) が NAT ゲートウェイとしてのアドレスとなります。 この例は、外部から見える LAN のマシンにも内部アドレス 10.0.0.x を割り当てること によって、すこし複雑になっています。しかしこの方法によって、 内部サービスは内部アドレスにのみバインドし、インターネットから守れます。 外から見える IP アドレスにバインドする サービスは、インターネットに対して公開しようとするものだけにするのです。

この例では、ネットワーク 10.0.0.x はファイアウォールによって保護されてい ません。このネットワークを外部からのアドレス偽装から守るために、 インターネットルータによる保護を確認して下さい。 また例では、外部から見えるホストが 内部 IP アドレスを通じてサービスを操作する場合、 内部のネットワークに非常に自由にアクセス可能としています。 この方法にはいくらかのセキュリティ上の危険が伴っており、 外部から見えるホストに問題がある場合には何が起きるかわかりません。 この危険を回避するためには、ルール 01010 と 01011 を削除して、 LAN0 経由で入ってくるものすべてを firewall を経由するようにするべきです。

また、この例では内部アドレス空間を使うことがファイアウォールによる保護機構 の重要な点であることに着目してください。適切なアドレス偽装対策を行うこ とにより、外部から、内部 (LAN1 および LAN2) のホストに直接アクセスするこ とは不可能となります。

    # /etc/rc.conf
    #
    firewall_enable="YES"
    firewall_type="/etc/ipfw.conf"
    # ファイアウォールを通過する一時的なポート割り当ての範囲を設定
    #
    # 注意 : ファイアウォールを通じて行われるサービスの負荷が高い場合には、
    # より広いポート割当の範囲を必要とすることになります。そのような際には
    # 4000-10000 や 4000-30000 がより良い選択でしょう。
    ip_portrange_first=4000
    ip_portrange_last=5000
    ...

    # /etc/ipfw.conf
    #
    # FIREWALL: ファイアウォール兼 NAT ゲートウェイ
    # LAN0	    10.0.0.X と 192.100.5.X (デュアルホーム)
    # LAN1	    10.0.1.X
    # LAN2	    10.0.2.X
    # sw:	    イーサネットスイッチ (管理対象外)
    #
    # 192.100.5.x は、インターネットから見える IP アドレス (インターネットから
    # ルーティングされる) を意味します。10.x.x.x は、内部 IP アドレス
    # (外からは見えない) を表します。
    #
    #   [LAN1]
    #      ^
    #      |
    #   FIREWALL -->[LAN2]
    #      |
    #   [LAN0]
    #      |
    #      +--> 外側のホスト A
    #      +--> 外側のホスト B
    #      +--> 外側のホスト C
    #      |
    #   インターネットルータ (2 番目のファイアウォール)
    #      |
    #    [インターネット]
    #
    # 注意！ ここには書かれていませんが、インターネットルータは発信元アドレスが
    # 10. であるパケットを許可しないように設定される必要があります。
    # これは、デュアルホームの 10.0.0.x ブロックを保護するためです。
    # そうでなければ、外部から見えるホストは、この例では守られていません。
    # これらのホストは、外部に見せるサービスのみを外部から見えるアドレスに
    # バインドすべきです。内部サービスは、安全に内部アドレスにバインド可能です。
    #
    # NAT ゲートウェイは、内部の IP アドレスから外部の IP アドレスへ
    # 向けて送られるパケットを、ポート 8668 で listen している natd に転送す
    # ることによって動作します。この動作はルール 00300 によって指定されています。
    # 外界から natd に返ってくるパケットも同様に、ルール 00301 によって natd に
    # 送られます。この例で興味深いのは、外に見せているホストへの内部からの
    # リクエストは、natd (ルール 00290) を通す必要がないということです。
    # これは、外部に見せているホストも内部の 10. ネットワークのことがわかるので
    # 可能であり、natd の負荷を軽減することができます。内部のトラフィックも
    # natd を通す必要がありません。これらのホストは、内部の 10. ネットワークの
    # ルーティングのことがわかるためです。
    # /etc/rc.local からは、natd は以下のように起動されます。
    # natd のカーネル組み込み型のバージョンである ipnat についても参照してく
    # ださい。
    #
    #	natd -s -u -a 208.161.114.67
    #
    #
    add 00290 skipto 1000 ip from 10.0.0.0/8 to 192.100.5.0/24
    add 00300 divert 8668 ip from 10.0.0.0/8 to not 10.0.0.0/8
    add 00301 divert 8668 ip from not 10.0.0.0/8 to 192.100.5.5
    # 高い帯域のアクセスがルールセット全体を通過していくのを防ぐためのショート
    # カットルールを設定します。すでに確立されている TCP コネクションはそのまま
    # 通し、また外へ出るパケットも同様にします。ファイアウォールを通す
    # のは入力パケットだけにします。
    #
    # 確立された TCP コネクションをそのまま通してしまうことは小さなセキュリティ
    # ホールになりますが、ファイアウォールの過剰な負荷を避ける意味で必要
    # になることもあります。もし心配ならばこのルールを、アドレス偽装チェック
    # のうしろに移動することもできます。
    #
    add 01000 allow tcp from any to any established
    add 01001 allow all from any to any out via fxp0
    add 01001 allow all from any to any out via fxp1
    add 01001 allow all from any to any out via fxp2
    # アドレス偽装防止のルールです。これは、内部ネットワークのパケットを
    # どれくらい信頼するかによって変わってきます。fxp1 を経由するパケットは必ず、
    # 10.0.1.x からのものでなければなりません。fxp2 を経由するもの
    # は 10.0.2.x からです。fxp0 を経由するものが LAN1 や LAN2 ブロックから
    # のものであることもあり得ません。ここでは 10.0.0.x を保護することはでき
    # ないので、ルータを適切に設定する必要があります。
    #
    add 01500 deny all from not 10.0.1.0/24 in via fxp1
    add 01500 deny all from not 10.0.2.0/24 in via fxp2
    add 01501 deny all from 10.0.1.0/24 in via fxp0
    add 01501 deny all from 10.0.2.0/24 in via fxp0
    # この例のルールセットでは、内部ホスト間には何の制約も設けていません。
    # 外部から見える LAN 上のホストであっても、内部 IP アドレスを使用する
    # 限りにおいてはそうです。これはセキュリティホールになる可能性があります
    # (外部から見えるホストに何かがあったらどうなるでしょうか ?)。これら
    # 3 つの LAN の間の通信を完全に制限したいのであれば、以下のふたつのルール
    # を削除してください。
    #
    # LAN1 と LAN2 を孤立させて、しかし外部から見えるホスト間の自由なアクセスを
    # 許したければ、ルール 01010 だけを削除して、01011 は残して下さい。
    #
    # (コメントアウトしてありますが、より制約の少ないファイアウォールにする
    # 場合はこれらを有効にしてください)
    #add 01010 allow all from 10.0.0.0/8 to 10.0.0.0/8
    #add 01011 allow all from 192.100.5.0/24 to 192.100.5.0/24
    #
    # 特定の LAN からは特定のサービスへのアクセスを許可する場合
    #
    # より制約の強いファイアウォールを使う場合には、特定の LAN からファイア
    # ウォール上で動作している特定のサービスにアクセスできるようにすることに
    # なります。この例では、LAN1 がファイアウォール上で動いているファイル共有
    # を必要とすると仮定します。もし、ルール 01010 が有効になっているような、
    # 制約の緩いファイアウォールであればこれらのルールは不要です。
    #
    add 01012 allow tcp from 10.0.1.0/8 to 10.0.1.1 139
    add 01012 allow udp from 10.0.1.0/8 to 10.0.1.1 137,138
    # 内部と外部の LAN の横断を許可する一般的なサービス
    #
    # DNS 参照、ntalk, ntp といった特定の UDP サービスは通過させます。
    # 内部サービスはアドレス偽装不可の内部アドレス (10. ネット) を持つことにより
    # 保護されているので、これらのルールは外部から見える IP アドレスにバインド
    # されているサービスに対してのみ意味を持ちます。また、UDP フラグメントは
    # 許可する必要があります。そうしないと、フラグメントされるような大きな
    # UDP パケットはファイアウォールを通過できません。
    #
    # DNS 参照に対する応答など、大きなポート番号を用いた一時的なサービスを
    # 行う必要があるかもしれません。この例ではそのようなポート番号を
    # 4000-65535 としています。外部から見える全マシンが一時ポートをこの
    # 外部から見えるポートにバインドするように、/etc/rc.conf の変数で設定
    # しています (上の、rc.conf の例を参照してください)。
    #
    add 02000 allow udp from any to any 4000-65535,domain,ntalk,ntp
    add 02500 allow udp from any to any frag
    # 同様のサービスを TCP についても許可します。ここでも、外部から見える
    # アドレスにバインドするサービスにのみ適用されます。また、この例では
    #  'auth' を通過させていますが、実際には外部から見えるポートでは identd
    # を動作させていません。これによって、auth 要求を受け取ったマシンは
    # TCP RESET を発行します。パケットを捨ててしまうと、ident 参照を行ってくる
    # サービスへの接続の遅延の原因となります。
    #
    # TCP フラグメントを許可していないことに注意して下さい。UDP 以外では、
    # 一般にフラグメントを許さないのです。TCP の、MTU ディスカバリプロトコル
    # が正しく動作して、TCP フラグメントが存在しないものと期待しています。
    #
    add 03000 allow tcp from any to any http,https
    add 03000 allow tcp from any to any 4000-65535,ssh,smtp,domain,ntalk
    add 03000 allow tcp from any to any auth,pop3,ftp,ftp-data
    # いくつかのタイプの ICMP を通過させることは重要です。
    # 一般形な ICMP タイプをここに列挙します。
    # ICMP タイプ 3 を通過させることが重要であることに注意してください。
    #
    #	0	エコーリプライ
    #	3	到達不能
    #	4	始点抑制 (通常は許可されません)
    #	5	リダイレクト (通常は許可されません。危険です !)
    #	8	エコー
    #	11	時間超過
    #	12	パラメータ問題
    #	13	タイムスタンプ
    #	14	タイムスタンプリプライ
    #
    # 状況によってはタイプ 5 の ICMP リダイレクトパケットを許可しなければな
    # らない場合がありますが、そのような場合にはインターネットルータでそれが
    # 禁止されていることを確認して下さい。
    #
    add 04000 allow icmp from any to any icmptypes 0,3,8,11,12,13,14
    # ここまで通って残ったフラグメントのログをとります。役にたつかもしれ
    # ませんが、邪魔なだけかもしれません。最後の deny ルールは、カーネルの設定
    # がどうであっても、ファイアウォールが包括的なものであることを保証する
    # ものです。
    #
    add 05000 deny log ip from any to any frag
    add 06000 deny all from any to any

内部向け、外部向けサービスのポートバインディング
マルチホームなホストで、サービスをどちらのアドレスにバインドするかという ことについて触れましたが、説明はしていません。複数の IP アドレスを持つホ ストでは、それぞれのサービスをすべての IP アドレスにバインドするのではな く、特定の IP アドレスやインタフェースにバインドすることが可能です。た とえばこの例のファイアウォールマシンには、インタフェースが 3 つあり、 その 1 つには 2 つの外部から見える IP アドレスがあるので、このマシンには 5 つの IP アドレス (10.0.0.1, 10.0.1.1, 10.0.2.1, 192.100.5.5, 192.100.5.1) があることになります。Windows の LAN セグメント (LAN1 とします) に対してファイル共有サービスを提供するのであれば、samba の 'bind interfaces' という設定項目で、LAN1 の IP アドレスにだけ samba をバインドできます。 こうすることで、他の LAN セグメントではこのファイル共有サービスを利用 できなくなります。また、LAN2 に UNIX エンジニアリングワークステーション があれば、nfsd を 10.0.2.1 にバインドするように設定することで NFS でも同様 のことができます。どのサービスをどのようにバインドするかはほとんどの場合 に指定できますし、またそれが指定できない場合には jail(8) を使うことによって、間接的にそれを行うこともできます。

関連項目
ipnat(1), dummynet(4), ipnat(5), rc.conf(5), smb.conf(5) (ports/net/samba), samba(7) (ports/net/samba), config(8), ipfw(8), jail(8), natd(8), nfsd(8)

関連文書
ipf(5), ipf(8), ipfstat(8)

歴史
firewall マニュアルページは最初、 Matthew Dillon によって書かれ、2001 年 5 月に FreeBSD 4.3 ではじめて登場しました。