Skip to content

Instantly share code, notes, and snippets.

@ThePlenkov
Last active November 13, 2024 23:49
Show Gist options
  • Save ThePlenkov/6ecf2a43e2b3898e8cd4986d277b5ecf to your computer and use it in GitHub Desktop.
Save ThePlenkov/6ecf2a43e2b3898e8cd4986d277b5ecf to your computer and use it in GitHub Desktop.
Resolve WSL DNS automatically
#!/bin/bash
# Remove existing "nameserver" lines from /etc/resolv.conf
sed -i '/nameserver/d' /etc/resolv.conf
# Run the PowerShell command to generate "nameserver" lines and append to /etc/resolv.conf
# we use full path here to support boot command with root user
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command '(Get-DnsClientServerAddress -AddressFamily IPv4).ServerAddresses | ForEach-Object { "nameserver $_" }' | tr -d '\r'| tee -a /etc/resolv.conf > /dev/null

How to resolve DNS issue in WSL once and forever

There are tons of gists available for this problem:

However even solution is there - I was not satisfied that this problem always persists and I had to do same steps again from time to time. Also using a static DNS like 8.8.8.8 was not the option for me because we use pool of enterprise DNS servers and those are controlled by our IT on our Windows machines. So I wanted to find the way to get those DNS servers dynamically from Windows.

This guide describes the permanent solution using brand-new WSL boot command.

This gist removes nameservers every time your WSL starts and adds newly from powershell results. If it doesn't work for you - please feel free to adjust the script according to your needs.

Prepare boot config

  • login to your wsl and run sudo bash. It's important that you do this with a root user because it's a user which executes this command during boot
  • change to ~ folder ( or use any folder you like )
  • create a new file for example boot.sh and update it with a content from boot.sh gist
  • make it executable with chmod +x boot.sh
  • create a symbolic link in one of PATH folders ( keep in mind, that for root user number of path folders is very limited by default )
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
  • i have used ln -s ~/boot.sh /usr/local/bin/boot.sh
  • having such a limited list of folders makes root not aware of powershell.exe and rest of window utilities. I have preferred to use an absolute path like /mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe in my script - but you may think of creating a symlink for powershell too.
  • now let's add boot command to /etc/wsl.conf file. In my case it looks like this:
[network]
generateResolvConf=false
[boot]
systemd=true
command=boot.sh
  • now just exit from bash, from wsl as well. Terminate your wsl with a command like wsl -t Ubuntu and start again
  • if everything went well cat /etc/resolv.conf should have nameservers from your Windows
@bayeslearner
Copy link

Will the recent added dns tunneling feature resolve the issue of needing to switch between public DNS and corporate DNS? I thought that's what it is for, but the documentation is sparse AF.

@ThePlenkov
Copy link
Author

ThePlenkov commented Jan 10, 2024

@bayeslearner good point. It could be. Gonna try that. It's seems like exactly what this script is doing - adding DNS servers from the host OS. However from what I see in their post - it might be not yet available in the enterprise version yet:

Please note this feature is currently only available to Windows Insiders canary and Release Preview Channel with the latest Windows 11, version 22H2 update here. You can get access now by joining the Windows Insider Program and choosing to opt in your device into the Release Preview Channel.

@rbruhn
Copy link

rbruhn commented Jan 28, 2024

Thank you for this. I wasted a day trying to figure out how to get my domains working.

@iamjpix
Copy link

iamjpix commented Mar 15, 2024

Thanks! This works great.

@7wells
Copy link

7wells commented Mar 25, 2024

@ThePlenkov
Finally, I arrived here like so man others and find your solution very helpful - thank you for that! ❤️

Do you have an idea if it is possible to update resolv.conf even more programmatically, i.e. not only when booting the WSL distribution but also when enabling a WireGuard tunnel or engaging anther one?

I observed the following (hence my question):
The update happens only (and expectedly) when the WSL distribution is booted, not when I login via e.g. Microsoft terminal. However, when I fiddle with my Windows WireGuard tunnel (switching it on after the WSL distribution has been started), then no update happens. Would it make sense to put your code also in e.g. .profile, so it is called with every login? I can try this myself, of course, but would like to hear your thoughts in case there are some unwanted side effects etc. Furthermore, it would be nice if a nameserver from a WireGuard tunnel would be programmatically removed from the WSL distribution's /etc/resolv.conf if the tunnel is closed/WG switched off. I lack the knowledge to try this right away, so hearing back from you if this is feasible and if it makes sense at all, would be great. Thanks for your time and for sharing your thoughts! 👍

