PF(パケットフィルタ) on FreeBSD

FreeBSD上でPF設定のメモ。

bruteforceblickerと特定国よけは別エントリーにメモします。

グローバルなIPを振っている環境とかDMZにおいてある環境なら使用しないポートは塞いでしまいましょう。

おいらの環境では、bruteforceblockerを使いたいのでPFを使用してフィルタリングしています。

PFについてはOpenBSDプロジェクトのPF:Packet Filteringを一読されることをお勧めします。最近の日本語訳が見つからなかったのでウィーン工科大に置かれている日本語訳も有用と思いますので貼っておきます(でもたぶん古い)。

FreeBSDでPFを使用する場合は、/etc/pf.confファイルにフィルタリングルールを記述します。(インストール直後はファイルがないので新規作成します)

作業ミスで通信が一切出来なくなるということも考えられるので、作業する場合は試験環境で十分確認した上でコンソールが使える状況で適用してください。

ちなみに、ルータとして使用するわけではないので、そういった部分は考慮していません。ですから、pf.confに記述する内容はマクロ・テーブル・フィルタルールとなります。

まずマクロを定義します。マクロとは言語におけるマクロと同義です。マクロを定義しておくことで、オプションやフィルタルールを記述する場合に簡潔に記述することが出来ます。マクロの書き方は

マクロ名 = "内容"

となります。マクロ名は予約語以外なら何でも構いませんが、判りやすい名称にしておきます。

マクロで定義する内容はインターフェイスとサービスのリスト。

インターフェイスのマクロは

  • インターフェイス名
  • インターフェイスのアドレス

を定義しておきます。例えば、外向きのインターフェイスがem0でIPアドレスが192.168.1.1、ローカル用のインターフェイスが”em1″でIPアドレスが192.168.2.1の場合なら

# WAN Interface
ext_if = "em0"
ext_addr = "192.168.1.1"
# LAN Interface
int_if = "em1"
int_addr = "192.168.2.1"

