FreeBSDをルータに
いまどき、ルータといえば、自宅なら無線WiFiルータ、業務でも専用のハードウェアを置くのが当たり前である。
FreeBSDをルータにするのは流行らない。
しかしだからこそ、もしものときのために記録に残す。
方針
FreeBSDをルータにするにはいろいろと種類がある。
特にそういったことは概観せず、もういきなり決め打ちするが、pfを使う。
本稿での構成
構成は以下のとおりである。
セキュリティ要件は一切、設定しない。
ただ、単にFreeBSD Routerを通してLAN内のクライアントが外に出ていければよい。
セキュリティはあとでゆっくり、ご自由にどうぞ。
Gateway to The Net <-- WLAN --> (wlan0) FreeBSD Router (em0) <-- Ethernet --> LAN
192.168.10.1 192.168.10.x 10.0.0.1 10.0.0.x
DHCP Server DHCP Client pf DHCP Server DHCP Client
本稿で扱う部分
上記構成図で言う、FreeBSD Routerのpf設定をメインにする。
ただし、FreeBSDのインタフェースが、片方がDHCP Client、他方がDHCP Serverになっているので、影響しそうなところは触れる。
また、以下、特に断りのない限り、FreeBSD Routerのことを「ルータ」と記す。
デフォルトルータの設定
兎にも角にもまずデフォルトルートを設定する。
これは言わずもがな、FreeBSDルータから見てインターネット側へのデフォルトルート。/etc/rc.conf
に以下を書き込む。
defaultrouter="192.168.0.1"
再起動したあと、/etc/resolv.conf
に以下の記載があることを確認する。
これはLAN向けのネームサーバですね。
# Generated by resolvconf
nameserver 192.168.10.1
pfの有効化
pfを有効にします。有効にするには/etc/pf.conf
に以下を書き込む。
pf_enable="YES"
もちろん、以下を実行してもOK。
sudo sysrc pf_enable=YES
pf基本設定
pfのログはデフォルトで/var/log/pflog
に記録される。
実際の記録にはrc.conf
でpflog_enable=yes
を記載する必要がある。
前後するが、私の/etc/rc.conf
は以下の通り。
# For router
pf_enable=yes
pflog_enable=yes
gateway_enable="YES" # set to YES if this host will be a gateway
defaultrouter="192.168.10.1"
FreeBSDのルータ化
上記で少し書いたが、ルータにするには、/etc/rc.conf
でgateway_enable=yes
を記載しておく必要がある。
pfルールの設定
exampleがあるので、/etc
の下にコピーする。
sudo cp /usr/share/examples/pf/pf.conf /etc/pf.conf
といいつつ、いったん全部コメントアウトして、必要最低限のものだけ有効化していく。
pf.confにおける記載順序
pf.conf
には記載順序にルールがある。
具体的には、以下の順番であることが求められる。
もっとハッキリ言うと、この順番でないとpfは起動しません。
- Options
- normalization
- queueing
- translation
- filtering
本稿では2, 4, 5が該当。
2はルータを通過するトラフィックの正常化のこと。
4は例えばNATのこと。
5はフィルタのこと。
このほか、記述を簡単にするためのマクロ定義がある。これはその性質上、冒頭で扱う。(だって文末でマクロ定義したって仕方ないもんね)
ではさっそくマクロ定義から。
定義(マクロ)の宣言
インターネット側IFをext_if
として、LAN側IFをint_if
に設定する。
外側向けIFはwlan0
、内側はem0
なのでそのとおりに。
ext_if="wlan0"
int_if="em0"
LAN側ネットワークとしてlocalnet
を定義。
こうすると、以降のルールでLAN側ネットワークを$localnet
として扱うことができる。:network
とは、ネットワークインタフェースに続けて記載し、そのインタフェースに接続されたネットワークのことを表す。
この場合、LAN側インタフェースにつながっているネットワーク全部を指し示す。
localnet = $int_if:network
トラフィック正常化
いたずらパケットを正常化するためのルール。
要件に示したように、特にセキュリティのためのルール設定はしないが、これは難しいこともなく一行追加するだけでよいので記述。
こうするとpfは断片化されたパケットを再構築したり、ありえない組み合わせのTCPフラグが立ったパケットを蹴飛ばすとかしてくれる。
やっておいて損はない。
scrub in all
NAT設定
目玉であるNATの設定。nat on
に続けて、NATを有効にするインタフェースを指定する。
ここでは$ext_if
。
起点はLAN側IFのすべての通信なので、from $localhost
を指定。
宛先は特に制限しないのでto any
とする。
それら通信のソースアドレスを$ext_if
に変換して外側へ送り出す。
なお、$ext_if
はダイナミックなので($ext_if)
と記述する。
こうすることで、IPアドレスが変わったとしても通信が継続されるようになる、と書いてある。
# ext_if IP address could be dynamic, hence ($ext_if)
nat on $ext_if from $localnet to any -> ($ext_if)
フィルタリング設定
まず全部の通信をブロック。
block in all
Keep state設定。
これ複雑なので図示する。
ルータの入り口である$int_if
に入るときと、
ルータからの出口である$ext_if
から出るときの双方に設定が必要。
LAN ---> em0 ---> wlan0 ---> the Net
--> rule 1
--> rule 2
上記をpfルールとして記述すると以下の通り。
rule1が1行目に、rule2が2行目に対応。
pass in on $int_if from $localnet to $ext_if:network keep state
pass out on $ext_if from $localnet to $ext_if:network keep state
設定確認
ここまでできたら設定確認をする。
確認はpfctl -vnf /etc/pf.conf
とすればよい。
実行してみると、各種ルールが具体的に展開されて表示されるので、自分で作ったルールを検証するとよい。
$ sudo pfctl -vnf /etc/pf.conf
ext_if = "wlan0"
int_if = "em0"
localnet = "em0:network"
scrub in all fragment reassemble
nat on wlan0 inet from 10.0.0.0/24 to any -> (wlan0) round-robin
block drop in all
pass inet from 127.0.0.1 to any flags S/SA keep state
pass inet from 10.0.0.0/24 to any flags S/SA keep state
pfの実行
pfの実行にはpfctl -e
と、停止にはpfctl -d
とすればよい。
また、実行中の通信についてはpfctl -s states
とすればよい。
設定読み込み
設定変更をした場合、それを有効化するには以下のようにすればよい。
pfの有効化はルータマシンでやりましょうね(戒め)。
$ sudo pfctl -F all -f /etc/pf.conf
rules cleared
nat cleared
0 tables deleted.
7 states cleared
source tracking entries cleared
pf: statistics cleared
pf: interface flags reset
以上