トンネルは戻り、クライアントは接続済みと表示し、ブラウザは切断前に読み込んでいたページで回り続けている。VPN の再接続が復元するのは経路であり、アプリと相手側が古い経路の上で積み上げた接続状態は復元しない。直すべきものは別物で、これを混同すると理由もなく VPN の設定画面へ戻ることになる。

一つのテストではっきり分けられ、十秒で済む。古い要求が固まっている間に、同じ宛先へ新しい要求を送る。

再接続で残るものと残らないもの

トンネルは経路と鍵とアドレスの集合であり、再接続はその三つを復元できるが、確立済みのフローはすべて死んだままになる。OS はアプリが求めたからソケットを開いたままにし、そのソケットのタイムアウトは数分後や数時間後かもしれない。その下では相手がフローを忘れ、変換エントリが失効し、あるいは経路がハンドシェイクを見ていない端点を通るようになっている。再接続の手順はアプリのソケットをたどって直すことをしない。

再接続時に古い接続を落とすフックを加えるクライアントもあるが、ほとんどの場合はない。ここでの挙動はクライアント、プラットフォーム、バージョンによって変わる。

相手側もあなたを忘れている

遠端は自分の状態を持つ——TLS セッション、TCP 制御ブロック、そして場合によってはアドレスとポートをフローに結びつける変換エントリである。経路上の変換装置は、トンネルが素早く戻っても、その空白の間にその対応付けを失効させることがある。対応付けが消えると、サーバがもう認識しないフローのパケットが届き、応答はクライアントが決して表示しないリセットかもしれない。アプリは、決して答えないソケットをまだ待っているため、これをエラーではなく停止として報告する。

サーバ側のアイドルタイムアウトは独自の予定で同じことをするので、ネットワークの出来事が一切なくてもセッションは死ぬことがある。サーバの想定より長くアイドルだった接続は、トンネルが動いたと気づく前にすでに消えている。

新しい要求こそ、両者を分けるテストである

古い要求がまだ固まっている間に同じホストへ新しい要求を送り、両方を見る。新しい要求が成功すれば、トンネル、経路、名前解決はすべて動いており、不具合は古いフローに限られる。新しい要求も失敗すれば、経路の問題であり、アプリのセッション状態は誤った手がかりである。

テストの間は直接通信のブロックをそのままにしておく。偶然の迂回路で成功した要求は、トンネルについて何も証明しない。

古い接続ではなく、新しい接続で確認する

以下のコマンドは、まだ開いているソケットを示し、新しい要求を一つ開始し、セッション表にフローの項目がまだあるかを確認する。差が見えるように、一つ目は再接続の前と後に実行する。ホストとポートは自分のサービスに置き換えること。

ss -tanp | grep -E ':(443|8443)'
curl -sS -o /dev/null -w '%{http_code} %{time_connect}\n' --max-time 8 https://intranet.example.com/
conntrack -L 2>/dev/null | grep 8443
dig +short intranet.example.com

通信が動いていない確立済みのソケットが探すべき特徴であり、状態コードを返す新しい要求は経路が生きていることを示す。接続表はフローをまだ対応付けているものがあるかに答え、それがクライアントから見えない部分である。リゾルバへの問い合わせは、名前の失敗と止まった接続がブラウザでは同じに見えるため、ページ読み込みの残り半分を覆う。合わせて、この四つの出力が古いセッションと壊れたトンネルを分ける。

中断した時刻、再接続した時刻、古い要求と新しい要求の差を記録する。その記録が、曖昧な不満を誰かが動ける報告に変える。

関連記事