境界ネットワークにおける単一障害点(SPOF)の課題
昨年、エッジゲートウェイのハイパーバイザーで突発的なクラッシュが発生し、社内ネットワークから240名のエンジニアが40分間にわたり切断される事態に見舞われました。FreeBSDカーネルのアップデート適用、IPSエンジンの再起動、Suricataルールパックの反映など、日常的な定期メンテナンスですら深夜の作業枠を確保し、祈るような思いで実施しなければならない運用上の大きなリスクとなっていたのです。障害時には稼働中のWireGuardやOpenVPNトンネルが一瞬で切断され、クエリ実行中だったPostgreSQLのレプリケーションストリームが途切れ、通話中のVoIPもすべて切断されました。
本番環境でファイアウォールを単一構成で運用することは、いつか必ず負けるギャンブルに等しいと言えます。ハードウェアコンポーネントは経年劣化し、ファームウェアのアップグレードが途中でハングすることもあり、PDU(電源タップ)のブレーカーが予期せず落ちることもあります。そこで私たちは厳格な再設計目標を設定しました。フェイルオーバーを2秒未満で完了させること、確立済みの接続を切断することなくアクティブなステートテーブルを維持すること、そしてルールの変更をすべて自動で同期することです。
OPNsenseは、実績あるBSDプロトコル群(CARP、pfsync、XMLRPC)によって、これらを標準機能として実現します。この2ノード構成を導入して以降、2回にわたるハードウェアのリプレイスや数十回におよぶファームウェアパッチの適用を経ても、当社の境界ネットワークで計画外のダウンタイムは一度も発生していません。
中核プロトコル:CARP、pfsync、XMLRPC
堅牢なOPNsenseクラスタは、連携して動作する3つの独立したBSDメカニズムに支えられています。
1. Common Address Redundancy Protocol(CARP)
CARPはレイヤー2およびレイヤー3におけるIPアドレスの共有を担います。Cisco独自のHSRPやVRRPに対するBSDのオープンソース代替プロトコルとして機能し、複数のファイアウォールで1つの仮想IP(VIP)を共有します。
クラスタ内の各ノードは、アドバタイズメントスキュー(advskew)値を用いて役割をネゴシエーションします。マスターノードは最も低いスキュー値(通常は0)で動作し、定期的なハートビートパケット(advbase、通常1秒に設定)をブロードキャストします。セカンダリノード(advskew 100に設定)がハートビートを3回連続で受信できなかった場合、自身をマスターへと昇格させ、Gratuitous ARP(GARP)を送信してスイッチ上のトラフィックを即座にリルートします。
2. pfsyncによるステート同期
IPアドレスの所有権を切り替えるだけでは片手落ちです。ステートフルインスペクションを行うファイアウォールは、TCPハンドシェイク、シーケンス番号、NAT変換情報を追跡しています。バックアップファイアウォールがプライマリのコネクションテーブルを持たずに引き継いだ場合、到着するすべてのインバウンドパケットを不正なステート外パケットと見なして即座に破棄してしまいます。
これを防ぐのがpfsyncドライバです。IPプロトコル240を使用し、専用リンク経由でステートテーブルの追加、更新、破棄情報をリアルタイムにストリーミングします。そのため、フェイルオーバーが発生した時点で、セカンダリノードにはすでにすべてのアクティブなTCP、UDP、ICMPセッションの同一テーブルが保持されています。
3. XMLRPCによる設定レプリケーション
ファイアウォールルールを手動で同期するのは時間がかかるだけでなく、設定の乖離(コンフィグドリフト)を確実に引き起こします。OPNsenseはos-xmlrpc-syncによってこの問題を解消しています。プライマリノードでApply Changes(変更を適用)をクリックするたびに、HTTPS経由の暗号化されたXMLRPCコールによって、更新されたルール、エイリアス、DHCP静的割り当て、NATポリシーがセカンダリへ直接プッシュされます。
ステップ別解説:デュアルファイアウォールクラスタの構築
以下は、2台のアプライアンス(Dell PowerEdgeベアメタルサーバーまたは別々のハイパーバイザー上の仮想マシン)で検証済みの構成例です。
インターフェースとIPアドレスの構成
両方のユニットに、ドライバ名が一致する3つの専用ネットワークインターフェースが必要です。
- WAN (vtnet0): プライマリ:
198.51.100.2/24| セカンダリ:198.51.100.3/24| 共有CARP VIP:198.51.100.1/24 - LAN (vtnet1): プライマリ:
10.0.10.2/24| セカンダリ:10.0.10.3/24| 共有CARP VIP:10.0.10.1/24(クライアントのデフォルトゲートウェイ) - SYNC (vtnet2): プライマリ:
172.16.99.1/30| セカンダリ:172.16.99.2/30(直結DACケーブルまたは分離されたスイッチVLAN)
ステップ1:SYNCインターフェースとフィルタリングルールの設定
両ユニットのvtnet2間をパッチケーブルまたは10GbE DACケーブルで直接接続します。このリンクは社内ネットワークの一般トラフィックから完全に分離してください。
両方のアプライアンスで、Interfaces > Assignmentsを開き、vtnet2をSYNCとして割り当てて、静的IPv4アドレス(172.16.99.1/30および172.16.99.2/30)を設定します。次に、両ノードのFirewall > Rules > SYNCへ進み、クラスタトラフィックを許可します。
# SYNCインターフェースで必要な許可ルール(両ノード共通):
# アクション: Pass | 方向: In | インターフェース: SYNC
# ルール 1: プロトコル CARP(IPプロトコル 112)/ 送信元 SYNC net / 送信先 any
# ルール 2: プロトコル PFSYNC(IPプロトコル 240)/ 送信元 SYNC net / 送信先 any
# ルール 3: プロトコル TCP、ポート 443(HTTPS)/ 送信元 SYNC net / 送信先 SYNC address
ステップ2:プライマリノードでのCARP VIP設定
プライマリのWeb管理画面にログインし、Interfaces > Virtual IPs > Settingsを開きます。+をクリックしてLANゲートウェイを定義します。
- Type: CARP
- Interface: LAN
- IP Address:
10.0.10.1/24 - Virtual IP Password: 複雑なクラスタパスフレーズを入力(例:
K8sSyncCluster#941!) - VHID Group:
1 - Advertising Frequency: Base:
1, Skew:0
WANインターフェースでも同様の手順を繰り返します。VHIDグループに2、アドレスに198.51.100.1/24、Baseに1、Skewに0を指定します。
ステップ3:セカンダリノードでのCARP VIP設定
セカンダリのWeb管理画面に切り替えます。プライマリと一致するVirtual IPを作成しますが、このノードの優先度を下げるためにアドバタイズメントスキューを引き上げます。
- LAN VIP:
10.0.10.1/24| VHID:1| Base:1| Skew: 100 | 同一のパスフレーズ - WAN VIP:
198.51.100.1/24| VHID:2| Base:1| Skew: 100 | 同一のパスフレーズ
アドバタイズメントスキューを100に設定することで、プライマリノードのハートビート(スキュー0)が毎秒届いている限り、セカンダリノードは完全にスタンバイ状態(サイレント)を維持します。
ステップ4:ステート同期(pfsync)とXMLRPCレプリケーションの有効化
プライマリノードで、System > High Availability > Settingsを開きます。
- Synchronize Statesにチェックを入れます。
- Synchronize Interface:
SYNCを選択します。 - pfsync IP:
172.16.99.2(セカンダリノード)に設定します。 - Synchronize Config to IP:
172.16.99.2を入力します。 - Remote System User:
root(または専用の同期用サービスアカウント)。 - Remote System Password: セカンダリノードのパスワードを入力します。
- レプリケーション対象にチェック:Firewall Rules、Aliases、Outbound NAT、Virtual IPs、DHCP Server。
セカンダリノードでは、pfsyncがプライマリ(172.16.99.1)をリッスンするように設定します。Synchronize Config to IPは必ず空欄のままにしておいてください。ここを空にしておくことで、ノード間での危険な設定同期ループを防止できます。
ステップ5:CLIからのクラスタ健全性確認
両方のアプライアンスにSSH接続し、インターフェースの状態およびステートテーブルの同期数を確認します。
# プライマリノード - CARPマスター状態の確認
root@opnsense-primary:~ # ifconfig vtnet1_vhid1
vtnet1_vhid1: flags=49<UP,LOOPBACK,RUNNING> metric 0 mtu 1500
inet 10.0.10.1 netmask 0xffffff00
carp: MASTER vhid 1 advbase 1 advskew 0
# プライマリノード - アクティブなステートテーブルサイズの確認
root@opnsense-primary:~ # pfctl -si | grep -i state
current entries 1428
searches 942810
inserts 120491
removals 119063
# セカンダリノード - バックアップ状態および同期されたステート数の確認
root@opnsense-secondary:~ # ifconfig vtnet1_vhid1
vtnet1_vhid1: flags=49<UP,LOOPBACK,RUNNING> metric 0 mtu 1500
inet 10.0.10.1 netmask 0xffffff00
carp: BACKUP vhid 1 advbase 1 advskew 100
root@opnsense-secondary:~ # pfctl -si | grep "current entries"
current entries 1428
ステップ6:実環境でのフェイルオーバーテスト
LAN上のワークステーション(10.0.10.50)から外部ターゲットに向けて、長時間のSSHセッションを維持しながら継続的なpingを実行します。
ping -c 30 1.1.1.1
pingの実行中に、プライマリファイアウォールで手動によるCARPデモーション(降格)をトリガーします。
# carp demotionカウンタを増やしてプライマリノードを降格
root@opnsense-primary:~ # sysctl net.inet.carp.demotion=240
クライアントワークステーション側でpingの出力を確認します。
64 bytes from 1.1.1.1: icmp_seq=12 ttl=57 time=14.2 ms
64 bytes from 1.1.1.1: icmp_seq=13 ttl=57 time=14.8 ms
# ここでフェイルオーバーが発生
64 bytes from 1.1.1.1: icmp_seq=14 ttl=57 time=31.6 ms
64 bytes from 1.1.1.1: icmp_seq=15 ttl=57 time=14.3 ms
フェイルオーバーは極めてシームレスに行われました。パケットロスはゼロで、バックアップノードのGratuitous ARPがスイッチのCAMテーブルを更新する間にわずか17ミリ秒のレイテンシスパイクが発生したのみでした。アクティブなSSHセッションも、TCPキーの再ネゴシエーションを要することなくレスポンスを維持しました。
プライマリを通常稼働状態に復帰させるには、デモーションカウンタをリセットします。
root@opnsense-primary:~ # sysctl net.inet.carp.demotion=-240
本番運用で注意すべき3つの落とし穴
このクラスタで450Mbpsの持続的なトラフィックを半年間運用する中で、特に注意すべき3つの重要な挙動が浮き彫りになりました。
- スイッチのポートセキュリティとMACスプーフィング:仮想環境(Proxmox VE、VMware ESXi、Hyper-Vなど)では、CARPパケットが破棄されるトラブルが頻発します。CARPは
00:00:5e:00:01:VHIDという形式の仮想MACアドレス(例: VHID 1の場合は00:00:5e:00:01:01)にバインドします。仮想スイッチやマネージドハードウェアスイッチで未登録のMACアドレスがブロックされていると、セカンダリノードが昇格してもトラフィックを一切受信できません。ポートグループでMACアドレススプーフィング(偽造)およびプロミスキャスモードを許可してください。 - アウトバウンドNATのゲートウェイバインディング:デフォルトでは、OPNsenseはアウトバウンドNATを物理インターフェースのIP(
198.51.100.2)にマッピングします。アウトバウンドNATの設定をHybrid(ハイブリッド)またはManual(手動)モードに変更し、変換ルールターゲットをWAN側のCARP VIP(198.51.100.1)に更新してください。この設定を怠ると、フェイルオーバー後に戻りパケットがダウンしたプライマリノードに送信されてしまいます。 - 暗号化されないSYNCリンク:
pfsyncプロトコルは、ステートパケットを認証なしの平文で回線上に送信します。このリンクを盗聴されると、アクティブな内部IPアドレス、送信先ポート、NATマッピングなどのメタデータが外部に露見する恐れがあります。このインターフェースは、必ず専用の物理パッチケーブルで直結するか、ルーティングされないプライベートなVLANに完全に隔離してください。
CARP、pfsync、XMLRPCを適切にチューニングすれば、OPNsenseはキャリアグレードの信頼性を発揮します。業務時間中であっても、ユーザーを誰一人切断させることなくファームウェアのパッチ適用を行えるようになります。

