[OpenBSD]

[前に戻る: authpf: 認証ゲートウェイ用 ユーザシェル] [目次]

PF: 例 #1: 自宅や小規模事務所用ファイアウォール


目次


シナリオ

この例では、PF は自宅か事務所の小規模ネットワーク用のファイアウォール兼 NAT ゲートウェイとして稼働中の OpenBSD マシン上で実行されています。 全体としての目的は、インターネットへのアクセスおよびインターネットから ファイアウォールへの限定されたアクセスを提供することです。このドキュメントでは、 これを可能にする完全なルールセットについて検討を加えます。

ネットワーク

ネットワークは以下のような構成となっています。
    
  [ COMP1 ]    [ COMP3 ]
      |            |                               ADSL
   ---+------+-----+------- fxp0 [ OpenBSD ] ep0 -------- ( インターネット )
             |
         [ COMP2 ]

内部ネットワークには多数のコンピュータが接続されています。上図では 3 台になっていますが、これは実際の台数とは無関係です。これらのコンピュータは、 web サーフィン、電子メール、チャットなどの用途で使用されるワークステーションです。 内部ネットワークは 192.168.0.0 を使用しており、ネットワークブロックは 255.255.255.0 となっています。

この OpenBSD ルータは 2 枚のネットワークカード 3Com 3c509B (ep0) ならびに Intel EtherExpress Pro/100 (fxp0) を持つ Pentium 100 のマシンです。この ルータはインターネットへの ADSL 接続を持っており、この接続を内部ネットワークで 共有するために NAT を使用しています。外部インターフェイスの IP アドレスは、 インターネットサービスプロバイダ (ISP) から動的に割り当てられています。

目標

目標は以下のとおりです。

準備

このドキュメントでは、OpenBSD のホストは、net.inet.ip.forwarding が "1" に設定されていて、また、IP ネットワークの設定やインターネットへの接続性の検査を含めて ルータとして動作できるよう、適切に設定されているものと仮定しています。

ルールセット

以下は、上記の目的を達成することのできるルールセットの 各部分の解説です。

マクロ

以下のマクロは、メンテナンスとルールセットの解読がより簡単になるよう、 定義されているものです。
int_if = "fxp0"
ext_if = "ep0"

tcp_services = "{ 22, 113 }"
icmp_types = "echoreq"

priv_nets = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }"

最初の 2 行は、実際にフィルタリングが行われるネットワークインターフェイスを 定義しています。3 行目と 4 行目とは、インターネットに対して開かれた TCP ポート番号 (SSH と ident/auth) および、ファイアウォールに届くことが許される ICMP のパケットタイプを定義しています。 最後の行はループバックと RFC 1918 のプライベートアドレスのブロックを定義しています。

: もし、ADSL によるインターネット接続が PPPoE を必要とする場合、フィルタリングや NAT は ep0 に対してでは なくtun0 インターフェイスに対して定義する必要があります。

オプション

以下のふたつのオプションは、block フィルタルールに対する デフォルトの反応を設定し、そして、外部インターフェイスに対する 統計情報のログを取得するように設定しています。
set block-policy return
set loginterface $ext_if

スクラブ (scrub)

すべての着信トラフィックに対する、推奨されるパケット再構成を行わない理由はなく、 以下は単純な一行の定義です。
scrub in all

ネットワークアドレス変換 (NAT)

内部ネットワーク全体に対する NAT を実行するためには、 以下のような nat ルールを使用します。
nat on $ext_if from $int_if:network to any -> ($ext_if)

外部インターフェイスの IP アドレスは動的に割り当てられますので、 アドレスが変化した場合に PF がこれに注意を払うことができるよう、 変換するインターフェイスに対して括弧を付与しておきます。

リダイレクション

ローカルネットワーク上の FTP クライアントがインターネット上の FTP サーバに接続できるようにするために必要とされるただひとつのリダイレクションは、 ftp-proxy(8) です。
rdr on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021

このルールは、ポート 21 番への FTP 接続だけを捕えることができます。もし、 ユーザが FTP サーバの他のポートに対して、いつも接続しているのでしたら、 たとえば from any to any port { 21, 2121 } のように、 リストは送信先ポートを指定するために使用されるべきです。

フィルタルール

ここからはフィルタルールについてです。このデフォルトは拒否で始まります。
block all

この時点では、内部ネットワークからのものも含めて、ファイアウォールを 通過するパケットは何もありません。以下のルールによって、上記の目的ごとに ファイアウォールを開いていき、同様に、すべての必要な仮想インターフェイス も開いていきます。

どの Unix システムも、「ループバック」インターフェイスを持ちます。これは、システム内の アプリケーションが互いに他と通信するのに使用される仮想的なネットワークインターフェイスです。 一般的に、すべてのトラフィックは、このループバックインターフェイスにわたされるべきです。 OpenBSD では、ループバックインターフェイスは lo(4) です。

pass quick on lo0 all

次に、 RFC 1918 のプライベートアドレスは、公のインターネットに出現してはいけないので、 外部インターフェイスに入って来たり、出て行ったりするのをブロックします。 これらをフィルタリングすることは、ルータがこれらのアドレスが内部ネットワークから 「漏れて」出て行かないことと、同じくこれらのネットワークのひとつを 送信元アドレスとする着信パケットをブロックすることを確実にします。

block drop in  quick on $ext_if from $priv_nets to any
block drop out quick on $ext_if from any to $priv_nets

