VPN ファイアウォールのフックが同じルールを二重に追加する理由
再接続でフックが再び実行されることがあるため、望ましい状態を基準に設計します。専用チェーン、挿入前のテスト、直列化された変更、限定的な撤去です。
フックは最初は機能し、三回の再接続の後にはチェーンに同じルールのコピーが三つ残ります。何も大声で失敗しませんでした。スクリプトは実行のたびに、言われたとおりのことをしただけです。VPN フックは複数回実行されうる構成操作なので、所有者、望ましい状態、直列化された変更が必要です。この順に進めれば重複は止まります。
ルールを書く前に所有者を決める
一つのルールも書く前に、この統合がどのチェーンまたはテーブルを所有するかを決めます。フックが作成して空にする専用チェーンが最も明確な所有形態です。後片付けのときに、他の誰かのポリシーに触れずにそのチェーンを削除できるからです。共有チェーンにルールを追加しなければならない場合は、コメントで印を付けて、後から検索したときに自分のものだけが見つかるようにします。所有が撤去を安全にし、撤去こそが静かに失敗する部分です。
iptables -N VPN_HOOK 2>/dev/null || true
iptables -C INPUT -j VPN_HOOK 2>/dev/null || iptables -I INPUT -j VPN_HOOK
iptables -S VPN_HOOK
ここでのコメントは飾りではありません。自分のルールを削除することと、たまたま似て見えるルールを削除することの違いであり、保存されたルールセットにも残ります。コメントは変数に一度だけ書き、追加・テスト・削除を行うすべてのコマンドで同じ文字列を使います。
再追加ではなく望ましい状態をテストする
トンネルが上がっている間に存在すべきものを書き出します。どのインターフェース、どの送信元と宛先、どのプロトコルとどのポートかです。次に各ルールをテストとそれに続く挿入として表現し、二度目の実行でルールが見つかり何も起きないようにします。ルールが存在するときチェックコマンドはゼロで終了し、それがまさにスクリプトに必要な信号です。iptables のリファレンスに、ここで使うチェック・追加・挿入の形式が記載されています。
iptables -C VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT ||
iptables -A VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT
位置に注意してください。ルールをチェックして先頭に挿入すると、以前は先に来ていた意図的な拒否より前に許可を移動させることがあり、二度目の実行は何も追加しないのに挙動を変えてしまいます。自分のチェーン内では末尾に追加し、そのチェーンへのジャンプの位置で順序を制御します。チェーン内の順序が重要な場合は、ルールごとに継ぎ足すのではなく、既知のリストからチェーンを再構築します。
必要なルールを安定したテストとして表現できないなら、そこで止まります。それは通常、望ましい状態がまだ曖昧だという意味です。
維持を願うのではなく変更を直列化する
フックの呼び出しは二つ重なることがあり、どちらも相手が追加する前にルールの不在を観測できます。xtables の待機オプションはコマンドをロック待ちにしますが、チェックしてから追加するという一連の流れを二つのコマンドにまたがって原子的にはしません。操作全体を独自のロックで直列化するか、宣言したバッチを一つのトランザクションとして適用する管理ツールを使います。nftables は後者ができ、利用可能なホストで優先する理由の一つです。
flock /run/vpn-hook.lock /usr/local/sbin/vpn-firewall-apply
nft -f /etc/nftables/vpn-hook.nft
どの仕組みを選んでも、クリティカルセクションは短く保ち、再入可能にします。途中で失敗したフックは再度実行しても安全であるべきです。結果を記録して、繰り返し実行されたことがルール数から推測されるのではなくジャーナルで見えるようにします。異なる二つのスーパーバイザーがフックを呼べるならまずそれを直し、ロックは二人の所有者を修復しません。
自分の所有物だけを撤去する
撤去は、置き換えられた接続と置き換え前の接続を区別しなければならず、丁寧な後片付けが決して得られないイベントにも耐えなければなりません。追加したときと同じ識別子で削除し、後片付けを簡単にするために共有チェーンを空にすることは決してしません。強制停止の後は、ダウンフックが実行されたと仮定せず、次の起動時に対帳処理を実行します。
iptables -D VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT
iptables -F VPN_HOOK
ip -brief link show type tun
インターフェースの寿命も設計の一部です。再接続で別の名前のデバイスが作られることがあるため、二番目のトンネル名を固定したルールは、有効に見えたまま古くなります。接続自体が示す識別情報を優先し、実行のたびに読み直します。三つのイベントを別々にテストします。繰り返し接続、置き換え接続、シャットダウンです。許可された通信は三つすべてを重複なく生き延び、無関係なサービスは影響を受けないべきです。
フックに所有者があり、追加する各ルールにテストがあり、直列化された経路が一つあり、自分自身の状態だけを削除する撤去があるとき、この道のりは終わります。二度実行しても安全なフックは、一度だけ賢いフックより価値があります。