FreeBSDでpfを使う（1/2）


OS Xを含むBSD系OSで使われている Firewall の pf の設定メモ．普段は Linux の iptables しか書いていない人なので間違っているところもあるかも．できるだけ iptables と対比させながら書いて見る．pfのテスト環境は`10.0-RELEASE-p12`．iptablesの方は検証せずに書いているので参考程度に．

### 参考資料

* [FreeBSDのドキュメント](https://www.freebsd.org/doc/handbook/firewalls-pf.html)
* [OpenBSDのpfのドキュメント](http://www.openbsd.org/faq/pf/index.html)

原則
---

pf はルールが上から順番に評価されるが，マッチするルールがあってもそこで評価は停止しない．最後にマッチしたルールが適用される．ただし，`quick`をつけた場合にはそこで評価が終了し，そのルールが適用される．

### Rule Syntax

フィルタの文法は以下の通り．詳しくは[OpenBSDのドキュメント](http://www.openbsd.org/faq/pf/filter.html)を参照のこと

```conf
action [direction] [log] [quick] [on interface] [af] [proto protocol] \
   [from src_addr [port src_port]] [to dst_addr [port dst_port]] \
   [flags tcp_flags] [state]
```

FreeBSDでのpfの有効化方法
----------------------

`/etc/rc.conf`に以下の行を追加する．

```conf
pf_enable="YES"
pf_flags=""
pflog_enable="YES"
gateway_enable="YES"
```

とりあえず有効化するだけであれば`service pf start`でできる．ログは別のサービスで取るので`service pflog start`も必要．`gateway_enable`はNATを使いたければ必要．


基本形
-----
ローカルループバックは許可し，入ってくるパケットはログを取った上ですべて拒否する．ただし，出て行くパケットに関連付けられていたパケットは許可する．

```conf
# Allow Local Loop back
set skip on lo0

# packet is silently dropped
set block-policy drop

# Block and log add packets from outside
block in log all

# Allow all packets to go out, and keep state of them
pass out all keep state
```

`pass out all keep state`で出て行くパケットの状態を記録し，これに関連する内向き通信を許可することを示している．関連すると判定された場合にはそもそもフィルタのチェック対象にはならない．iptablesの`-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT`に相当するが，in/outが逆なので一瞬戸惑う．

### 相当するiptables

```
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT 
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -j LOG --log-prefix "iptables-INPUT-drop:" --log-level 5
```

ログを確認する
------------

ログは標準では`/var/log/pflog`に出力されている．ただ，このログはバイナリなので直接は読めない．そこで`tcpdump`を使う．

```sh
$ tcpdump -n -e -ttt -r /var/log/pflog
```

iptablesのようにログにヘッダーはつけられないけど，何番目のルールにマッチし，結果としてblockされたのかpassされたのかも一緒に記録されるのでそれほど問題にはならないはず．

[参考](https://www.freebsd.org/cgi/man.cgi?query=pflogd&sektion=8&apropos=0&manpath=FreeBSD+10.1-RELEASE)

もし，ログをリアルタイムに確認したければ`tcpdump -n -e -ttt -i pflog0`でできる．


特定のサービスのIncomingを許可
-------------------------

SSHを許可してみる．サービスの名前は`/etc/services`にあるものが使える．数字でも良い．

```conf
set skip on lo0
set block-policy drop

block in log all
pass in proto tcp to any port ssh
pass out all keep state
```

### 相当するiptables

```
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT 
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -j LOG --log-prefix "iptables-INPUT-drop:" --log-level 5
```

特定のアクセス元のみIncomingを許可
------------------------------

IPアドレスを指定して接続を許可する．この例では`192.168.67.0/24`からのSSHアクセスを許可している．

```conf
set skip on lo0
set block-policy drop

block in log all
pass in proto tcp from 192.168.67.0/24 to any port ssh
pass out all keep state
```

### 相当するiptables

```
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT 
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -m state --state NEW -m tcp --dport 22 -s 192.168.67.0/24 -j ACCEPT
-A INPUT -j LOG --log-prefix "iptables-INPUT-drop:" --log-level 5
```

複数のポート，IPをまとめて指定する．
-----------------------------

`{}`で囲うことで複数のサービス/IPをまとめて指定できる．

```conf
set skip on lo0
set block-policy drop

block in quick proto udp to any port 17500
block in log all
pass in log proto tcp from {192.168.67.0/24, 192.168.68.0/24} to any port {ssh, http}
pass out all keep state
```

変数を使う
--------

変数のようなものが使える．

```conf
set skip on lo0

tcp_services="{ ssh, http }"
acceptables_ip="{ 172.16.0.0/12, 192.168.0.0/16 }"

block in quick proto udp to any port 17500
block in log all
pass in log proto tcp from $acceptables_ip to any port $tcp_services
pass out all keep state
```