pfを使い外部からのアクセスを内部ネットワークへリダイレクトする

FreeBSDではファイアウォールとしてpfが標準で使えます。くわえてFreeBSDではbhyvejailのようなハイパーバイザやコンテナも利用できます。FreeBSD特有の連携というわけではありませんが、これらの機能を連携させることで、仮想マシンやコンテナ上のデータをそのホストで提供しているかのように振る舞えます。


ネットワーク構成

今回例に挙げるネットワーク構成は図1のとおりです。

PF routing example
図1: LAN内のデスクトップPCに内部ネットワークをつくり仮想マシンやコンテナなどをそのネットワークに所属させる構成

あるLAN(ここでは172.16.0.0/24のネットワーク)にはおもにデスクトップPCやノートPC、スマートフォンが接続されます。物理マシンは基本的にこのネットワークに所属するわけです。

一方「PC A」では内部ネットワーク192.168.1.0/24をつくり、そのネットワークにゲストの仮想マシンやコンテナを接続させるようにしています。ようはNATです。「PC A」がゲートウェイの役割も果たすため「仮想マシン A」や「仮想マシン B」からインターネットに出ることはできます。しかし172.16.0.0/24内の「PC B」から「仮想マシン A」には直接アクセスできません。

リダイレクトのためのpf.conf

ここで「PC B」から「仮想マシン A」「仮想マシン B」へアクセスするには、「PC A」でpfを動かしリダイレクトの設定をします。具体的には、/etc/pf.confを書き、service pf startするだけです。まずはpf.confを書きましょう。

ext_if = "re0" # 「PC A」のネットワークインタフェース
bhyve_internal_network = "192.168.1.0/24" # 仮想マシンの内部NW
vm_A = "192.168.1.20" # 「仮想マシン A」のIPアドレス
vm_B = "192.168.1.50" # 「仮想マシン B」のIPアドレス

# 192.168.1.0/24と「PC A」のネットワークインタフェースとでNATします
nat on $ext_if from { $bhyve_internal_network } to any -> $ext_if

# リダイレクトの設定
# re0へのTCP通信が特定のポートへ来たのであれば内部NWのVMへそれぞれ
# 転送します
rdr on $ext_if proto tcp from any to $ext_if port 80 -> $vm_A port 80
rdr on $ext_if proto tcp from any to $ext_if port 443 -> $vm_A port 443
rdr on $ext_if proto tcp from any to $ext_if port 22 -> $vm_B port 22

# はじめにre0への通信はすべてブロックします
block in on $ext_if all

# 外向きのパケットはすべて許可します
pass out all

# 許可する通信を宣言します
# 192.168.1.20の80番・443番へのアクセスと、
# 192.168.1.50の22番へのアクセスは許可します
pass in on $ext_if inet proto tcp from any to $vm_A port 80
pass in on $ext_if inet proto tcp from any to $vm_A port 443
pass in on $ext_if inet proto tcp from any to $vm_B port 22

リダイレクトの設定をrdrルールで記述しても、passルールで通信を許可することを忘れないでください。

またnatrdrpassといった各ルールは一定の順序にしたがって書くよう標準で強制されています。詳細はオンラインマニュアルpf.conf(5)に譲りますが、ここではTranslationのルールnatrdrはPacket Filteringのルールblockpassよりも先に書かなければいけません。

pfを動かす

/etc/pf.confに不備がなければpfデーモンを動かしルーティングを開始してください。文法エラーなどなにかしらの不備がある場合、pfは動かずエラーメッセージを出力して終了します。

service pf start

おわりに

上記のように通信を別ホストに転送する設定によって、ホストPCで直接なにかしらのデータやコンテンツを管理することなく、比較的安全なVMの上で任意のサーバを運用できます。今回はホストOSがFreeBSDという前提で話を進めましたが、仮想マシンのOSは自由に選択できます。「NATでネットワークを隔離したいけれど、ときどきLAN内のマシンからアクセスしたい」というニーズをうまく満たせられるでしょう。