NetBSD Advent Calendar 2022 2日目の記事です。今年もNetBSDの記事を増やして行きましょう!
今日はnpf(7)におけるフィルタルールの記法を紹介しようと思います。
NPFのフィルタルール
NPFに限らず、ファイアウォールを設定する際には何らかの記法でフィルタルールを設定します。FreeBSDのipf(5)では以下のような記述を用います。ipfではフィルタルールとルールのグルーピングを一行で指定する形になっています。
# ICMPパケットをブロックする例
block in log quick proto icmp all group 100
同等のフィルタルールをNPFに置き換えると以下のようになります。一行でルールを表現するのはipfと同じなのですが、フィルタルールをグルーピングしたうえでルールを記述できる記法になっています。このように
、NPFは他のファイアウォール実装におけるフィルタルールと比べてわかりやすいルール記述が可能となっています。
group "icmp rule" {
block in on $ext_if proto icmp all apply "log"
}
また、ルールのグルーピング以外にも、変数やインタフェースからIPv4/v6アドレスの取得、IPアドレスのリスト(特定のポート疎通を許可する等で使用)といった、便利な記法(文法)がサポートされています。
NPFのフィルタルール文法
NPFのフィルタルールの文法を見てみます。文法の全てを紹介しているわけではありませんが、通常設定するフィルタルールを記述する場合は、ここで紹介する内容で十分かと思います。
変数
NPFのフィルタルールでは変数を使用可能で、先頭に $
をつけると変数として扱われます。変数に代入可能な値は以下となっており、 $var2 = { 10.0.0.1, 10.0.0.2 }
のように値をリストとして代入することも
可能です。
- IPアドレス
- ネットワーク
- ポート
- インタフェース
$var1 = 10.0.0.1
$var2 = { 10.0.0.1, 10.0.0.2 }
インタフェース
インタフェースに降られているIPアドレスの取得も文法でサポートされています。 inet4()
, inet6()
でそれぞれIPv4,IPv6アドレスが取得できます。また、 ext_addrs()
を使用すると、IPv4,IPv6両方のアド
レスを取得できます。
ちなみに、 inet4()
等はインタフェースに紐づくIPアドレスを返す拡張関数として実装されているとのことです。
$ext_if = "wm0" # LANの外側のインタフェース
$ext_v4 = inet4($ext_if) # IPv4アドレスを取得する
$ext_v6 = inet6($ext_if) # IPv6アドレスを取得する
$ext_addrs = ifaddrs($ext_if) # IPv4/IPv6両方のアドレスを取得する
グループ
NPFでは「グループ」という単位でフィルタルールを定義します。 default
グループは最初から存在しているグループになっており、パケットがどのグループにもマッチしなかった場合は、この default
グループ
のルールがマッチングされます。
そのため、 default
グループでは不要なパケットを全てブロックする設定にしておき、他のグループで疎通させたいパケットのルールを記述する、という使い方が良さそうです。
グループは複数定義することも可能なうえ、入れ子の形でも定義できます。このあたりの記法を活用すると、フィルタルールのメンテナンスが楽になりそうです。
$ext_if = "wm0" # LANの外側のインタフェース
$int_if = "wm1" # LANの内側のインタフェース
# グループに名前をつけることも可能。
# このグループには"icmp rule"という名前が付けられている
group "icmp rule" {
# LANの外側からのICMPパケットは全てブロックする
block in on $ext_if proto icmp all apply "log"
block out on $ext_if proto icmp all apply "log"
# LANの内側から出てゆくICMPパケットは許可する
pass in on $int_if proto icmp all
pass out on $int_if proto icmp all
pass stateful out all
}
group default {
# defaultグループのルール
}
また、グループにインタフェースを紐づけておくことも可能です。以下の例ではLANの外側のインタフェースに対するsshアクセスをブロックする設定です。group "external" on $ext_if
という記述で、"external"
グループのフィルタルールがLANの外側のインタフェースにのみ適用される形になります。
$ext_if = "wm0" # LANの外側のインタフェース
group "external" on $ext_if {
block in on $ext_if from any to $ext_v4 port 22 apply "log"
}
ルール
パケットフィルタルールを指定する、一番肝心な文法です。ルールの例はこれまでにいくつか挙げていますので、ここではルールのシンタックスを示します。
ルールのマッチング動作としては、複数ルールが存在した場合は最初に一致したルールが適用されます。NPFの挙動としては、フィルタルールを一通り当てはめたのち、一致するルールを適用しますが、 final
キー
ワードを指定することで、そのルールにマッチした時点でパケットを処理させることも可能です。基本的にはルールにマッチした時点でパケットを処理したいはず(?)なので、 final
指定は頻繁に使用するような気
がします。
また、 proto
キーワードを指定することで、任意のプロトコル(TCP,ICMP等)をフィルタすることも可能です。
group = "group" ( "default" | group-opts ) "{" rule-list "}"
group-opts = name-string [ "in" | "out" ] [ "on" interface ]
rule-list = [ rule new-line ] rule-list
npf-filter = [ "family" family-opt ] [ proto ] ( "all" | filt-opts )
static-rule = ( "block" [ block-opts ] | "pass" )
[ "stateful" | "stateful-ends" ]
[ "in" | "out" ] [ "final" ] [ "on" interface ]
( npf-filter | "pcap-filter" pcap-filter-expr )
[ "apply" proc-name ]
dynamic-ruleset = "ruleset" group-opts
rule = static-rule | dynamic-ruleset
アドレス変換(NAT)
アドレス変換、いわゆるNAT(Network Address Translation)です。 ->
で外に出てゆくパケット(outbound)のアドレス変換、 <-
で外から入ってくるパケット(inbound)のアドレス変換を行います。以下の例ではLANの内側( 192.168.0.0/24
のネットワーク)からLANの外側に出てゆくパケットのアドレス変換ルールを設定しています。
また、 <->
でoutbound/inboundのアドレス変換ルールも設定可能です(ちょっと具体的なユースケースが思いつかない...)。
$ext_if = "wm0" # LANの外側のインタフェース
$int_if = "wm1" # LANの内側のインタフェース
まとめ
NPFにおける、パケットフィルタルールの文法を紹介しました。ここで紹介した文法を用いることで、最低限のフィルタルールを記述できるようになるかと思います。