「Packet Filter」によるパケットフィルタリング

「Packet Filter」を利用してパケットフィルタリングを行う。
ここでは通信のみの制御で、ルータとしての機能は行わない事とする。
当然の事ながら、ここでの設定はあくまで一例なので、この通りに行えば良い、という訳ではなく各自の環境に合わせて設定する。
なお、「Swatch」を利用してログを監視し、アクセス制限を行う場合はこちらを参照。

カーネルの再構築

FreeBSDのバージョンが古い場合や「Packet Filter」をカーネルに組み込む場合、カーネルを再構築する。
FreeBSD5.3以降であればloadable moduleとして組み込まれているので、無理にこの作業を行う必要は無い。
なお、ここでは最低限必要なもののみ設定している。

「Packet Filter」用の設定

ALTQを有効にする場合の記述もあるが、当サイトではALTQは利用しない。
ALTQについてはこちらを参照。

FreeBSD# cd /usr/src/sys/i386/conf
FreeBSD# cp GENERIC MYKERNEL <= カーネル設定ファイルのコピー
FreeBSD# vi MYKERNEL <= 設定ファイルの編集
使用しているCPU以外の部分はコメントアウトする
cpu             I486_CPU
cpu             I586_CPU
cpu             I686_CPU
↓
#cpu             I486_CPU <= コメントアウト
#cpu             I586_CPU <= コメントアウト
cpu             I686_CPU

ident           GENERIC
↓
ident       MYKERNEL <= 新しいカーネルの名前(変更したファイル名)

以下を追加(Packet Filter用)
device pf
device pflog
device pfsync

ALTQを利用する場合、以下も追加
options         ALTQ
options         ALTQ_CBQ        # Class Bases Queuing (CBQ)
options         ALTQ_RED        # Random Early Detection (RED)
options         ALTQ_RIO        # RED In/Out
options         ALTQ_HFSC       # Hierarchical Packet Scheduler (HFSC)
options         ALTQ_PRIQ       # Priority Queuing (PRIQ)
options         ALTQ_NOPCC      # Required for SMP build

カーネルの再構築

上記の設定後、以下のようにしてカーネルを再構築し、インストールする。

FreeBSD# config MYKERNEL
FreeBSD# cd ../../compile/MYKERNEL
FreeBSD# make depend
FreeBSD# make
FreeBSD# make install

新しいカーネルで起動しない場合

カーネル再構築後、リブートして新しいカーネルで起動するが、起動に失敗する場合、以下のように対処する。
まず、Boot時の起動レベルの選択中にEnterキー以外のキーを押下し

unload
boot kernel.old

と入力すれば古いカーネルで起動する。
その後、設定ファイルを修正し、再度カーネルの構築を行う。

「Packet Filter」の有効化

設定ファイルを編集し、必要な設定を記述する。

FreeBSD# vi /etc/rc.conf <= 設定ファイルの編集
以下を追加
pf_enable="YES"                 # Enable PF (load module if required)
pf_rules="/etc/pf.conf"         # rules definition file for pf
pf_flags=""                     # additional flags for pfctl startup
pflog_enable="YES"              # start pflogd(8)
pflog_logfile="/var/log/pflog"  # where pflogd should store the logfile
pflog_flags=""                  # additional flags for pflogd startup
「Packet Filter」の設定

セクション

「Packet Filter」の以下の7つのセクションから構成されている。

セクション 説明
Macros IPアドレスやインタフェース名等、ユーザ定義の変数。
Tables IPアドレスのリストの構造体。
Options オプション。
Normalization パケットの通常化およびデフラグメントするための再処理行程。
Queueing 帯域制御とパケットの優先付け。
Translation NATとパケットリダイレクションの制御。
Filtering フィルタリングルール。

Macros

IPアドレスやインタフェース名等のユーザ定義の変数を設定する。
例えばインタフェース名とLAN内のマシンを指定する場合。

