アドレスによる ping はディストリビューション内で通りますが、パッケージのインストールや git の取得は通らず、障害はトンネルが上がった直後に現れました。止まったのは名前の解決だけで、これは接続性ではなく DNS を指します。ここで最もよくある誤った第一手は、パッケージの再インストールです。WSL2 にはまったく同じ症状を生む独立した経路が二つあり、それぞれ別の修復が必要です。

ディストリビューションが独自の resolver を持つ理由

ディストリビューション内では Windows の DNS クライアントは使われません。WSL2 は起動時に独自の resolver ファイルを作り、Windows がユーティリティ VM 用の仮想スイッチに公開する stub アドレスを指します。

この仕組みは一度書いて再利用するため、通常のネットワーク変更では安定しています。しかし、ユーティリティ VM が起動した後にトンネルクライアントがホストのアダプター一覧を書き換えると、この仕組みは正しくなくなります。

ディストリビューション内の何もその変化を監視しないため、ファイルはもう経路上にないかもしれないアドレスを指し続けます。次の照会は待たされ、タイムアウトし、あるいは内部ゾーンを知らないサーバーから答えを受け取ります。内側から見れば resolver が応答をやめるだけで、すべての名前が一斉に失敗します。

まず両側で証拠を集める

どちらも変える前に、ディストリビューションから見えるもの、次に Windows から見えるものを記録する。二つの見え方を合わせて初めて、障害の種類を区別できます。

ディストリビューション内で、resolver ファイル、ルーティングテーブル、内部と公開の照会を一つずつ出力します:

ip -4 route
cat /etc/resolv.conf
getent hosts intranet.corp.example
getent hosts example.com

Windows 側では、アダプターごとの DNS サーバーとメトリック付きの IPv4 テーブルを一覧します。VPN クライアントは通常 WSL2 サブネット向けのルートを入れ、そのメトリックが、その通信がトンネルから出るかローカルに残るかを決めます。

Get-NetAdapter | Get-DnsClientServerAddress
route print -4

ルートメトリックか古いファイルか

一つ目の障害は古いファイルです。resolver は起動時のアダプター状態に合わせて書かれ、トンネルがその状態を変えました。stub アドレスは今は届かないかもしれませんし、内部ゾーンを何も知らないサーバーを指しているかもしれません。

二つ目はルーティングの問題です。VPN が WSL2 サブネットを覆うルートを、ホストのリンクルートより低いメトリックで追加すると、ユーティリティ VM 宛てのパケットがトンネルに入ります。すると DNS だけでなく、すべての WSL2 サービスがタイムアウトします。

見分ける試験は一行で済みます。公開 resolver のような数値アドレスが名前の失敗中でも ping に応答するなら、経路は無事で問題は DNS です。これが resolver の変更が正当化される場合で、そうでない場合もはっきりします。数値の試験も失敗するなら、ユーティリティ VM はルートを失っており、どの resolver ファイルでも直せません。

ルーティングの場合、トンネルアダプターの自動メトリックを外し、WSL2 のリンクルートより大きい数値を与えます:

Get-NetAdapter | Where-Object InterfaceDescription -Match VPN | Set-NetIPInterface -InterfaceMetric 5500
wsl.exe --shutdown

シャットダウンが重要です。メトリックを変えても稼働中のユーティリティ VM は書き換わらないため、変更が見える前にディストリビューションを作り直す必要があります。

ファイルを意図的に引き取る

ルーティングが正しく、内部の名前だけが失敗するなら、原因は resolver で、生成側を止められます。これは保守コストを伴う意図的な変更であり、気軽に当てる回避策ではありません。

ディストリビューション内に、生成を無効にするネットワークセクション付きで /etc/wsl.conf を作成し、そのうえで resolver ファイルを自分で書き、社内サーバーを先に、公開の予備を次に置きます:

[network]
generateResolvConf = false
search corp.example
nameserver 10.88.4.53
nameserver 9.9.9.9

順序が重要です。公開 resolver は内部の名前に答えられず、予備はインターネット照会のためだけに存在します。ファイルが自分のものになれば何も再生成しないので、移転した社内 resolver は自分で気づくべき障害になります。

名前を確認し、次にパッケージを確認する

システムの resolver で内部の名前と公開の名前を一つずつ検証し、失敗した操作をやり直します。パッケージマネージャーは独自の resolver を使うことがあり、そのため動作する getent が壊れたインストールと共存することがあります。

書き換えを自動化するなら冪等にし、トンネルがないときは空のサーバー一覧を書くのではなく既存のファイルを残します。

getent hosts intranet.corp.example
resolvectl status | head -20
getent hosts example.com

ディストリビューション内から内部照会と公開照会の両方が通ることが、記録する価値のある結果です。ホスト側の緑の VPN アイコンはどちらも証明しません。

関連記事