Automatic DNS configuration with WSL and AnyConnect client

We have the AnyConnect client and are now sending all traffic over the tunnel. This breaks WSL DNS resolution, here's how I worked around it.

When using the Cisco AnyConnect client in a full tunnel setup, where it sends all traffic over the VPN, the automatic DNS configuration in WSL does not work. It fails to resolve any address. The recommendations that I was finding online was not great. They were things like, disable automatic creation of resolv.conf or manage your resolv.conf file manually every time you connect/disconnect from the VPN. My answer to that is a resounding no. I wanted an automated way of updating the resolv.conf with the correct DNS entries and keeping it that updated.

My solution was to get the DNS server entries of the local system and build the resolv.conf from those. I do this every time the VPN connects, disconnects, or a shell is opened. This way, it is always up to date and easy to recover from any unexpected changes to the resolv.conf.

I based the work of the shell script from this comment in a GitHub issue

DNS server coming from vpn network is not reflected in WSL · Issue #1350 · microsoft/WSL
A brief description I've L2TP/IPsec vpn connection without default gateway set and own DNS server Expected results Bash should add VPN DNS IP to /etc/resolv.conf Actual results (with terminal o...

There were 2 primary components in this, the first was to configure WSL, the second was to automate it.

First hurdle, WSL configuration

We will start with the configuration of your primary WSL distribution; in my case it is Debian.

We will do a few different things in your primary WSL distribution,

  1. Create the shell script to set resolv.conf.
  2. Make it executable.
  3. Allow sudo without prompting for a password.
  4. Execute it every time the bash shell (and most others) launch.

vpn-dns.sh

Here is the script for /bin/vpn-dns.sh. You need to use sudo to create that file. I use vi to edit files,  that command is sudo vi /bin/vpn-dns.sh.

Press i to enter insert mode, and copy the below text and paste it in.

#!/bin/bash
echo "Getting current DNS servers, this takes a couple of seconds"
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command '
$ErrorActionPreference="SilentlyContinue"
Get-NetAdapter -InterfaceDescription "Cisco AnyConnect*" | ?{ $_.Status -eq "Up" } | Get-DnsClientServerAddress | Select -ExpandProperty ServerAddresses
Get-NetAdapter | ?{-not ($_.InterfaceDescription -like "Cisco AnyConnect*") -and ($_.Status -eq "Up") } | Get-DnsClientServerAddress | Select -ExpandProperty ServerAddresses
' | \
        awk 'BEGIN { print "# Generated by vpn fix func on", strftime("%c"); print } { print "nameserver", $1 }' | \
        tr -d '\r' > /etc/resolv.conf
clear

Now press escape the :wq to save and exit VI.

Mark executable

Now, mark it as executable.

sudo chmod +x /bin/vpn-dns.sh

sudo vpn-dns.sh without a password

Allow executing that script with sudo without prompting for a password.

echo "$(whoami) ALL=(ALL) NOPASSWD: /bin/vpn-dns.sh" | sudo tee /etc/sudoers.d/010-$(whoami)-vpn-dns

Execute anytime we open a login shell

And to make it run every time we open a new WSL shell.

echo "sudo /bin/vpn-dns.sh" | sudo tee /etc/profile.d/vpn-dns.sh

At this point, when you open a new shell for WSL (either by typing wsl, debian, or a new tab in Terminal, etc.), you should be greeted with a message, Getting current DNS servers, this takes a couple of seconds and then it should clear the screen. You can also call sudo /bin/vpn-dns.sh and it will reconfigure your WSL distributions for you when the unexpected changes occur.

Second hurdle, automatic configuration

Based on a previous post, Work around for AnyConnect client and Windows Subsystem for Linux 2 (frakkingsweet.com), we know how to run a command every time you connect to or disconnect from your VPN connection. The differences between that task and what you will be creating here is that you will start that task as the current user, and execute wsl instead of PowerShell.

The command you want the scheduled task to run is wsl --user root /bin/vpn-dns.sh.

This executes the vpn-dns.sh script we created above which reconfigures your resolv.conf. Here are the detailed steps.

  1. Open Task Scheduler.
  2. In the right-hand pane, under Actions click Create Task....
  3. In the name put something descriptive, like AnyConnect DNS Work Around.
  4. Click the Triggers tab.
  5. Click New....
  6. Change the Begin the task drop down to On an event.
  7. Change the Log drop down to Cisco AnyConnect Secure Mobility Client.
  8. Put 3020 in the Event ID box.
  9. Click OK to close the trigger dialog.
  10. Click the Actions tab.
  11. Click New....
  12. Put wsl.exe in the Program/script box.
  13. Put --user root /bin/vpn-dns.sh in the arguments box.
  14. Click OK to close the action dialog.
  15. Click OK to close the event dialog.
  16. Now, if you are connected to the VPN, disconnect.
  17. Check WSL, you should be able to ping www.google.com.
  18. Connect to the VPN.
  19. Check WSL, you should still be able to ping www.google.com.

The issue with disabling auto config

Based on my findings, when you disable auto configuration of your resolv.conf in your distributions, you break the link between that distribution and the others on your system. You would then have to go in and re-configure all your distributions in much the same way (if possible) as before. For example, if you have Docker Desktop, that distribution will not get the changes you make in your primary distribution.

When you have auto configuration enabled, it seems to copy your changes from one distribution into the others, which allows this solution to work.

Known limitation

If you invoke a shell or other command using wsl, example, wsl sh, it will overwrite the resolv.conf with what wsl wants, it will not run the vpn-dns.sh script. To recover from this, execute the script with sudo vpn-dns.sh or open a new shell.

Conclusion

This journey with the AnyConnect client (and potentially other VPN's) and WSL has been a rough one. Finally, I think we are at a good spot with this and the other workaround for WSL network/internet traffic. It would be nice if everything played well together, but they don't. At least there's a well-documented work around now.