ext_if = "fxp0"
lan_machine = "{ 192.168.0.2, 192.168.0.3 }"

Tables

IPアドレスのグループを保持する。
上記のMacrosのlan_machineで設定しているようなリストに比べて非常に高速に実行される。
よって、大規模なアドレスのグループを保持するのに利用する。
Tablesの書式は以下の通り。

table <テーブル名> 属性 { IPアドレス }

属性には以下を指定出来る。

属性 説明
const テーブルが生成された後にテーブルの内容を変更することは出来ない。
persist テーブルが生成された後も動的にテーブルの内容を変更出来る。

IPアドレスの前に ! あるいは not を指定すると否定になり、例えば以下のように設定する。

table <private> const { 192.168.0.0/24, \
                        192.168.1.0/24, !192.168.1.1 }
table <blacklist> persist

上記の persist を指定した場合、後からテーブルにルールの追加・削除を行う場合、以下のように pfctlコマンドを使う。

FreeBSD# pfctl -t blacklist -Tadd 192.168.2.0/24 <= 追加

FreeBSD# pfctl -t blacklist -Tdelete 192.168.2.0/24 <= 削除

FreeBSD# pfctl -t blacklist -Tshow <= 参照

Options

Optionsで指定出来る主なものは以下の通り。

Options 説明
set block-policy blockの動作でのデフォルトの挙動を設定。
・drop:送信元に通知しない。
・return:TCPパケットには TCP RST パケットを返し、他のパケットには ICMP UNREACHABLE パケットを返す。
set debug デバッグレベル
・none:無効
・urgent:重大なエラーのみ(デフォルト)
・misc:一般的なエラーを含む
・loud:全てのメッセージ
set limit セッションを保持する為のタイムアウトや保持数を設定する。
・frags:scrubルールを行う為に使用されるメモリプール中のエントリの最大の数。
デフォルトは5000
・states:keep state が指定されたルールに使用されるメモリプール中のエントリの最大の数。
デフォルトは10000
set loginterface 統計情報を収集するインタフェースを指定。
set optimization 以下のいずれかのネットワーク環境のに応じて最適化する。
・normal:ほとんどのネットワークに適合する(デフォルト)。
・high-latency:遅延の大きなネットワーク。
・aggressive:強制的に状態テーブルから接続を除去する。
ビジー状態のファイアウォールのメモリ要求量を減少させる必要がある場合。
ただし、早期にアイドル状態の接続を切断してしまう可能性がある。
・conservative:アイドル状態の接続を切断しないようにする。
ただし、より多くのメモリ消費量と若干のCPU使用量が増加する。
set timeout タイムアウトの設定。
・interval:失効した状態の排除からパケットのフラグメント化までの間の秒数。
・frag:再構成されていないフラグメントが失効するまでの秒数。
set skip on 処理を行わないインタフェースを指定。
ループバックインタフェース等で利用。

Normalization

パケットの通常化や再構成を行い、正しくないフラグの組み合わせを持つTCPパケットを廃棄する。
すべてのインタフェース上の受信パケットに対してスクラブを行う場合、以下のように指定する。

scrub in all

指定出来るオプションは以下の通り。

オプション 説明
no-df フラグメント禁止ビットをパケットのヘッダから除去する。
random-id 送信パケットの識別フィールドの値をランダムな値で書き換える。
min-ttl TTLの最小値に強制的に適用する。
max-mss MSS最大値に強制的に適用する。
fragment reassemble フラグメント化された受信パケットをバッファリングし、完全なパケットに再構成してからフィルタエンジンに渡す。
fragment crop 重複部分を廃棄する。
fragment drop-ovl fragment crop に加え、その後に着信する関連フラグメントも含めて廃棄される。
reassemble tcp IPのTTLを減少させることは禁止し、TCPのパケットヘッダのRFC1323のタイムスタンプを乱数を使用して変調する。

Queueing

キューイングの設定を行う。
ALTQを組み込まないと動作しない。
当サイトでは利用しない為、ここでは割愛。

