VPNが落ちても何も漏れないようにLinuxの出口を固定する方法
ルーティングとフィルタリングは別々の判断です。出力フックを固定し、その後トンネルが再接続するために必要なエンドポイント、DNS、管理の例外を限定します。
トンネル経由でしかインターネットに出られないメディアボックスが自宅回線につながっている状況を考えてみましょう。トンネルが落ちたら、アプリケーションは物理インターフェースの上を黙って流れ続けるのではなく、停止すべきです。この振る舞いを得るのはルーティングではなくファイアウォールの問題であり、その区別が重要なのは、ルートはデーモンの再起動で置き換えられてもフィルタは置いた場所に残るからです。以下では、パケットの経路が実際にどう判断するのか、そしてトンネルを再接続させる例外をどう限定するのかを見ていきます。
ローカル出力は転送とは別のフック
マシン上のアプリケーションから出ていくパケットは出力フックを通り、マシンが別のホストのために経路制御するパケットは転送を通ります。forwardだけに触れたポリシーは後者を制限し、前者を大きく開いたままにするので、多くの出口の固定は正しく見えて何もしていません。インターフェースの照合も同じルールに入り、oifname "tun0" acceptはすでにトンネルから経路制御されたトラフィックを通します。ルーティングとフィルタリングも別々の判断のままで、フィルタがパケットを拒否していても上流へのルートは存在し得ます。あなたが頼っているのはこの分離です。
他の端末のために転送するゲートウェイには両方のフックが必要です。出力だけを固定するとマシン自体は守られますが、下流のクライアントはどれも漏らし放題になります。
例外リストはエンドポイントから外へ書く
トンネルのエンドポイントから始め、宛先アドレスを一つ、あるいは承認済みの小さな集合、トランスポートプロトコル、宛先ポート、たとえばudp dport 1194 ip daddr 203.0.113.7 acceptを決めます。クライアントがホスト名を解決するなら、初期のDNS交換にはリゾルバとポート53に限定した専用のルールが要り、そのリゾルバのアドレスへの包括的な許可にしてはいけません。どの例外にも、それが存在する理由を書くべきです。VPNポートへのすべてのUDPトラフィックを許すのは、想定していた一つの認証済みサービスよりはるかに広い範囲を覆います。
初期解決は、固定がたいてい腐る場所です。タイマーで更新するアドレス集合には、更新が失敗したときの挙動を定義する必要があり、その挙動が成功するまで回線を開くことではいけません。トンネルが上がっている間はトンネル経由で更新し、そうでなければ最後に確認できた正常なエンドポイントに戻します。
ローカル管理にも同じ扱いが要ります。管理用サブネットはアドレスが私有だからといって信頼できるわけではなく、それを信頼するルールは固定の周りに第二の経路を招き入れます。
インターフェースの照合は健全なトンネルの証明ではない
tun0から出るものをすべて受け入れるルールは、そのインターフェース上のパケットがトンネルから来たと仮定します。インターフェース名は作り直され、名前は暗号学的な身元ではありません。ピアが消えていてもインターフェースが上がったままのことがあり、その場合このルールは非常によくできた無意味な処理になります。
接続追跡は関連する罠を加えます。確立済み接続に対する広いルールはフローが以前に得た許可を再生するので、トンネルが落ちる前に始まったセッションはその後も動き続けます。状態に基づく受け入れを本当に意図したインターフェースとアドレスに限定しなければ、自分から開く固定を作ったことになります。入力と出力のルールも別の問いに答えるもので、片方だけを固めてもう片方を無視すると、考えていなかった方向にマシンが到達可能なままになります。
デーモンのクラッシュを生き延びるもの
トンネルが上がった後に走るスクリプトが入れたフィルタは、まさにそれが必要だった瞬間にトンネルと一緒に消えます。固定は起動時に読み込み、デーモンにはその例外だけを管理させてください。
デフォルトルートを消すことも代わりになりません。ルートはデーモンが所有して書き換える状態であり、消すと再接続に必要な経路まで失うことがあります。IPv6には、近隣探索とルーター広告というリンクのサービスを通常のトラフィックを止めている間も動かし続けるための独自の計画が要ります。DHCPも同じ分類です。これらは後から障害として発見するのではなく、目的のある例外として書き留めてください。
配備前の復旧経路
固定が失敗するその形、つまりトンネルなしでの起動、デーモンの停止、エンドポイントの変更、再接続でテストします。どの場合もアプリケーションが止まり、復旧経路が生きているべきです。自分のルールで閉め出される可能性が最も高いのはリモートシェルなので、これは必ずコンソールから行ってください。
次に、普通のアプリケーションがまだどこに届くかを、無害な宛先と検証できるリクエストで確かめます。望む結果は、上流側での拒否とトンネル経由の成功であり、どちらとも取れる曖昧なタイムアウトではありません。ルール集合はシェルスクリプトでテーブル全体を置き換えるのではなく、ファイアウォールの他の部分と同じ所有者の下に置いてください。すべての許可が自らを正当化できるポリシーは、誰も説明できない長いルール集合より価値があります。