PF: OpenBSDパケットフィルタを設定する

PF: OpenBSDパケットフィルタは IP通信に使用されるパケットを、 指定された条件に従ってフィルタリングするもので、 ファイアーウォールを構成する際などに利用するものです。 ここでは、その設定例を紹介します。

このメモは OpenBSD 3.3版の機能に基づいています。 PF: OpenBSDパケットフィルタは 3.0版で新規に導入されたものですが、 OSがバージョンアップする度にPFも機能強化されてきたました。 そこで、3.3版になったのを契機に全面的に更新しました。 それ以前の版にも大筋では適用できるはずですが、 未サポートの機能が含まれています。

参考: PF: The OpenBSD Packet Filter日本語訳

フィルタリングの想定仕様

  1. 2つのネットワーク・インターフェース、fxp0 と fxp1 を持っていて、 fxp0 が内部の LAN に、fxp1 が外部につながっているものとします。 アドレスは、fxp0 = 192.168.0.1、fxp1 = 192.168.1.1 とします。
  2. 外側からのアクセスは、tcp/80 を除いて全て拒否します。 tcp/80 は公開 HTTP サーバへのアクセスを想定して、 192.168.1.1 宛の通信のみを許可します。
  3. 内側から外側へのアクセスは全て許可します。
    注意: この仕様は非常に大雑把なものです。 実際にファイアーウォールとして運用するのであれば、 本当に必要な通信のみを通すように、詳細な設定をするべきです。
  4. 内側から外側へのFTP(passiveモードでない通常モード)によるアクセスを可能にするため、 ftp-proxy を使う設定も同時に行います。
  5. PF は NAT の機能も提供していますが、ここでは使わないものとします。
  6. PF は IPv6 対応ですが、とりあえず、IPv4 のみで考えます。

設定の手順

  1. /etc/pf.conf にフィルタリングの設定を定義します。 この詳細は次項で説明します。
  2. IPフォワーディング機能が有効になっている必要があるため、 /etc/sysctl.conf の中の net.inet.ip.forwarding=1 の行を有効にします。
  3. IPが起動するように、 /etc/rc.conf の中の pf=NO を pf=YES に変更します。
  4. マシンをリブートします。
  5. 外側からのアクセス等を試みて、フィルタリングが正常に動作することを確認します。 また、ログファイル /var/log/pflog に適切なログ情報が出ているか確認します。 このファイルは、バイナリー形式なので、以下のようにして読む必要があります。
    # tcpdump -n -e -ttt -r /var/log/pflog
    
  6. 設定に問題があった場合、/etc/pf.conf を修正し、シンタックスのチェックを行います。
    # pfctl -n -f /etc/pf.conf
    
    エラーが無ければ、設定を再読み込みします。
    # pfctl -f /etc/pf.conf
    
    【注意】 OpenBSD 3.1 以前の場合、pfctl の仕様が異なります。 上記のオプション指定では期待通りに動作しません。

設定の定義

ここでは、/etc/pf.conf の記述例を紹介します。 以下のような定義を順に記述します。 なお、/etc/pf.conf の詳しい仕様は、 man pf.conf および PF: OpenBSDパケットフィルタ を参照してください。

  1. マクロを定義する。 ネットワークインタフェース名やルータのIPアドレス、ローカルネットワークのアドレス等は、 後でマシン構成が変わった時に、細かいルール定義の中を触らなくても済むように、 マクロとして定義しておきます。
    IFint = "fxp0"
    IFext = "fxp1"
    IFAint = "192.168.0.1"
    IFAext = "192.168.1.1"
    MyNet = "192.168.0.0/24"
    PubSvc = "{ www }"
    FTPdata = "60000 >< 61000"
    
    また、ルール定義の記述を簡素化するためのマクロも定義しておきます。
    PIint = "pass in quick on" $IFint "proto"
    PIext = "pass in quick on" $IFext "proto"
    POint = "pass out quick on" $IFint "proto"
    POext = "pass out quick on" $IFext "proto"
    
  2. テーブルを定義する。 テーブルはアドレスの集合を効率良く管理するもので、 OpenBSD 3.3版で追加された機能です。 この例ではルーティング対象外のアドレスの管理に使用します。
    table <NoRouteIPs> { 0.0.0.0/8, 10.0.0.0/8, 127.0.0.0/8, \
        169.254.0.0/16, 172.16.0.0/12, 192.0.2.0/24, 192.168.0.0/16, \
        204.152.64.0/23, 224.0.0.0/3 }
    # 0.0.0.0/8       : illegal
    # 10.0.0.0/8      : private
    # 127.0.0.0/8     : localhost
    # 169.254.0.0/16  : auto-configuration
    # 172.16.0.0/12   : private
    # 192.0.2.0/24    : example IP netblock
    # 192.168.0.0/16  : private
    # 204.152.64.0/23 : cluster interconnects
    # 224.0.0.0/3     : class D networks
    
  3. オプションを指定する。 通常はオプション指定なしのデフォルトの動作で問題ないと思いますが、例として、 遅延の大きいネットワークに最適化するオプションを指定してみました。
    set optimization high-latency
    
  4. Scrabの指定をする。 Scrabはフラグメント化されたパケットの再構成を行い、 正しくないフラグの組み合せを持つTCPパケットを破棄します。 「強く推奨される」 とのことなので、指定します。
    scrab in all
    
  5. FTP-proxy のためのリダイレクトを定義します。
    rdr on $IFint proto tcp from any to any port ftp -> 127.0.0.1 port 8021
    
    なお、FTP-proxy を使用するには、 /etc/inetd.conf に以下の設定(実際は1行で記述する)が必要です。 使用するポート番号はPFの設定に合わせます。
    127.0.0.1:8021  stream  tcp     nowait  root
            /usr/libexec/ftp-proxy  ftp-proxy -n -m 60001 -M 60999
    
  6. ループバック・インターフェースとの通信を全て許可します。
    pass in quick on lo0 all
    pass out quick on lo0 all
    
  7. 外部から公開サービス(現在は HTTP サーバのみ)へのアクセスを許可します。
    $PIext tcp from !<NoRouteIPs> to $IFAext port $PubSvc flags S/SA keep state
    
  8. 内部から外部へのアクセスを全て許可します。
    $PIint tcp from $MyNet to !<NoRouteIPs> flags S/SA keep state
    $POext tcp from $MyNet to !<NoRouteIPs> modulate state
    $PIint udp from $MyNet to !<NoRouteIPs> keep state
    $POext udp from $MyNet to !<NoRouteIPs> keep state
    $PIint icmp from $MyNet to !<NoRouteIPs> keep state
    $POext icmp from $MyNet to !<NoRouteIPs> keep state
    
    2行目の modulate state はTCPの初期シーケンス番号を推測し難いものに付け替える設定で、 MyNet内にある推測し易い初期シーケンス番号を付与するマシンを保護します。
  9. FTP-proxy に関するアクセスを許可します。
    $PIint tcp from $MyNet to 127.0.0.1 port 8021 flags S/SA keep state
    $PIext tcp from !<NoRouteIPs> to $IFAext port $FTPdata flags S/SA keep state
    $POint tcp from $IFAint port $FTPdata to $MyNet port >= 1024 keep state
    
  10. その他の通信は全て拒否します。
    block in quick log on $IFext from <NoRouteIPs>
    block return log