WSL 2 uses a Hyper-V Virtual Network adapter. Network connectivity works without any issue when a VPN is not in use. However when a Cisco AnyConnect VPN session is established Firewall Rules and Routes are added which breaks connectivity within the WSL 2 VM. This issue is tracked WSL/issues/4277
Below outline steps to automatically configure the Interface metric on VPN connect and update DNS settings (/etc/resolv.conf) on connect/disconnect.
After connecting to the VPN, you'll want to modify the Interface Metric of the Cisco VPN Adapter
PS C:\Users\gyurgyik> Get-NetAdapter | Where-Object {$_.InterfaceDescription -Match "Cisco AnyConnect"} | Set-NetIPInterface -InterfaceMetric 6000
Run the following command in Powershell with Administrative permission.
At this point you should have connectivity in your container (but without name resolution). You can test this by running ping 8.8.8.8
.
Once connected the VPN determine the DNS servers that are configured:
PS C:\Users\gyurgyik> (Get-NetAdapter | Where-Object InterfaceDescription -like "Cisco AnyConnect*" | Get-DnsClientServerAddress).ServerAddresses
10.10.0.124
10.10.0.132
Update /etc/resolv.conf
N-20S5PF20MB4R:~$ cat /etc/resolv.conf
nameserver 10.10.0.124
nameserver 10.10.0.132
Verify Connectivity
ping google.com -c 4
Save the following scripts to %homepath%\wsl\scripts
Get-NetAdapter | Where-Object {$_.InterfaceDescription -Match "Cisco AnyConnect"} | Set-NetIPInterface -InterfaceMetric 6000
$dnsServers = (Get-NetAdapter | Where-Object InterfaceDescription -like "Cisco AnyConnect*" | Get-DnsClientServerAddress).ServerAddresses -join ','
$searchSuffix = (Get-DnsClientGlobalSetting).SuffixSearchList -join ','
function set-DnsWsl($distro) {
if ( $dnsServers ) {
wsl.exe -d $distro -u root /opt/wsl_dns.py --servers $dnsServers --search $searchSuffix
}
else {
wsl.exe -d $distro -u root /opt/wsl_dns.py
}
}
set-DnsWsl fedora
For each Linux instance:
- Disable automatic updating of resolv.conf by WSL
$ cat <<EOF > /etc/wsl.conf [network] generateResolvConf = false EOF
- Restart/Shutdown WSL:
wsl --shutdown
(WARNING: this will kill all current sessions!)
For each VM, run:
$ cp /mnt/c/Users/$username/wsl/scripts/wsl_dns.py /opt/wsl_dns.py
$ chmod +x /opt/wsl_dns.py
Windows Scheduled Tasks allows you to trigger an action when a certain log event comes in. The Cisco AnyConnect VPN client generates a number of log events.
We will create two tasks. The first task, will configure the interface metric when the VPN connects. The second task, will execute the dns update script inside of your Linux VM when the VPN Connects and Disconnects.
- 2039: VPN Established and Passing Data
- 2061: Network Interface for the VPN has gone down
- 2010: VPN Termination
- 2041: The entire VPN connection has been re-established.
- Open Task Scheduler
- Create a Folder called
WSL
(Optional, but easier to find rules later) - Create Rules
- Update AnyConnect Adapter Interface Metric for WSL2
- General: Check: Run with highest privileges
- Triggers:
- On an Event, Log:
Cisco AnyConnect Secure Mobility Client
, Source:acvpnagent
, Event ID:2039
- On an Event, Log:
Cisco AnyConnect Secure Mobility Client
, Source:acvpnagent
, Event ID:2041
- On an Event, Log:
- Action: Start a program, Program:
Powershell.exe
, Add arguments:-WindowStyle Hidden -NonInteractive -ExecutionPolicy Bypass -File %HOMEPATH%\wsl\scripts\setCiscoVpnMetric.ps1
- Condition: Uncheck: Start the task only if the computer is on AC power
- Update DNS in WSL2 Linux VMs
- Triggers:
- On an Event, Log:
Cisco AnyConnect Secure Mobility Client
, Source:acvpnagent
, Event ID:2039
- On an Event, Log:
Cisco AnyConnect Secure Mobility Client
, Source:acvpnagent
, Event ID:2010
- On an Event, Log:
Cisco AnyConnect Secure Mobility Client
, Source:acvpnagent
, Event ID:2061
- On an Event, Log:
Cisco AnyConnect Secure Mobility Client
, Source:acvpnagent
, Event ID:2041
- At log on: At log on of $USER
- On an Event, Log:
- Action: Start a program, Program:
Powershell.exe
, Add arguments:-WindowStyle Hidden -NonInteractive -ExecutionPolicy Bypass -File %HOMEPATH%\wsl\scripts\setDns.ps1
- Condition: Uncheck: Start the task only if the computer is on AC power
- Triggers:
- Update AnyConnect Adapter Interface Metric for WSL2
- Test: Connect to the VPN, a powershell window should pop-up briefly
Q: Does traffic orginating from the Linux VM still route through the VPN?
A: Yes, I believe so. I did not see any leaked traffic when running a tcpdump on my router.
Q: Are VPN resources accessible from the Linux VM?
A: Yes
Q: Can the Linux VM communicate with Windows?
A: No, it appears a firewall rule is preventing traffic between Windows and the Linux VM.
Q: Can I still run WSL1 instances?
A: Yes, you can run WSL1 and WSL2 insatnces simutaneously
Q: How do I revert/disable these changes?
A: Disable scheduled Tasks, remove/modify /etc/wsl.conf
from each WSL Instance, Reboot
I had issues with the
json.loads(p.stdout.decode("utf-8")
section as my code kept returning\nSUCCESS: Specified value was saved.\n
at the beginning ofp.stdout
of myCompletedProcess
object,p
, that was returned back from thesubprocess.run(...)
calls. This kept throwingjson.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)
and I didn't realize that I had a second line due to that line showing up at the beginning and there being no comma at the end of that line meant it wasn't valid JSON.What I did to fix this were a few things (some of which are probably pointless, but clean the code up a bit more):
p = subprocess.run(...)
, I addeduniversal_newlines=True
as an argument so that thetype
ofp.stdout
wasn't a byte literal that needed decoding and was instead converted to a string, saving some ugliness/redundancy in the return statement (arguments todecode()
are deprecated and ignored, so "utf-8" didn't do anything).So I sliced on the newline chars and grabbed the data after the split but then I had to also account for if/when people don't run into this issue either, so I wrote a method to handle this and replace in the dns methods:
This allows me to check the returned values. The string appears to always start and end with a newline character, so if there's no extra line, grab index 1 that contains the values we want. If there is an extra line, grab index 2. Expected data formats:
With success message:
With no success message:
My
get_cisco_vpn_dns_servers()
andget_dns_search_list()
now look like this:Also,
with open('/etc/resolv.conf', 'w') as fd:
doesn't work when theresolv.conf
is a symlink. In my case, when Ill
d the/etc/
directory, I got a red highlightedlrwxrwxrwx 1 root root 29 Dec 6 2019 resolv.conf -> ../run/resolvconf/resolv.conf
as the linked location didn't exist. This didn't matter toopen
asresolv.conf
existed, so it tried to write to it, unsuccessfully. I fixed this by checking if the file is a link, rename it toresolvOLD.conf
to maintain the symlink, in case you still want to use it.Before:
After:
My
resolv1.conf
is a backup of my currentresolv.conf
file for testing purposes. The contents ofwrite_resolv(data)
is now as follows:Hopefully, this will fix the issue @ScottTaftPotter was running into above with deleting and possibly reverting
resolv.conf
Full text of my fixed
wsl_dns.py
file:Almost forgot, I exported each of these tasks as an xml file to use for importing. This does require some user input to change the values I removed from mine as it contained my username and UserID. You'll need to replace ${username} with your local windows username and ${UserID} with your uid. You can fetch your uid by pasting this into a cmd.exe window:
for /F "tokens=1,2 delims=: " %A in ('WhoAmI /USER /FO LIST') do @if /I "%~A" == "SID" echo %B
I'll be uploading these to a public repo and hyperlinking them here so my comment isn't so massive, but until then, here's the raw text.
Update AnyConnect Adapter Interface Metric for WSL2.xml
Update DNS in WSL2 Linux VMs.xml