block drop は、TCP RST や ICMP Unreachable パケットに応答する必要がないことを PF に指示するために使用されるということに注意してください。RFC 1918 のプライベートアドレスは インターネットに存在しないので、これらのアドレスに送信先されるパケットはすべて、何があろうと 何もしてはいけません。また、quick オプションは、そこから上のルールにパケットが マッチした場合に、PF に残りのフィルタルールを評価する手間をかける必要がないことを指示するために 使用されます。$priv_nets ネットワークへの、あるいは、そこからのパケットは、 直ちに廃棄されます。

ここで、インターネットに対して公開しているネットワークサービスが使用する ポートを開きます。

pass in on $ext_if inet proto tcp from any to ($ext_if) \
   port $tcp_services flags S/SA keep state

$tcp_services マクロの中にネットワークポートを指定することにより、 単にマクロを編集し、ルールセットを再ロードすることで、インターネットに対して 付加的なサービスを開くことが簡単にできるようになります。同様に、UDP のサービスも $udp_services マクロを生成し、上記のように、proto udp を指定したフィルタルールを追加しておくことで、開くことができるようになります。

この時点で、ICMP のトラフィックは以下のように通過させておくべきでしょう。

pass in inet proto icmp all icmp-type $icmp_types keep state

$tcp_services マクロと同じように、$icmp_types マクロも、 ファイアウォールに到達することができる ICMP パケットのタイプの変更するため、 簡単に編集することができます。このルールはすべてのネットワークインターフェイスに 適用されるということに注意してください。

この時点で、内部ネットワークからのトラフィックは通過できるようにすべきです。ここで、 私たちは、内部ネットワークのユーザは、自分たちが何をしようとしているのかを知っていて、 トラブルを引き起こすつもりなどはないものと仮定しましょう。 これは必ずしも正当な仮定ではなく、いくつかの環境では、より限定的なルールセットが 適切であるかも知れません。

pass in on $int_if from $int_if:network to any keep state

上記のルールは、すべての内部のマシンに、ファイアウォールを通過するパケットの送信を 許可していますが、ファイアウォールから内部のマシンに対して接続しようとすることは 許可していません。しかし、本当にこれで良いのでしょうか ?  もちろん、その答は ネットワーク構成のより詳細な部分に依存することです。たとえば、ファイアウォールが DHCP サーバを兼ねている場合には、IP の割り当て前にクライアントが生きているかどうかを確認するのに "ping" を行う必要があるかも知れません。ファイアウォールが内部ネットワークに接続するのを 許可することはまた、インターネットからファイアウォールに SSH で接続してきた誰かに 内部ネットワークへのアクセスを許可することでもあります。 ファイアウォールが直接内部ネットワークに通信を行うことを許可しないことが セキュリティ上の大きな利点になるわけではないということに留意しておいてください。 もし、誰かがファイアウォールへのアクセスを手に入れたとすると、どのみちフィルタルールも たいていは変更されてしまうかも知れないからです。以下のフィルタルールを追加することによって、 ファイアウォールから内部ネットワークに接続しようとすることができるようになります。

pass out on $int_if from any to $int_if:network keep state

もし、これらの両方の行が記述されている場合には、keep state オプションは 不要であることに注意してください。つまり、双方向にパケットを通過させるための ルールが記述されているのですから、すべてのパケットは内部インターフェイスを 通過することができるわけです。しかし、pass out の行が含まれていない場合には、 pass in の行には keep state オプションが含まれている必要があります。 また、この状態の保持 (keep state) には、いくつかの性能上の利点もあります。 たとえば、状態テーブルはルールの評価前にチェックされ、 状態のマッチがあった場合には、パケットはルールセットの評価を受けることなく ファイアウォールを通過します。これによって、負荷の重いファイアウォールに対して 性能上の利点を提供することができますが、この単純なことによって、 システム内部で非常に大きな負荷を発生してしまうようなことは考えにくいでしょう。

最終的に、外部インターフェイスから出て行くトラフィックを通過させます。

pass out on $ext_if proto tcp all modulate state flags S/SA
pass out on $ext_if proto { udp, icmp } all keep state

これで TCP、UDP および ICMP のトラフィックはファイアウォールからインターネットに向かって 出て行くことができます。また、応答パケットがファイアウォールを通過して内部ネットワークに 入れるよう、状態情報が保持されます。

完全なルールセット

# macros
int_if = "fxp0"
ext_if = "ep0"

tcp_services = "{ 22, 113 }"
icmp_types = "echoreq"

priv_nets = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }"
	  
# options
set block-policy return
set loginterface $ext_if

# scrub
scrub in all

# nat/rdr
nat on $ext_if from $int_if:network to any -> ($ext_if)
rdr on $int_if proto tcp from any to any port 21 -> 127.0.0.1 \
   port 8021

# filter rules
block all

pass quick on lo0 all

block drop in  quick on $ext_if from $priv_nets to any
block drop out quick on $ext_if from any to $priv_nets

pass in on $ext_if inet proto tcp from any to ($ext_if) \
   port $tcp_services flags S/SA keep state

pass in inet proto icmp all icmp-type $icmp_types keep state

pass in  on $int_if from $int_if:network to any keep state
pass out on $int_if from any to $int_if:network keep state

pass out on $ext_if proto tcp all modulate state flags S/SA
pass out on $ext_if proto { udp, icmp } all keep state

[前に戻る: authpf: 認証ゲートウェイ用 ユーザシェル] [目次]


[back] www@openbsd.org
Originally [OpenBSD: example1.html,v 1.12 ]
$Translation: example1.html,v 1.13 2004/01/03 05:12:49 toshi Exp $
$OpenBSD: example1.html,v 1.12 2004/01/04 22:47:55 horacio Exp $