WSL 3.0.1 で WSLC がGA (一般提供) となりました。
「待ってました!」と、意気揚々と試してみたところ、謎のTLSハンドシェイクエラーにより、コンテナイメージのpullすらできずに撃沈しました。
このエラーは、Windowsホストはもちろん、WSLCではない通常のWSLディストリビューション環境では発生しません。WSLCだけ発生します。
本稿は、同じようなエラーに遭遇した人のための、トラブルシューティングの記録です。
TL;DR (解決)
詳細を全部省略して、解決策だけ先に書くと、WSLCのネットワークモードを nat に設定することで解決しました。
WSLCの設定は、%USERPROFILE%\AppData\Local\wslc\settings.yaml にyaml形式で定義できます。以下のように設定します。
session:
networkingMode: nat
設定内容を反映させるために、wslc system session terminate で一度WSLCセッションを終了させる必要があります。
調査記録
以下は、興味のある人向けの調査記録です。
TLSハンドシェイクの確認
Windowsホストからは、TLSハンドシェイクは成功します。
※レスポンスは401ですが、TLSハンドシェイクの確認が目的なので、期待通りの成功です。
PS C:\> curl -ISs https://registry-1.docker.io/v2/
HTTP/1.1 401 Unauthorized
Date: Wed, 30 Sep 2026 10:09:58 GMT
Content-Type: application/json
Content-Length: 87
Connection: keep-alive
docker-distribution-api-version: registry/2.0
www-authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io"
strict-transport-security: max-age=31536000
通常のWSLからも成功です。
PS C:\> wsl --system --exec curl -ISs https://registry-1.docker.io/v2/
HTTP/2 401
date: Wed, 30 Sep 2026 10:09:31 GMT
content-type: application/json
content-length: 87
docker-distribution-api-version: registry/2.0
www-authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io"
strict-transport-security: max-age=31536000
しかしながら、WSLCからは、以下のエラーとなります。
PS C:\> wslc system session run curl -ISs https://registry-1.docker.io/v2/
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the webpage mentioned above.
wslc system session run {シェルコマンド} で、WSLCのコンテナホスト環境で任意のシェルコマンドを実行できます。また、wslc system session shell で対話シェルに入ることもできます。デバッグに役立ちます。
上記エラーの内容としては、サーバー証明書のissuerの証明書、つまりCA証明書が無いので、サーバー証明書の検証に失敗しています。
Issuerを確認します。まずは正常なWSL側で見てみます。IssuerはAmazonであることが確認できます。
PS C:\> wsl --system --exec sh -c 'openssl s_client -connect registry-1.docker.io:443 -servername registry-1.docker.io -showcerts </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject'
issuer=C=US, O=Amazon, CN=Amazon RSA 2048 M01
subject=CN=*.docker.com
続いて、問題のWSLCです。
PS C:\> wslc system session run sh -c 'openssl s_client -connect registry-1.docker.io:443 -servername registry-1.docker.io -showcerts </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject'
issuer=OU=generated by Norton Antivirus for SSL/TLS scanning, O=Norton Web/Mail Shield, CN=Norton Web/Mail Shield Root
subject=CN=*.docker.com
ん? Norton? 貴様の仕業か!!!
これでTLSハンドシェイクエラーの直接的な問題原因は特定できました。ローカルPCにインストールされた、NortonのセキュリティソフトによるTLSインスペクションが原因でした。Nortonが、クライアント (WSLCホスト) とサーバー (コンテナレジストリ) の間に割って入り、WSLCホストに対してNorton自身のサーバー証明書を用いてTLSハンドシェイクをさようとしたが、WSLCホスト側にはNortonの証明書を検証するための設定がされていなかったということです。
なぜWSLでは成功するが、WSLCではエラーになるのか
ネットワークモードの違いが原因
今回頭を悩ませたのが、なぜWSLでは問題ないが、WSLCではエラーになるのか、という点です。
WSLCアーキテクチャを紹介するMicrosoft公式のブログ記事を確認したところ、WSLCでは consomme という新しいネットワークモードをデフォルトで採用していることがわかりました。一方で、従来のWSLのデフォルトネットワークモードは nat です。どうやら、WSLとWSLCのネットワークモードの違いが怪しそうです。
WSLと同じネットワークモードを使うとエラーは解消
WSLCのネットワークモードを、実績のあるWSLと同じ nat に変更して試してみます。WSLC設定ファイルに、以下の設定を行います。
session:
networkingMode: nat
設定を反映させるために、一度セッションを終了し再起動します。
PS C:\> wslc system session terminate
そして、コンテナイメージのpullを再トライしたところ、成功しました!
PS C:\> wslc image pull debian:latest # pull成功!
latest: Pulling from library/debian
6eefb2f5d3e9: Pull complete
Digest: sha256:9cc080028c43b27d2074d63a5f9caf7166d731494965616c1a6d2827a004585c
Status: Downloaded newer image for debian:latest
docker.io/library/debian:latest
コンテナ起動も正常です。
PS C:\> wslc run --rm debian apt-get moo
(__)
(oo)
/------\/
/ | ||
* /\---/\
~~ ~~
..."Have you mooed today?"...
今回は、NortonによるTLSインスペクション問題を、ネットワークモード設定を変更することで対応しましたが、NortonのルートCA証明書をWSLC側に登録することでも解決できるはずです。しかしながら、WSLCホストだけでなく、WSLCで起動する各コンテナ側にも設定が必要になり、それなりに構成が複雑になるでしょう。
では、consomme モードと nat モードはどう違うのでしょうか?
consomme モード
先述のブログ記事や、WSLドキュメントによれば、Linux (WSLCホスト) 側ではvirtio-netドライバを通じて通信が行われ、Windows側ではユーザープロセスにより通信が中継されます。Windows側では通常のWindowsソケットを用いた普通のアプリケーションと同じような通信として扱われるので、Windowsホスト環境のネットワークポリシーと統合してファイアウォールやVPN等との連携がよりシームレスに行える利点があります。一方で、通常のWindowsアプリケーションと同じように、ローカルPCのセキュリティソフトによる監視の対象にもなりやすくなります。今回、NortonによるTLSインスペクションにより、WSLCによるTLSハンドシェイクがエラーとなったのも、この consomme モードの特徴に起因します。
動作確認
まずは、Linux側のドライバを確認します。ドキュメント通り、virtio-netが使われていることが確認できます。
PS C:\> wslc system session run sh -c '
>> for adapter in /sys/class/net/*; do
>> if [ -e "$adapter/device/driver" ]; then
>> printf "%s: " "${adapter##*/}"
>> readlink -f "$adapter/device/driver"
>> fi
>> done
>> '
eth0: /sys/bus/virtio/drivers/virtio_net
loopback0: /sys/bus/virtio/drivers/virtio_net
次に、comsomme モードによる通信が、実際にWindowsホスト上でユーザープロセスによって中継されていることを確認します。
※以下では、裏でWSLCホストからコンテナレジストリへのTCP接続を試み、その接続を維持した状態で確認しています。
以下のとおり、Windows側では、WSL関連モジュールをロードする Windowsプロセス (dllhost) がTCP接続を所有していることが確認できます。Windowsソケットを通じて外部との通信が行われることになります。つまり通常のWindowsアプリの通信と同様に、ローカルPCのセキュリティソフトによる監視/検査の対象となりえます。
PS C:\> Get-NetTCPConnection | ?{ $_.RemoteAddress -eq '3.220.149.94'} | select State,OwningProcess
State OwningProcess
----- -------------
Established 22104
PS C:\> Get-Process -Id 22104 | select ProcessName
ProcessName
-----------
dllhost
PS C:\> Get-Process -Id 22104 -Module | ?{ $_.ModuleName -match 'wsl|consomme' }
Size(K) ModuleName FileName
------- ---------- --------
1780 wsldevicehost.dll C:\Program Files\WSL\wsldevicehost.dll
128 wsldevicehostproxystub.dll C:\Program Files\WSL\wsldevicehostproxystub.dll
nat モード
nat モードでは、consomme と異なりWindowsのユーザープロセスが外部とのTCP接続を作成したりせずに、Linux側からの通信はWindowsカーネルによってアドレス変換され転送されます。
動作確認
natモードに設定を変更し、同じように確認していきます。
natモードでのドライバは、Hyper-Vネットワークドライバーの hv_netvsc が使用されています。
PS C:\> wslc system session run sh -c '
>> for adapter in /sys/class/net/*; do
>> if [ -e "$adapter/device/driver" ]; then
>> printf "%s: " "${adapter##*/}"
>> readlink -f "$adapter/device/driver"
>> fi
>> done
>> '
eth0: /sys/bus/vmbus/drivers/hv_netvsc
次に肝心のWindows側にTCP接続を中継するプロセスがいるかどうかです。以下の通り、該当するWindowsプロセスはいません。
PS C:\> Get-NetTCPConnection | ?{ $_.RemoteAddress -eq '3.220.149.94'} | select State,OwningProcess
PS C:\>
consommeモードとは異なり、ユーザープロセスによるWindowsソケットを用いた通信は行われていません。
比較
| 確認項目 | consomme |
nat |
|---|---|---|
| デフォルト状況 | WSLCデフォルト | WSLデフォルト |
| SSLハンドシェイク | 証明書検証エラー | 成功 |
| 仮想NICドライバー | virtio_net |
hv_netvsc |
| Windowsユーザープロセスによる中継 | あり | なし |
| ローカルのNortonによるTLSインスペクション | された | されなかった |
さいごに
WSLでは問題ないが、WSLCでネットワーク関連のエラーが発生する場合、WSLCのネットワークモードを nat に設定することを一度試してみてください。nat は、WSLのデフォルトです。
ただし、WSLCデフォルトの consomme モードの利点が失われるので、適用にあたってはドキュメント等参考にしてください。
