🐙

「DNS issues in WSL2」の回避 — WSL起動時に/etc/resolv.confを書き換える

2023/11/04に公開

はじめに

追記(2024年07月16日)対策は不要に!?

この追記を執筆時点(2024-07-16)での WSL2 の最新リリースバージョンは、2.2.4.0 です。このバージョンでは、dnsTunneling がデフォルトで有効になるようで、DNSが索けなくなる現象は起きなくなったようです。少くとも、筆者の環境では起きなくなりました。
ここでは、以前の記事にしたがった設定の無効にし、WSLを更新する方法を示しておきます。なお、設置したファイルなどは残ります。それも不要な場合は、個別に削除してください。

  1. WSL:UBuntu側の設定無効化
sudo chmod -x /usr/local/bin/set-resolv-conf
sudo systemctl disable set-resolv-conf
  1. Windows側からWSLの更新(管理者権限でターミナルを起動)
wsl --shutdown
wsl --update

その後 Windows 11 の再起動

"DNS issues in WSL2"

問題となる現象としては、WSL2でUbuntuを利用しているとき[1]、インターネットアクセスができなくなるというものです。WSL2起動時に自動生成された/etc/resolv.confで指定されているWSL2の仮想ネットワーク上のネームサーバーの不調が原因のようです。

"DNS issues in WSL2"を見ると、1年以上前から報告されている問題で、2023-11-04の時点でイシューが閉じられていないので、解決にはいたってないようです。

回避する方法

概要

基本的には、/etc/resolv.confnameserver のIPアドレスを

  1. アクセス可能な
  2. 正しく動作しているDNSの
  3. IPv4のアドレス

に修正することで、この問題を回避します[2]

resolv.conf で nameserver を指定する IP アドレス

候補としては、

  1. Googleなどの公開DNSサーバーのアドレス:例: 8.8.8.8
  2. ホストのWindows 11が利用しているDNSサーバーのアドレス:例: 192.168.1.1

があります。ただ、筆者がPCを接続するLANでは外部の公開DNSサーバーへクエリが許可されていないこともあって、この記事では 2. の「ホストが利用しているDNSサーバーのアドレス」を指定することにします。

具体的な作業手順

  1. WSLのアップデート(管理者権限で起動したWindows PowerShellで行う)

    wsl --shutdown
    wsl --update
    
  2. Windowsが使うDNSサーバーのアドレスを確認(Windows PowerShellで行う)

    Get-DnsClientServerAddress
    

    以下のような出力になります。

    InterfaceAlias         Interface Address ServerAddresses
                           Index     Family                 
    --------------         --------- ------- ---------------
    ローカル エリア接続* 1        13 IPv4    {}                                                    
    ローカル エリア接続* 1        13 IPv6    {fec0:0:0:ffff::1, fec0:0:0:ffff::2, fec0:0:0:ffff::3}
    ローカル エリア接続* 2        16 IPv4    {}                                                    
    ローカル エリア接続* 2        16 IPv6    {fec0:0:0:ffff::1, fec0:0:0:ffff::2, fec0:0:0:ffff::3}
    Wi-Fi                          6 IPv4    {192.168.3.1}                                         
    Wi-Fi                          6 IPv6    {2400:2411:8f63:a500:1111:1111:1111:1111}             
    Bluetooth ネットワーク接続    12 IPv4    {}                                                    
    Bluetooth ネットワーク接続    12 IPv6    {fec0:0:0:ffff::1, fec0:0:0:ffff::2, fec0:0:0:ffff::3}
    Loopback Pseudo-Interface 1    1 IPv4    {}                                                    
    Loopback Pseudo-Interface 1    1 IPv6    {fec0:0:0:ffff::1, fec0:0:0:ffff::2, fec0:0:0:ffff::3}
    vEthernet (WSL)               35 IPv4    {}                                                    
    vEthernet (WSL)               35 IPv6    {fec0:0:0:ffff::1, fec0:0:0:ffff::2, fec0:0:0:ffff::3}
    

    この場合は、192.168.3.1 が Hostが使用しているDNSのIPアドレスです。

  3. wsl.conf を設置(以降は、WSL:Ubuntuのbashで行う)

    [boot]
    systemd=true
    
  4. 一時的に resolv.conf を設定
    2.で確認したHostが使用するDNSのアドレスをresolv.confに設定します。

    echo "nameserver 192.168.3.1" | sudo tee /etc/resolv.conf
    
  5. Ubuntuにnkfをインストール

    sudo apt update; sudo apt upgrade -y; sudo apt install nkf -y
    
  6. WSL起動時に実行するスクリプトの設置

    #!/bin/bash
    ifname=`/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0//powershell.exe -Command "Get-NetAdapter | Where-Object Status -eq Up | Select-Object -ExpandProperty Name" | nkf -w -Lu`
    /mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0//powershell.exe -Command "Get-DnsClientServerAddress -InterfaceAlias '${ifname}' -AddressFamily IPv4 | Select-Object -ExpandProperty ServerAddresses" | nkf -w -Lu | sed -e 's/^/nameserver /' > /etc/resolv.conf
    
    

    上のスクリプトを保存したset-resolv-confというファイルを、/usr/local/bin/にコピーし、実行可能にしておく。

    sudo cp set-resolv-conf /usr/local/bin/
    sudo chmod +x /usr/local/bin/set-resolv-conf
    
  7. スクリプトの動作確認

    sudo /usr/local/bin/set-resolv-conf
    

    これがエラーなく実行されたら、/etc/resolv.conf を確認します。

    cat /etc/resolv.conf
    

    の結果が、nameserver 192.168.3.1 となって、2.で確認したホストが使用するDNSサーバーのIPv4アドレスと一致していれば OKです。

  8. 6.のスクリプトを起動するためのsystemd用サービス記述ファイルの設置

    [Unit]
     Description=set resolv.conf
     After=network.target
    
     [Service]
     ExecStart=/usr/local/bin/set-resolv-conf
    
     [Install]
     WantedBy=multi-user.target
    
    

    上の内容を保存した set-resolv-conf.service というファイルを /etc/systemd/system/ にコピーします。

    sudo cp set-resolv-conf.service /etc/systemd/system/
    
  9. 8.のサービス記述ファイルの有効化

    sudo systemctl enable set-resolv-conf
    
脚注
  1. 筆者は、Ubuntu以外のディストリビューションで発現するかどうか確認していません。 ↩︎

  2. 筆者はIPv6についてはなにも確認していません。 ↩︎

Discussion