のように記述します。(”#”をつけるとコメント行となります)定義したマクロはルール中で”$マクロ名”を記述すことで参照できます。

次にサービスリストのマクロ。

リストとは1つのルールに複数の判断基準を設定するときに使用するもので、リストは”{}”で括ります。例えばsshおよびhttpの通信を許可したい場合、

pass in quick on $ext_if proto tcp from any to any port ssh flags S/SA modulate state
pass in quick on $ext_if proto tcp from any to any port http flags S/SA modulate state

のように記述しますが、ルールが一緒なら

pass in quick on $ext_if proto tcp from any to any port {ssh,http} flags S/SA modulate state

のようにまとめて記述することが出来ます。

リストとして記述した内容はPFの起動時に展開されますから、PF的にはどちらでも構わないのですが、ルールが増えるとメンテナンスがやり辛くなりますので、なるべくリストを使用したほうが良いでしょう。

おいらの環境では、サービスリストのマクロをこのように定義しています。

tcp_services = "{ ssh, smtp, smtps, submission, http, https, pop3, domain, ntp, postgresql, snmp, snmptrap, sunrpc, pop3s, imaps, ftp }"
udp_services = "{ domain, ntp, snmp, sunrpc }"
netbios_ports = "{ 135, 137, 138, 139, 445, 1433 }"

用途としては

  • tcp_services(TCP関連)
  • udp_services(UDP関連)
  • netbios_ports(NetBIOS関連ポート)

となっています。

リスト中には/etc/servicesファイルに記述されているサービス名称かポート番号を記述します。ポート番号だと判りにくいので、可能であれば/etc/servicesのサービス名を記述するようにして、サービス名がない場合のみポート番号を記述するようにしたほうが良いでしょう。RFC等で規定されているポートの場合は/etc/servicesファイルに追記しても良いのですが、そうでない場合は止めておくほうが無難です。

次にテーブルの設定を行います。

PFにおけるテーブルとはIPアドレスグループを保持するために使用するものだとされています。リストと比べて検索速度が速いらしいので、IPアドレスはテーブルを使用して定義します。テーブルの書き方は

table <テーブル名称> オプション テーブルの内容

となります。

基本的に変更のないものについてはpf.conf内に、pfctlコマンドを使用して動的に変更したい内容の場合は外部ファイルに持つようにします。

変更のないテーブルの場合

table <テーブル名称> const {テーブル内容のリスト}

外部ファイルに持つ場合

table <テーブル名称> persist file "ファイル名(フルパス)"

のように書きます。

また、リスト中のIPアドレスの書き方は”ネットワークアドレス/ネットワークマスク長”となります。

おいらの環境では

# Private address summry
table <private> const { 10/8, 172.16/12, 192.168/16 }
# Special IP
table <special> const { 0/8, 14/8, 24/8, 39/8, 127/8, 128.0/16, 169.254/16, 192.0.0/24, 192.0.2/24, 192.88.99/24, 198.18/15, 223.255.255/24, 224/4, 240/4 }
# AUTH BLOCK
table <authblock> persist file "/var/db/authblock"

のように定義しています。

  • privateテーブル → 自ネットワーク内のローカル側で使用するIPアドレスリスト
  • specialテーブル → インターネット上存在していないはずのアドレス群
  • authblockテーブル → /var/db/authblockファイルを参照

authblockの使い方は後述します。

次にオプションを記述します。

# Option
# ブロックしたパケットは全て破棄(無反応)。拒否を返す場合はreturn。
set block-policy drop
# ロギングするインターフェイスを指定
set loginterface $ext_if

基本的にフィルタは素性の悪い人に対して効力を発揮するものです。そういう人をわざわざ煽ることもありませんから、block-policyについてはdropにすることをお勧めします。某鳥坂先輩のように「かかってきなさい」なスタンスなのであればお止めしませんけれども(笑)。

ここからフィルタルールについて。

PFのフィルタルールは記述順に解釈されていきます。ただし、ルールにヒットしてもすぐにそのルール通りの動作になる訳ではなく、全てのルールが比較された後に動作が決まります。

該当ルールにヒットした場合に以降の比較を行わないならquickオプションを指定してください。

まず、Scrub(スクラブ)の設定を行います。

Scrubは研磨とかそういう意味があるようですが、PFにおいてはパケットの正規化を行うもので、分断化されたパケットを再構築し、正しくないフラグを持つパケットを破棄するとされています。

「パケット フラグメント 攻撃」でぐぐると事例が出てきますが、こういったものを回避するオプションがScrubだと考えれば宜しいかと思います。(OpenBSDのドキュメントにはNetwork Intrusion Detection: Evasion, Traffic Normalization, and End-to-End Protocol Semanticsを読みなされと書かれていますが、英語なのでおいらには解読不能です。)

どのような設定が解なのかはお使いの環境に合わせてください。おいらの環境では

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

としていて、IN/OUT共にScrubを適用、OUT方向にはrandom-idとmax-mssオプションをつけています。

max-mss 1414はBフレッツを使って接続している環境なのでPPPoEのパケットサイズを合わせているため。さくらVPSのような環境ではいらないはずです。

random-idはTCPシーケンス番号予測攻撃対策用途(のはず)で識別フィールドをランダムな値に置き換えるというものです。

次にIPスプーフィング(IPアドレス詐称)よけにantispoofを設定しますが、antispoofを設定するとローカルループバック(lo0)からのパケットをブロックするような事を言っているので、antispoofを適用したくないインターフェイスについては、antispoofを記述する前に

### LAN
pass in quick on $int_if all
pass out quick on $int_if all
### loopback ###
pass in quick on lo0 all
pass out quick on lo0 all

のようにquickでルールを記述しておいた上で、antispoofを記述します。

# Spoofing
antispoof log quick for $ext_if inet

PFは特に指定しなければ全て透過するようになっています。なので、明示的にブロックするように

# Default Deny
block in all
block out all

と透過禁止を明示的に記述しておきます。また、上記は

block all

のようにまとめる事も出来ます。

後はずらずらとフィルタルールを記述していきます。以下、おいらの環境サンプル

IN方向のフィルタルール

# TCP
# インターネット上存在しないアドレスからのパケットブロック
block in log quick on $ext_if from { <special> } to any
# authblockテーブルに存在するIPアドレスからのパケットブロック
block in log quick proto tcp from <authblock> to any
# Windows95/98からのsmtpパケットブロック
block in log quick on $ext_if proto tcp from any os {"Windows 95", "Windows 98"} to $ext_addr port smtp
# ident/Authポートへのパケットに対してRSTパケット(TCPリセット)を返す
block return-rst in quick on $ext_if proto tcp from any to any port 113
# tcp_serviceマクロ記載ポートへのアクセスを許可
pass in quick on $ext_if proto tcp from any to any port $tcp_services flags S/SA modulate state
# 全てのTCP INパケットを禁止
block in quick on $ext_if proto tcp all
# UDP
# udp_servicesマクロ記載のUDPポートパケットを許可
pass in quick on $ext_if proto udp from any to any port $udp_services keep state
# 全てのUDPポートパケットのブロック
block in quick on $ext_if proto udp all
# ICMP
# 経路障害関連(3,11)/Ping(8)のみを許可(ping禁止なら8を除外)
pass in quick on $ext_if inet proto icmp all icmp-type { 3, 8, 11 } keep state
# 上記以外のICMPをブロック
block in quick on $ext_if proto icmp all

外向けパケット

# WAN outgoing
# インターネット上存在しないIPアドレスへのパケットをブロック
block out quick on $ext_if from any to { <special> }
# ICMP
# Echoのみ許可
pass out quick on $ext_if inet proto icmp all icmp-type 8 code 0 keep state
# それ以外のICMPをブロック
block out quick on $ext_if proto icmp all
# ルールになければ許可
pass out quick on $ext_if proto tcp all modulate state
pass out quick on $ext_if proto udp all keep state

/etc/pf.confが記述できたら、/var/dbの下にauthblockというファイルを作成してください。当初は空で構いませんので

touch /var/db/authblock

を実行します。

次にpf.confの構文チェックを行うため、

pfctl -nf /etc/pf.conf

を実行して、エラーが出ないことを確認します。

エラーが出ないことを確認してから、/etc/rc.confにPF起動のためのオプションを記述します。

# PF
pf_enable="YES"
pf_rules="/etc/pf.conf"
pf_flags=""
pflog_enable="YES"
pflog_logfile="/var/log/pflog"
pflog_flags=""

記述したら

/etc/rc.d/pf start

するかサーバを再起動しましょう。

PFが無事起動しているかどうか調べるにはkldstatを実行してpf.koが表示されればOKです。

authblockの使い方。

このファイルは何に使うかというと、動的にブロックしたいIPアドレスを指定する際に使用します。

まず、authblockファイルにブロックしたいIPアドレスを

ネットワークアドレス/ネットマスク長

のように記述します。例えば192.168.1.1をブロックしたい場合

192.168.1.1/32

と記述します。次に

pfctl -t authblock -T replace -f /var/db/authblock

を実行すると、192.168.1.1からのTCPパケットは全てブロックされます。解除するにはauthblockファイルから192.168.1.1/32を削除し、同様に上記コマンドを実行します。


コメント

  1. […] と追記してください。(前回の例で言うと、追記場所はauthblockの下辺り) […]

タイトルとURLをコピーしました