Translation

NATの設定を行う。
当サイトではNATの設定は行わない為、ここでは割愛。

Filtering

「Packet Filter」のメインの設定であるフィルタリングルール。
次から説明。

フィルタリングルール

フィルタリングルールの基本的な構文は以下の通り。

action [in|out] option keyword

ここでは、上記のそれぞれについて主要なものだけ記述する。

action

actionでは許可するものは pass、破棄するものは block を指定する。

[in|out]

[in|out]は入力の場合、in を、出力の場合は out を指定する。

option

optionで指定出来る主なものは以下の通り。
なお、複数のoptionを指定する場合、上に記述しているものから指定する。

option 説明
log ログに残す
quick 該当する場合、以降のルールをチェックせず適用
on interface インタフェース

keyword

keywordで指定出来る主なものは以下の通り。
なお、複数のkeywordを指定する場合、上に記述しているものから指定する。

keyword 説明
inet/inet6 パケットのアドレスファミリ。
ICMPではIPv4とIPv6の実装が異なるのでtypeを指定した場合、必須。
inet:IPv4
inet6:IPv6
proto プロトコル プロトコル(tcp,udp,icmp等)
from,to/all/ IPアドレス from:発信元IPアドレス
to:宛先IPアドレス
IPアドレスにanyを指定すると全てに一致する。
なお、allを指定するとfrom any to anyになる。
port ポート番号 ポート番号。
この設定はprot tcpにした場合のみ指定出来る。
flags flags TCPのフラグを指定。
以下の左のように指定する。右はフラグの内容。
F:FIN(終了:送信終了)
S:SYN(同期:接続要求)
R:RST(リセット:強制切断要求)
P:PSH(プッシュ:バッファリングせず、すぐアプリケーションに届けるよう要求)
A:ACK(肯定:パケットを受け付けた)
U:URG(緊急:緊急で扱うべきデータであることを示す)
E:ECE(明示的輻輳通知のエコー)
W:CWR(輻輳ウィンドウの減少通知)

フラグは、flags フラグ/フラグマスク と記述する。
フラグマスクは常に指定する。
例:flags S/SA
拒否:SYN+ACK
許可:SYN+RST、SYN+FIN、SYN+URG、SYN+PSH
state 保存しておくパケットの情報を決定する。
keep state:TCP、UDP、ICMPパケットで保存する。
modulate state:TCPに対してのみ保存する。
パケットがルールにマッチする為に強力な初期シーケンス番号(ISN)を生成する。
synproxy state:詐称された TCP SYN flood からサーバを防護するため、着信するTCP接続のプロキシを行う。
このオプションは、keep state および modulate state の機能を含んでいる。

「Packet Filter」の設定

デフォルトの設定ファイルは全てコメントアウトされており、有効な設定が一つもない。
その為、必要な設定の部分をコメント解除するか追記する。
ここでは基本的な部分のみ記述している。

FreeBSD# vi /etc/pf.conf <= 設定ファイルの編集
###################################
# Macros                          #
###################################
lo_if = "lo0"
ext_if = "fxp0"
lo_addr = "127.0.0.0/8"
lo_net = "192.168.0.0/24"

###################################
# Tables                          #
###################################
table <fakeaddr> const { 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 169.254.0.0/16 }
table <blacklist> persist

###################################
# Options                         #
###################################
set block-policy drop
set loginterface fxp0

###################################
# Normalization                   #
###################################
scrub in all
scrub out all random-id max-mss 1414

###################################
# Queueing                        #
###################################


###################################
# Translation                     #
###################################


###################################
# Filtering                       #
###################################

### INPUT ###
# 基本破棄
block in all

# ループバックの許可
pass in quick on $lo_if from $lo_addr to $lo_addr
pass out quick on $lo_if from $lo_addr to $lo_addr

# 送信元のおかしなもの、拒否しているIPアドレスはログを取って破棄
block in log quick on $ext_if from { <fakeaddr> <blacklist> } to any

