キルスイッチは人が予想しない方向で失敗する——トンネルが落ちている間にトラフィックを遮断するように作られているが、トンネルを作るパケット自体がトラフィックなのだ。これが不具合のすべてだ。トンネルから出ないものすべてを落とすルールセットは、トンネルを立ち上げるはずのハンドシェイクも落とすので、クライアントはループで再試行し、エラーは最初に確認した段階のせいになる。ファイアウォールを広げる前に、三つの段階のどれが拒否されているのかを突き止めよう——名前解決か、ピアへの外側パケットか、戻ってくる応答か。

トンネルとその輸送が互いに依存する仕組み

VPN クライアントは、一つのソケット経路を共有する二つのネットワーク状態として読む。トンネルができる前、クライアントは物理アダプターでソケットを開き、暗号化したフレームをピアのアドレスとポートへ送る。その交換が成功して初めて、トンネルデバイスを作りルートを設定する。キルスイッチは二つ目の状態についてのポリシーだが、両方に当たるほど広く書かれている。

インターフェース基準のルールが通常の形だ:次を通って出るものを受け入れ、tun0またはwg0、残りの出力経路を落とす。この書き方ではクライアント自身のサーバー接続を除外できないので、正しい実装はその例外を明示的に足すし、手でルールを書くときも同じことをする。Windows と macOS のアプリ単位のルールは、インターフェースではなくプロセスで照合して一部を避けるが、プロセスはやはりトンネルができる前に外へ出なければならない。

三つの段階を、起きる順に

第一段階は解決だ。プロファイルが文字通りのアドレスではなくホスト名を保存している場合、何かを送る前に DNS の答えが要る。DNS をトンネルのリゾルバーに向けるプロファイルは行き止まりを作る——リゾルバーは、その問い合わせが有効にするはずのトンネルを通してしか届かない。トンネルの外で解決するか、エンドポイントをアドレスに固定すれば、この曖昧さは消える。

第二段階は外側パケットで、この順にまずルーティング、次にファイアウォールを越えなければならない。wireguard-tools は WireGuard のためにこのルーティング側の半分を担う——自分のパケットに印を付け、たった今入れたデフォルトルートを飛ばすポリシールールを足すのだが、wg-quick のマニュアルページがそのルールと使うテーブルを説明している。そのルーティングを手で組み直すか、別のツールにルールを書き換えさせると、印を付けたパケットが、作るはずのトンネルへ送り返されうる。

第三段階は戻り経路だ。応答のないハンドシェイクは外向きの破棄の証拠ではない——サーバーの受信ルールや、対応付けを忘れた NAT 機器とも同じくらい整合する。クライアントに触る前に切り分けよう。

What you seeStageWhere to look
The client log repeats a resolve step and no packets appear on the wire1, name lookupResolver path while the tunnel is down
The send counter on the physical adapter stays flat2, routingPeer route and policy rules
The send counter rises and a firewall drop counter rises2, firewallRule order, mark, and interface match
Packets leave and nothing comes back3, return pathServer input rules, NAT, listening port

クライアントは検知した症状を記録するのであって、失敗した段階を記録するのではない——だから表の方がログより速いことが多い。

カウンターはログより速く決める

Linux では、nft list rulesetまたはiptables -vnL OUTPUTがルールごとにパケットカウンターを表示する。一度接続し、失敗させてから、もう一度カウンターを読む。カウンターが動いたルールが、ハンドシェイクの当たったルールだ——どこも動いていなければ、パケットはファイアウォールに届いていない。ルールセットが自分のハンドシェイクを落とすのを見たことがあるが、それを語ったのはカウンターだけだった。

ファイアウォールに届かなかったのか、ホストから出なかったのかを分けるには、インターフェースそのものを見る。ip -s link show dev wlan0は送信パケットを報告し、tcpdump -ni wlan0 udp port 51820はフレームを表示する。クライアントが再試行している間に平らなままなら、障害は netfilter より上にある——ピアのルート欠落、まだ存在しないデバイスを指すポリシールール、あるいはクライアントが飲み込んでいるソケットエラーのどれかだ。

例外は、203.0.113.10のような文書用アドレスで試す——うっかり他人のホストを探査しないためだ。

古いエンドポイント例外はサーバー障害のように読める

最も紛らわしいのは、ピアのアドレスが変わった後だ——動的アドレスのサービスが移したか、ホストが作り直されたか、あなたのネットワークの出口アドレスが違う。例外は今も古いアドレスを名指ししていて、新しい方へのパケットが破棄ルールに当たる。同じ設定が昨日は動いていたからこそ、人はサーバーの調査から始める。

ファイアウォールルールの中のホスト名は助けにならない。nftables ではルールセットを読み込む時点で名前が解決されるので、その瞬間にあったアドレスを固定し、文字通りのアドレスと同じように古くなる。耐久性のある直し方は、エンドポイントが変わったら例外を更新し、狭く保つことだ——ピアのアドレス一つ、輸送ポート一つ。アドレス変更に耐えるほど広い範囲は、無関係なトラフィックを通すのにも十分広い。

キルスイッチがサーバーホストで動く場合

同じ依存は反対側にもあり、そちらの方が見落としやすい。VPN サーバーを務めるマシンが、キルスイッチ付きのデスクトップクライアントや、ノート PC からコピーしたファイアウォールプロファイルも動かしていると、その破棄ルールはトンネルを終端するサービスに適用される。クライアントのハンドシェイクへの応答が落とされ、サーバーは健全に見え、トンネルインターフェースは使われないまま、どのクライアントも接続できない。

そのホストには、破棄の前に三つの狭い許可が要る——物理インターフェースの待ち受けポート、復号後のトラフィック用のトンネルインターフェース、そしてトンネルからクライアントの届くべき先への転送だ。インストール済みのセットは、nft list ruleset、iptables -S、またはpfctl -srで読み、何かを変える前に、保存されたルールセットは再起動を越えて残ることを忘れない——そこの間違いは、それを作ったプロセスより長く生きる。

マシンを閉じたまま保つ復旧順序

プロファイルから外へ向かって作業する。クライアントが使うピアのアドレスとポートを読み、トンネルなしでホスト名が解決することを確かめ、そのアドレスとポートの例外を物理アダプターにだけ一つ足す。再接続して送信カウンターを見る——ハンドシェイクが完了すれば、第二段階が直った合図だ。

テスト中は破棄ポリシーの残りをそのままにして置く。無害な確認は、無関係な文書用アドレスへの接続を試し、今も時間切れになることを確かめることだ——キルスイッチが切られているのではなく働いていると分かる。トンネルが安定したら例外を絞り、便利だからといってクライアントのバイナリに恒久的な全許可を残さない。

トンネルが上がってもアプリが失敗し続けるなら、残りの段階はこれではない——分割 DNS、MTU、アプリ単位のルーティングは、どれも接続済みのトンネルと壊れたトラフィックを生み、最初に確認すべき点はそれぞれ違う。

関連記事