社内 wiki は Edge で開くのに、ディストリビューション内の git clone はタイムアウトする。この一つの観察はたいてい「VPN が WSL2 をブロックしている」と読まれ、次の一時間は /etc/resolv.conf の編集に消える。より有用な読み方は、WSL2 にはそもそもホストのネットワーク経路がなかったというものだ。それは独自のアダプター、独自のルート、そして Windows が生成する resolver ファイルを持つユーティリティ VM で動いている。以降のすべては、この三つのどれが実際に欠けているかで決まる。

その VM は独自のスタック、メトリック、resolver を持つ

既定の NAT モードでは、ディストリビューションは仮想スイッチの背後にあり、ホスト経由でインターネットに到達するが、ホストのアダプターは見えない。ip addr はプライベートアドレスを持つインターフェースを一つ表示し、ip route はそのスイッチ経由の既定ルートを表示し、/etc/resolv.conf は Windows が選んだ stub resolver を指す生成ファイルである。Windows 側で VPN クライアントが起動しても、これらは何も変わらない。

だからこそ「ブラウザーは動く」はほとんど何も証明しない。ブラウザーはホストのスタック、トンネル、ホストの resolver ポリシーを使う。あなたのシェルは別のスタックを使い、たまたま物理 NIC を共有しているだけだ。両者は順調な場合には一致し、VPN がフィルターを入れ、DNS リストを書き換え、仮想スイッチのサブネットにルートを追加したときにだけ食い違う。

どちらの世界にいるかは二つのテストで決まり、それらはディストリビューション内で実行する必要がある。

getent hosts example.com
curl -sS -m 8 -o /dev/null -w "%{http_code}\n" 1.1.1.1
ip route
cat /etc/resolv.conf

リテラルアドレスが応答するのに名前が失敗するなら resolver の問題だ。名前とアドレスが同時に失敗するなら経路の問題で、resolver をどう編集しても届かない。次に Windows 側の証拠を見る。Get-NetAdapter | Get-DnsClientServerAddress と route print -4 は、トンネルが VM のいるサブネットを奪ったかどうかを示す。

一つ目の俗説:社内 DNS サーバーで直る

resolver ファイルの編集は古典的な助言で、およそ半分の確率で正しい。正しいときには具体的な理由がある。生成ファイルは公開名しか知らない resolver を指すため、経路が正常でも intranet.example.internal の問い合わせは存在しないものとして返る。

VPN のサーバーを固定するには、Windows にファイルを再生成させないようにする必要がある。手で書いたファイルは次回起動時に置き換えられるからだ。

[network]
generateResolvConf = false

それを /etc/wsl.conf として保存し、Windows から wsl --shutdown を実行し、それからディストリビューションを起動して /etc/resolv.conf を書く。トンネルのサーバーを先に、公開 resolver はあくまでフォールバックとして置く。これは保守コストを伴う恒久的な変更だ。ファイルはもうホストに追従しないので、社内 DNS アドレスが変わってもディストリビューションは古いアドレスを問い続ける。誰かがこのファイルを所有しなければならない。

障害がより広い場合にも何もしない。curl から 1.1.1.1 がタイムアウトするなら、トンネルクライアントが仮想スイッチをフィルタリングしているか、VM のサブネットをトンネルにルーティングしている。正しい resolver は、依然として到達できない名前を返すだけだ。

二つ目の俗説:メトリック変更は定着する

名前は解決するがトラフィックが物理アダプターから出ていく場合、てこになるのはインターフェースメトリックだ。VPN アダプターに高いコストを、WSL スイッチに低いコストを与える。

Get-NetAdapter | Where-Object InterfaceDescription -Match "AnyConnect" | Set-NetIPInterface -InterfaceMetric 4000
Get-NetIPInterface -InterfaceAlias "vEthernet (WSL)" | Set-NetIPInterface -InterfaceMetric 1

そのうえで、多くのクライアントビルドではトンネルアダプターが接続のたびに再作成され、メトリックが既定値に戻ることを覚えておく。一度きりのコマンドは静かに期限切れになる修正で、たいてい週末を挟み、チケットは「先週は動いていた」と戻ってくる。VPN イベントで起動するスケジュールタスクか、接続後にユーザーが実行するスクリプトが、この修理の正直な形だ。

同じ期限切れは、一つの頑固なプレフィックスのために追加したホストルートにも当てはまる。診断としては価値がある。問題が一つの宛先なのか経路全体なのかを教えてくれるからだ。しかしアーキテクチャではない。

三つ目の俗説:一つの修正がすべての Windows ビルドを覆う

ApproachWhat it actually does
Resolver pinFixes private names only; no effect on path failures
Sidecar distributionForwards traffic over a socket to a helper VM, so the switch's filter no longer matters
Mirrored networkingGives the distribution the host's interface set; also enables DNS tunnelling

wsl-vpnkit は sidecar パターンだ。小さなディストリビューションがホスト側の経路を所有し、仮想ソケット経由で他のディストリビューションのために転送するので、アプリのトラフィックはフィルタリングされたスイッチに依存しなくなる。標準経路が動く必要があり、つまり networkingMode と dnsTunneling の上書きを %UserProfile%\.wslconfig から削除し、各ディストリビューションで generateResolvConf = true を復元し、ログイン時にサービスを開始する。

ミラーモードは Windows 11 22H2 以降の組み込みの代替で、設定一つとシャットダウン一つで済む。

[wsl2]
networkingMode=mirrored

ディストリビューション内で ip addr はホストのアダプターを列挙し、トンネルがその中に現れることもあり、DNS トンネリングはクライアントが DNS リストを書き換えても Windows の resolver 経由で問い合わせを転送する。万能ではない。一部のフィルタードライバーは今もミラー経路を落とし、Hyper-V ファイアウォール規則に明示的な許可が必要なこともある。Microsoft の WSL ネットワークガイドは、より新しい版向けに書かれたレシピを適用する前に自分のビルドと突き合わせるべきページだ。

変更のたびに三つを試す

マシンごとに一つの仕組みを選んで書き留める。二つは相性が悪い。ミラーモードの上に sidecar を重ねると転送が二重になり、.wslconfig に古い sidecar エントリが残ったミラーホストは、アップグレードの前にではなく後に失敗しやすい。

どちらを選んでも、受け入れテストはディストリビューション内の同じ三つの要求だ。内部名、公開名、生のアドレス。次にトンネルを切断して再接続した後と、wsl --shutdown の後に繰り返す。内部名だけが失敗するなら resolver ポリシー、内部名と公開名が同時に失敗しアドレスが通るならこれも resolver ポリシー、すべて失敗するなら経路だ。

一つの内部ホストだけが失敗し他は動くなら、resolver を見るのをやめ、そのプレフィックスの欠けたルートを探す。次に再接続するまではすべて動くなら、実行時の状態に依存する修正を見つけたことになり、自動で再適用するか、上の二つの仕組みのどちらかに置き換える必要がある。

関連記事