# ping(内部ネットワークのみ許可)
pass in quick on $ext_if inet proto icmp from $lo_net to any icmp-type 0
pass in quick on $ext_if inet proto icmp from $lo_net to any icmp-type 8

# SSH
pass in quick on $ext_if proto tcp from any to any port 22 flags S/SA modulate state

# DNS
pass in quick on $ext_if proto {tcp, udp} from $lo_net to any port 53 keep state

# IDENT
block return-rst in quick on $ext_if proto tcp from any to any port 113

### OUTPUT ###
# 基本許可
pass out all 

# Samba
pass out quick on $ext_if proto {tcp, udp} from any to $lo_net port 136 >< 140 keep state
block out quick on $ext_if proto {tcp, udp} from any to any port 136 >< 140

pass out quick on $ext_if proto {tcp, udp} from any to $lo_net port 445 keep state
block out quick on $ext_if proto {tcp, udp} from any to any port 445

# ping(内部ネットワークのみ許可)
pass out quick on $ext_if inet proto icmp from any to $lo_net icmp-type 0
pass out quick on $ext_if inet proto icmp from any to $lo_net icmp-type 8
block out quick on $ext_if proto icmp all

「Packet Filter」の起動

FreeBSD# /etc/rc.d/pf start
FreeBSD# /etc/rc.d/pflog start

ルールの確認

FreeBSD# pfctl -vv -s rules
-v:詳細な情報を表示

ルール更新後の反映

FreeBSD# pfctl -f /etc/pf.conf

ログの確認

ログファイルは /var/log/pflog であるが、テキストファイルではないので以下のようにして確認する。
なお、リアルタイムに監視する場合のfxp0はデバイス名

FreeBSD# tcpdump -n -e -ttt -r /var/log/pflog <= ログファイルを確認
FreeBSD# tcpdump -n -e -ttt -i fxp0 <= ログファイルをリアルタイムに監視

国別IPアドレスリスト破棄の設定

拒否したい国がある場合、以下のようなスクリプトを作成してアクセス拒否の設定をする。

FreeBSD# vi drop_country.sh <= 拒否スクリプトファイル作成
#!/bin/sh

PFCTL="/sbin/pfctl"
WGET="/usr/local/bin/wget"
RM="/bin/rm"

# 国別IPアドレスリスト取得
$WGET -q http://ftp.apnic.net/stats/apnic/delegated-apnic-latest # APNIC
$WGET -q ftp://ftp.arin.net/pub/stats/arin/delegated-arin-latest # ARIN

# 指定した国からのアクセスを破棄
DROP_COUNTRY(){
  for IP in `cat $1 | grep "$2|$3|ipv4|"`
  do
    ADDR=`echo $IP |cut -d "|" -f 4`
    TEMP=`echo $IP |cut -d "|" -f 5`
    CIDR=32
    while [ $TEMP -ne 1 ];
    do
      TEMP=`expr "$TEMP" / 2`
      CIDR=`expr "$CIDR" - 1`
    done
    $PFCTL -t blacklist -Tadd $ADDR/$CIDR
  done
}

# 中国
DROP_COUNTRY delegated-apnic-latest apnic CN
DROP_COUNTRY delegated-arin-latest arin CN
# 韓国
DROP_COUNTRY delegated-apnic-latest apnic KR
DROP_COUNTRY delegated-arin-latest arin KR
# 北朝鮮
DROP_COUNTRY delegated-apnic-latest apnic KP
DROP_COUNTRY delegated-arin-latest arin KP
# 台湾
DROP_COUNTRY delegated-apnic-latest apnic TW
DROP_COUNTRY delegated-arin-latest arin TW

# 国別IPアドレスリスト削除
$RM delegated-apnic-latest
$RM delegated-arin-latest


FreeBSD# chmod 700 drop_country.sh <= 作成したスクリプトに実行権限付加
▲ページのトップへ