ファイアウォールのファイルを開く前に許可を決めます。認証済みのクライアントは特定の 一つのサブネットへの接続だけを開け、ゲートウェイは他のあらゆる通過経路を拒否しなければなりません。クライアントが余分なルートを入れても同じです。この一文が設計であり、以下のすべてのルールはそれを強制するためにあります。より広いもの、たとえばプライベートアドレス空間の全体、トンネル経由のインターネット出口、ピア間の通信は別の設計であり、独自の判断が必要です。ルールはわざと退屈にしています。賢いファイアウォールとは、後から誰も監査できないものだからです。

まずトポロジーと許可を書き留める

例では Community 版の OpenVPN サーバーを使い、ユーザー空間の TUN デバイスを tun0、クライアントプールを 10.77.0.0/24、接続されたサブネットを 一つ 192.168.60.0/24 として lan0 上に置きます。IPv4 の転送はすでに動作しており、LAN にはプールへの戻り経路もすでにあるので、ここでは両者の間の変換に何も依存しません。

このホストのファイアウォールは UFW が所有しており、コンテナのルールセットや別の nftables テーブルがより早い許可経路を入れてはいけません。フィルタを飛ばすフローオフロードの近道があるとガードチェーンは飾りになるので、どれも有効でないことを確認します。OpenVPN 自身の client-to-client 転送は無効のままにします。オペレーティングシステムのフィルタを決して通らないピア間の通信 はそれでは制限できないからです。外部リスナーと管理用のルールはそのままにします。この変更はファイアウォールを置き換えるのではなく、復号されたトンネル通信を扱うものだからです。

ガードを UFW の早期許可より前に置く

ルート許可の後に拒否を置くだけでは厳密な境界になりません。標準の before ルールは、どのユーザールールを見るよりも先に確立済みの通信と一部の ICMP を許可するため、先週許可されたフローは状態が残っているというだけで新しい宛先制限の外に居続けられます。UFW のファイルをバックアップし、編集前に帯域外のコンソールを開いておきます。

ファイル *filter の /etc/ufw/before.rules セクションで、既存の宣言の隣、かつどのルール行よりも前に 二つのチェーンを宣言します。

:vpn-src-guard - [0:0]
:vpn-reply-guard - [0:0]

宣言の直後、入力と転送の許可より前に、その中へジャンプして何を通すかを定義します。

-A ufw-before-input -i tun0 -j DROP
-A ufw-before-forward -i tun0 -j vpn-src-guard
-A ufw-before-forward -o tun0 -j vpn-reply-guard

-A vpn-src-guard -o lan0 -d 192.168.60.0/24 -s 10.77.0.0/24 -m conntrack --ctdir ORIGINAL -j RETURN
-A vpn-src-guard -j DROP

-A vpn-reply-guard -i lan0 -d 10.77.0.0/24 -s 192.168.60.0/24 -m conntrack --ctstate RELATED,ESTABLISHED --ctdir REPLY -j RETURN
-A vpn-reply-guard -j DROP

セクションの残りと COMMIT 行はそのまま残します。二つ目のフィルタテーブルを貼り付けたり無関係なルールを消したりすると、ファイアウォールの変更が障害になります。ファイルは再起動 一 回で、重要になる唯一のコピーです。

各ガードが何を通し何を拒否するかを読む

最初のチェーンが処理を続けるのは、送信元がプール内にあり、宛先がその 一つのサブネットで、出口インターフェースが LAN で、パケットがそのフローの元の向きに進むときだけです。トンネルから届くそれ以外はすべて破棄されます。プール外の送信元や、許可されたサブネットの先にある宛先も含みます。

ピア間のパケットは tun0 に入って出ていくので、宛先の検査に届く前に LAN インターフェースの試験で落ちます。LAN からクライアントへの新しい接続も応答方向の試験で落ち、既定で逆方向のコールバック経路が閉じます。

RETURN ターゲットは通常の UFW ポリシーを生かしたままにします。これらのチェーンはフィルタであり、次に加えるルート許可が、生き残ったパケットを受け入れるかをなお決めます。この分離があるからこそ、後で許可するサブネットを 一 か所で変えられます。ガードがパケットをそのまま受け入れていたら、今後のルールはすべて 二回追加しなければなりません。

サブネットを許可し、脇の扉を閉じる

まずルート許可を予行し、それからプレビューのフラグを外して適用します: sudo ufw --dry-run route allow in on tun0 out on lan0 from 10.77.0.0/24 to 192.168.60.0/24。その後でユーザールールの順序を見直します。先の拒否は後の許可より依然として優先されるからです。サブネット全体の許可も、一台のホストの HTTPS ポート だけを到達させたいときには形が違います。

入力の破棄が何をするかに注意します。トンネルのクライアントはゲートウェイ自身のどのサービスにも届きません。LAN アドレスに束縛した DNS リスナーも含みます。そこに DNS を置くには、破棄の前に意図的な例外と、見直した許可の契約が必要です。UFW が IPv6 を管理しているなら、tun0 に対する同様の破棄を before6.rules にも加えます。IPv4 の宛先リストは IPv6 のパケットについて何も語らないからです。

予行ではなく読み込まれたポリシーを確認する

再読み込みを予行し、保守時間帯に適用し、実際のルールセットを読みます。

sudo ufw --dry-run reload
sudo ufw show raw

トンネルのガードが既定の状態許可と ICMP 許可より前に現れることを確認します。素の ufw status は手で編集した before ルールを表示しないので、実際には読み込まれているのにルールが欠けていると運用者は結論づけます。断片を探すのではなく、生の一覧を上から下へ読みます。ガードが状態許可の後に現れるなら、境界はファイルが示すとおりに動いていません。

次に文言ではなく挙動を試します。許可されたホストへ新しく接続して応答を見ます。サブネット外の宛先へ無害な要求を送って、届かないことを確かめます。ピア間の接続を試し、ゲートウェイ内のサービスを試し、要求していない LAN からクライアントへの接続を試します。変更前に存在した範囲外のフローを 一つ含め、マシンの追跡テーブル全体を消すのではなく、そのフローだけを消します。

限界もルールと一緒に記録します。これが制限するのは通過であり、それ以外ではありません。クライアントが自分のインターネット接続を使うことは止められず、許可された LAN のホストが通信を先へ中継することも止められず、許可されたサービス内の認証を置き換えるものでもありません。

関連記事