VPNクライアントを再起動しても、トンネルが戻ったことしか証明できません。それを通して出ていくよう指示されたプロキシについては何も語りません。Squid、tinyproxy、その他のほとんどのフォワーダーは、起動時に解決したローカルの送信元アドレスを、何かに再読み込みを強いられるまで保持し続けます。再接続で新しいトンネルアドレスが割り当てられると、フォワーダーは送信接続のたびに失敗するか、通常のルートから出ていくようになります。

危険なのは二つ目の失敗で、トンネルは上がり、ページも読み込まれ、出口アドレスだけが間違っています。

トンネルではなくリスナーから始める

関与するアドレスは三つあり、再接続で動くのはその一部だけです。リスナーはブラウザーが向かう先で、通常は127.0.0.1かLANアドレスであり、トンネルが変わったことは気にしません。送信側のローカルソースは、フォワーダーが次のホップを呼び出すときにカーネルに使わせるもので、トンネルデバイスに結び付いています。公開側の出口はWebサーバーが記録するもので、数ホップ先の変換されたアドレスである場合もあります。

公開IPの検索は最後の問いにしか答えません。一つの公開出口が複数の内部ソースを隠しうるため、フォワーダーがどのローカルアドレスに束縛すべきかは分かりません。アドレスは接続を所有する側(クライアントの状態出力、インターフェース自体、またはトンネルが存在する名前空間)から読んでください。名前にトンネルらしいものが付いた最初のインターフェースを選ぶと、二つ目のプロファイルがある時点で間違った答えになります。

コンテナや名前空間の構成は、この前提を静かに壊します。あるネットワーク名前空間のフォワーダーは、別の名前空間にあるトンネルに束縛できず、設定をいくら編集しても直りません。

何かを編集する前の四つの確認

順番に進めてください。最初に失敗した確認のところで止まりますが、それは後の確認が前の確認の成立を前提にしているからです。

  1. 使用中のトンネルがプロキシの前提としたプロファイルであり、勝手に接続した二つ目のプロファイルでないことを確認する。
  2. そのデバイスに現在付いているアドレスを出力し、実行中の設定にあるアドレスと比較する。
  3. ディスク上のファイルの主張ではなく、フォワーダーが実際に解決したソースを尋ねる。
  4. フォワーダーからのトラフィックが、想定どおりのルーティングとファイアウォールの方針に従っていることを確認する。

デバイスのアドレスは簡単で、ip -4 addr show dev tun0 が現在のアドレスを出力し、ip -4 route get 1.1.1.1 がその宛先に対してカーネルがどのインターフェースを選ぶかを示します。正確なアドレスに束縛するフォワーダーは、アドレスが消えると大きな音を立てて失敗します。デバイスに束縛する、あるいは既定のソースに戻るフォワーダーは動き続けて漏れ、これが静かな結果です。

ドキュメントは検索結果の先頭ではなく、実行しているリリースに合わせてください。Squid の送信アドレス指令は v7 以前について文書化されており、v8 のドキュメントはその指令がそこでは利用できないと明言しているため、それ前提のスクリプトはその境界を越えられません(Squid tcp_outgoing_address のリファレンスを参照)。何かを再読み込みする前に、製品自身のパーサーで検証してください(squid -k parse はエラーを報告し、実行中のプロセスに触れずに終了します)。

トンネルに番号を振り、古い入れ替えを捨てる

再接続は引き継ぎであり、引き継ぎには順序が必要です。観測した各接続に、確立が成功するたびに増えるカウンターかタイムスタンプのタグを付け、そのタグを候補設定の中に持ち込みます。候補のタグが現在のものより古ければ、適用せずに候補を捨ててください(前のトンネル向けに書かれた遅い更新が、現在の設定を上書きしてしまうからです)。イベントの急増はまとめ、揺れるリンクが十五回ではなく一回の遷移を生むようにします。

作業は四つの動きに分けます。古い出口状態を無効にし、接続の所有者が完了を報告するのを待ち、管理対象の断片だけを組み立て、最後にサービスマネージャー経由で検証して有効化します。二つのアドレス値からファイル全体を再生成すると、リスナーの制限、認証、宛先ポリシーが静かに消えます。意図的な停止も尊重してください(利用者が切ったトンネルに呼び続けるヘルパーはバグであり、回復力ではありません)。

開いているソケットは新しいアドレスに従わない

フォワーダーを再設定しても、すでに確立した接続については何も言えません。排出されるか死ぬかは製品次第なので、仮定せずに挙動を観察してください。

入れ替えの前に開いたソケットで処理される要求は、新しい設定が正しくても古い経路から出ていきます。それが問題になるなら、短い排出期間を設けて新しい作業の受け入れを止めるか、古いソケットを終了してエラーを受け入れます。そのうえで新しい接続でテストし、その結果は排出の確認とは別に保ちます。ソースの束縛もセキュリティ境界ではありません。トラフィックが通常のルートに決して触れてはならない場合は、マークとファイアウォール規則で強制し、DNSとIPv6も含めて、再読み込みに失敗したときはプロキシが開いたままではなく明示的に停止したままになるようにしてください。

新しい出口を新しい要求で証明する

重要なのは有効化後に開いた要求であり、pingでもキャッシュ済みのページでもありません。プロキシに実際のURLを要求し、遠端が報告する出口アドレスを読み、プロバイダーが表示する場合はクライアント自身のトンネルの見方と比較します。

解析、再読み込み、要求を別々のコマンドに保ち、失敗が一つの手順を指すようにします。最初の二つは失敗しても何も変えないからこそ、再起動の前に実行するのです。

squid -k parse
sudo squid -k reconfigure
curl -x http://127.0.0.1:3128 -sS -o /dev/null -w '%{http_code} %{remote_ip}\n' https://example.com

再読み込みが失敗したらサービスを停止し、断片を直してください(古い出口のまま提供するプロキシは、短い停止より悪いからです)。再読み込みが成功しても新しい要求が予期しない場所から出るなら、アドレスを再び編集するのではなくルーティングの確認に戻ってください。検証したトンネルの世代を記録し、次の再接続で具体的に比較できるようにします。

関連記事