PS:
Sometimes, opening a WSL distribution from the Microsoft terminal drop-down menu takes longer, and in these cases, the distribution seems to be booted anew; therefore, resolv.conf is updated as wanted. However, sometimes, closing the WSL distribution tab and picking it again from the drop-down list opens it quickly, i.e. no booting; then, no update happens.

@ThePlenkov
Copy link
Author

@7wells thanks for the feedback. Well indeed what you describe - I face myself too when changing the network locations. Please let me know if your experiment works

@ddlandim
Copy link

ddlandim commented May 7, 2024

in boot.sh, change

sed -i '/nameserver/d' /etc/resolv.conf

to

:> /etc/resolv.conf

In some cases the file does not exist and the script will give an error on tee.

about output redirection:
The :> operator redirects an empty string to the any file.
Creation/cleanup:
If the file does not exist, it will be created as an empty file.
If the file exists, its contents will be overwritten with an empty string, clearing it.

@ThePlenkov
Copy link
Author

@7wells you were right! It works like a charm.

Dear all, I'm happy to say that mentioned above problem is not a problem anymore since version 22H2 with DNS tunneling introduced .

The only step step you need to perform:

[wsl2]
dnsTunneling=true

That's it. We waited for this solution for years. It's enabled by default since WSL 2.2.1

@BigSamu
Copy link

BigSamu commented Jun 6, 2024

@ThePlenkov

I tried your initial solution and indeed my config file /etc/resolv.conf is being updated, however when I connect to my company VPN I still can't get a successful ping to check the DNS resolution (I am pinging to google.com using ping google.com). Without connecting to my company VPN I have no issues at all (ping is successful).

Don't know what is going. This is really annoying and I can't use WSL 2.2.1 so I can't use the DNS tunneling feature, because the Windows version is setup by my company, and according to Craig Loewen this feature is only available in Windows Insiders canary and Release Preview Channel with the latest Windows 11.

Any suggestions? I really want to use WSL2 in my PC

@Obiken
Copy link

Obiken commented Jun 19, 2024

Thank you, this works for me.

Just want to not that to use dnsTunneling you need to be using Windows 11 with 22H2. I'm currently stuck on a Windows 10 with 22H2, and it doesn't work for me. So for the time being this Gist is the solution for me.

@ThePlenkov
Copy link
Author

@BigSamu can you please try first to connect to a company VPN and then start WSL? Thanks!

@BigSamu
Copy link

BigSamu commented Jul 7, 2024

@BigSamu can you please try first to connect to a company VPN and then start WSL? Thanks!

@ThePlenkov, indeed, that was the issue. I have to open the VPN first and the start the WSL so the boot.sh load the correct DNS addresses.

Many thanks for your response and your post.

@yiqiangjizhang
Copy link

I also had the same issue. DNS on my WSL2 was not working when I was connected to my work VPN at home. I followed all the steps above but found that it was still working... problem was... I needed a PC restart! Shutting down and WSL does not seem to be sufficient. Very many thanks to put this in place without the need of admin accounts (as my work laptop is quite restricted)!!!!

@jamesmckibbenathrb
Copy link

jamesmckibbenathrb commented Nov 13, 2024

Sometimes an interface can have a DNS value on it, but not be "Up", this takes care of that (User used as username for reference to next step update):
file (Windows): C:\Users\User\bootDNS.ps1

$upAdapters = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' }
$adapterDNS = Get-DnsClientServerAddress
$upAdapters | ForEach-Object {
    $adapterName = $_.Name
    $ipAddresses = Get-NetIPAddress -InterfaceAlias $adapterName | Where-Object { $_.IPAddress -ne $null }
    if ($ipAddresses) {
        $ipAddresses | ForEach-Object {
            $adapterDNS | Where-Object { $_.InterfaceAlias -eq $adapterName } | Select-Object -ExpandProperty ServerAddresses | ForEach-Object { "nameserver $_" }
        }
    }
}

then update as such (fix your "User" reference for the -File path, and update example.com to your environment):
file (WSL): /root/boot.sh

#!/bin/bash

# Remove existing "nameserver" lines from /etc/resolv.conf
sed -i '/nameserver/d' /etc/resolv.conf

# Run the PowerShell command to generate "nameserver" lines and append to /etc/resolv.conf
# we use full path here to support boot command with root user
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -File C:\\Users\\User\\bootDNS.ps1 | sort | uniq | tr -d '\r'| tee -a /etc/resolv.conf > /dev/null

# Remove anything not having 'nameserver' in it
sed -i '/nameserver/!d' /etc/resolv.conf

# Add search domains
echo "search example.com" >> /etc/resolv.conf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment