How to Set Up a Firewall Server on FreeBSD 14.2
はじめに
私の自宅にはサーバーがいくつかあります(このウェブサーバーも含め)。シンプルなウェブサーバーとして動いているだけでは少し物足りなく感じていた時、スイッチングハブをもらいました。このまま使っても良かったのですが、ファイアウォールサーバーを作って一層セキュリティを追加したら面白いんじゃないかと思い、作成してみました。
ネットワーク図は大体こんな感じになる想定です。

手元にあった余ったPCパーツを集めて新しいサーバーを1台組み立てました(ミドルタワーケースを使用したので少し大きめ)。これを自宅ネットワークのファイアウォールサーバーとして活用していくことにしました。
ファイアウォールには様々な選択肢がありますが、今回はFreeBSDのPF(Packet Filter)を使用することにしました。他のファイアウォール専用のLinuxディストリビューションも検討しましたが、それらの多くはWebUIでの操作を前提としていて、私の好みであるSSH接続でのターミナル操作があまり考慮されていないように感じました。
普段からターミナルでの操作を好んでおり、コマンドラインでの設定変更やログ確認が自然に行える環境を求めていた私にとって、FreeBSDとPFの組み合わせは理想的でした。PFの設定はシンプルで直感的なため、学習用としても最適です。また、テキストベースの設定ファイルで管理できることで、バージョン管理システムとの相性も良く、設定の変更履歴を追跡しやすいという利点もあります。
初期設定
今回、FreeBSDの公式サイトからISOファイルをUSBに焼いてインストールしました。
https://www.freebsd.org/where
インストーラーでは以下を選択しました。ファイアウォールサーバーとしての目的から、最小構成で行いました。
キーマップ: Japanese 106
ホスト名:
コンポーネント: base-system, kernel, ports(最小構成)
パーティション: Auto (UFS)
他の設定は適当でも後から変更が可能です。
インストール完了後、以下のコマンドを入力します。
pkg update && pkg upgrade
pkg install -y isc-dhcp44-server
ifconfigを行い、インターフェースが正しく認識されているか確認します。
$ ifconfig
rl0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
options=2008<VLAN_MTU,WOL_MAGIC>
ether 00:a0:b0:a5:c4:11
inet 192.168.2.125 netmask 0xffffff00 broadcast 192.168.2.255
media: Ethernet autoselect (100baseTX <full-duplex>)
status: active
nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
re0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
options=8209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC,LINKSTATE>
ether 40:8d:5c:46:e7:2d
media: Ethernet autoselect (1000baseT <full-duplex>)
status: active
nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
lo0: flags=1008049<UP,LOOPBACK,RUNNING,MULTICAST,LOWER_UP> metric 0 mtu 16384
options=680003<RXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
inet 127.0.0.1 netmask 0xff000000
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x3
groups: lo
nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
rl0
: 外側(インターネット側)のインターフェース。DHCPで自動的にIPが割り当てられているre0
: 内側(LAN側)のインターフェース。まだIPアドレスが設定されていないlo0
: ループバックインターフェース。システムの内部通信に使用
無事に両方のネットワークがみえていたので、次にファイアウォールサーバーのネットワーク基本設定を構築していきます。
基本設定
/etc/rc.confの設定を行っていきます。
※今回ルーター側のIPも固定にしましたが、ルーター側での設定が必要になる場合があるので、行う場合は使用している機器の説明書などを読みながら進めましょう
hostname="hogehoge"
keymap="jp.kbd"
ifconfig_re0="inet 192.168.3.1 netmask 255.255.255.0" # 内向き
ifconfig_rl0="inet 192.168.2.53 netmask 255.255.255.0" # 外向き
defaultrouter="192.168.2.1"
gateway_enable="YES"
pf_enable="YES"
pflog_enable="YES"
dhcpd_enable="YES"
dhcpd_ifaces="re0"
続いて、/etc/pf.confを作成していきます
ext_if="rl0"
int_if="re0"
trusted_net="192.168.3.0/24"
set block-policy drop
set skip on lo0
nat on $ext_if from $trusted_net to any -> ($ext_if)
pass in on $int_if all
pass out on $int_if all
# 外部ネットワークのルール
pass out on $ext_if all
pass in on $ext_if inet proto tcp to $ext_if port {22, 80, 443}
pass in on $ext_if inet proto icmp all
その後、PFが正常に動作するか確認していきます
pfctl -pf /etc/pf.conf
service pf start
pfctl -f /etc/pf.conf
DHCPサーバーの設定とセキュリティ強化
DHCPサーバーの設定を行っていきます。/usr/local/etc/dhcpd.confを作成していきます。
※サーバー関係はIPを固定にしたほうが管理が楽なので、一般的なデバイスは100~200番、サーバー用は2~99番にしました。
cat /usr/local/etc/dhcpd.conf
subnet 192.168.3.0 netmask 255.255.255.0 {
# DHCPは100~200
range 192.168.3.100 192.168.3.200;
option routers 192.168.3.1;
option domain-name-servers 8.8.8.8, 8.8.4.4;
default-lease-time 600;
max-lease-time 7200;
}
# 固定IPを割り振りたい場合
# web-serverの場合
host web-server {
hardware ethernet XX:XX:XX:XX:XX:XX; # デバイスのMACアドレス
fixed-address 192.168.3.10; # 割り当てたい固定IP
}
リースタイムは短めに設定(デフォルト10分、最大2時間)していますが、複数接続をしないのであればもっと長くても問題ないと思います。
デバイスのMACアドレスの確認について色々方法がありますが、FreeBSDの場合「arp -a」というコマンドで通信している機器一覧のMACアドレスが確認できます。
$ arp -a
web-server (192.168.3.10) at 00:11:22:33:44:55 on re0 expires in 1200 seconds
DHCPサーバーを起動していきます
service isc-dhcpd start
私の場合、外部からウェブサーバーにSSH接続することがよくあるので、PFに明示的に設定を追加しました。
ext_if="rl0"
int_if="re0"
trusted_net="192.168.3.0/24"
# ウェブサーバー追加
web_server="192.168.3.10"
set block-policy drop
set skip on lo0
# リダイレクト設定
rdr on $ext_if proto tcp to ($ext_if) port {22,80,443} -> $web_server
nat on $ext_if from $trusted_net to any -> ($ext_if)
# レート制限
pass in on $ext_if proto tcp to port {22,80,443} flags S/SA keep state \
(max-src-conn 100, max-src-conn-rate 15/5, \
overload <ratelimit> flush global)
pass in on $int_if from $trusted_net to any
pass out on $ext_if from any to any
pass in on $ext_if proto icmp from any to any
pass out on $ext_if proto icmp from any to any
pass from $trusted_net to 192.168.2.0/24
pass from 192.168.2.0/24 to $trusted_net
pass in quick on $int_if proto udp from any to 255.255.255.255
pass in quick on $int_if proto udp from any to 192.168.3.255
# ウェブサーバーへのアクセス許可
pass in on $ext_if proto tcp from any to $web_server port {22,80,443}
追加で、PFのテーブルという機能を使用して、特定IPアドレスのリストを管理するようにします。
#インターフェースの以下に追加
table <blocklist> persist file "/etc/pf.blocklist"
table <bruteforce> persist
table <geoblock> persist
table <ratelimit> persist
table <fail2ban> persist
blocklist: 永続的にブロックしたいIPアドレスのリスト。外部ファイルから読み込むため、更新が容易
bruteforce: SSHなどへの総当たり攻撃を行うIPアドレスを動的に追加
geoblock: 特定の地域からのアクセスをブロック
ratelimit: 短時間に大量のアクセスを行うIPを一時的にブロック
fail2ban: Fail2banと連携して不正アクセスを行うIPを自動でブロック(fail2banを要インストール)
テーブルへのIPアドレスの追加は以下のコマンドは以下で行うことができます。
pfctl -t blocklist -T add 192.168.1.100
もしくは、/etc/pf.blocklistに直接追加も可能です。
cat /etc/pf.blocklist
192.168.1.100
192.168.1.101
※PFではルール順序を守らないとエラーになってしまいます。また、パケットに関しては上から順に評価されて最初にマッチしたルールが適用されます。コメントアウトを慰労してセクション分けをすることをオススメします。
おわりに
FreeBSDのPFを使用することで、比較的シンプルな設定でもセキュアなネットワーク環境を構築することができます。Unixに慣れ親しんだ方なら結構簡単に設定が出来ると思います。
一応、現在の設定でも十分実用的ですが、さらなるセキュリティ強化のため以下のような拡張を検討しています
- GeoIPを使用した国別アクセス制限
- pfBlockerNG-develによる高度なファイアウォール機能の実装
- ログ解析ツールの導入によるネットワークトラフィックの可視化
fail2ban自体はウェブサーバー上で稼働させており、現在外側に公開するサーバーはそれ以外ないのでFreeBSDでは必要ないかなと思い、設定などしませんでした。今後もしかしたら設定するかもしれませんが、この記事では言及しません。
(そもそも自宅のネットワークをそんな複雑にすることはないと突っ込まれそうですが、そこはロマンということで・・)
実装でき次第、順次記事にしていく予定です。
おわり