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.
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
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,
- Create the shell script to set
resolv.conf
. - Make it executable.
- Allow
sudo
without prompting for a password. - 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.
- Open
Task Scheduler
. - In the right-hand pane, under
Actions
clickCreate Task...
. - In the name put something descriptive, like
AnyConnect DNS Work Around
. - Click the
Triggers
tab. - Click
New...
. - Change the
Begin the task
drop down toOn an event
. - Change the
Log
drop down toCisco AnyConnect Secure Mobility Client
. - Put
3020
in theEvent ID
box. - Click
OK
to close the trigger dialog. - Click the
Actions
tab. - Click
New...
. - Put
wsl.exe
in theProgram/script
box. - Put
--user root /bin/vpn-dns.sh
in the arguments box. - Click
OK
to close the action dialog. - Click
OK
to close the event dialog. - Now, if you are connected to the VPN, disconnect.
- Check WSL, you should be able to
ping www.google.com
. - Connect to the VPN.
- 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.