Skip to content

Instantly share code, notes, and snippets.

@machuu
Last active April 18, 2024 23:13
Show Gist options
  • Save machuu/7663aa653828d81efbc2aaad6e3b1431 to your computer and use it in GitHub Desktop.
Save machuu/7663aa653828d81efbc2aaad6e3b1431 to your computer and use it in GitHub Desktop.
Workaround for WSL2 network broken on VPN

Overview

Internet connection and DNS routing are broken from WSL2 instances, when some VPNs are active.

The root cause seems to be that WSL2 and the VPN use the same IP address block, and the VPN routing clobbers WSL2's network routing.

This problem is tracked in multiple microsoft/WSL issues including, but not limited to:

Change WSL2 subnet (Preferred Solution)

Permanently changing the IP address block used by WSL2 appears to prevent the routing conflict that breaks WSL2 networking.

More details and instructions in this gist: (Thanks @mikegerber for the explanation)

Interface Metric solution

Below is my original solution of modifying the VPN interface metric each time the VPN connects

Preferably, use the WSL subnet change above for a permanent fix.

The workaround breaks down into two problems:

  1. Network connection to internet
  2. DNS in WSL2

Network connection

When the VPN connection is active, network traffic out of WSL2 is not passed to the internet.

Changing the Interface Metric 1 -> 6000 for AnyConnect VPN Adapter resolves the connection issue, but this has to be done after each time the VPN connects.

By default, the Interface Metrics for AnyConnect are:

  • IPv6: 6000
  • IPv4: 1

ping times out from WSL Shell.

Changing the Interface Metrics for AnyConnect to:

  • IPv6: 6000
  • IPv4: 6000

ping to IP Addresses succeed, but still no DNS Resolution.

DNS Resolution

When the VPN is active, the autogenerated /etc/resolv.conf does not work. The list of nameservers must be manually built to include some sane default DNS Name Servers and the DNS from the VPN.

First, disable automatically generating /etc/resolv.conf. Add the following configuration, or create the file if it doesn't exist. The path to this file is from the shell prompt of your WSL2 instance.

/etc/wsl.conf

[network]
generateResolvConf = false

Next, manually add the corportate DNS Server as the first nameserver in /etc/resolv.conf.

/etc/resolv.conf

nameserver <corporateDNS1>
nameserver <corporateDNS2>
nameserver 1.1.1.1

To get <corporateDNS> addresses, use ipconfig /all from CMD or Powershell prompt, and check the details of the VPN adapter:

Description . . . . . . . . . . . : Cisco AnyConnect Secure Mobility Client Virtual Miniport Adapter for Windows x64
Physical Address. . . . . . . . . : XX-XX-XX-XX-XX-XX
DHCP Enabled. . . . . . . . . . . : No
Autoconfiguration Enabled . . . . : Yes
IPv6 Address. . . . . . . . . . . : xxxx:xxxx:xxxx:xxxx(Preferred)
Link-local IPv6 Address . . . . . : xxxx:xxxx:xxxx:xxxx(Preferred)
IPv4 Address. . . . . . . . . . . : 10.20.30.40(Preferred)
Subnet Mask . . . . . . . . . . . : 255.255.255.255
Default Gateway . . . . . . . . . : ::
                                    0.0.0.0
DHCPv6 IAID . . . . . . . . . . . :
DHCPv6 Client DUID. . . . . . . . : 
DNS Servers . . . . . . . . . . . : 123.45.67.89    <- Corporate DNS 1
                                    123.45.67.90    <- Corporate DNS 2
Primary WINS Server . . . . . . . : xxx.xx.xxx.xx
NetBIOS over Tcpip. . . . . . . . : Enabled

Automatically update Interface Metric

To automate this, I put the PS command in a script and created a Scheduled Task to run every time there is a network change.

Save the script in a file

First, create the script. I have a 'scripts' directory in my Windows user home, so I put it at:

%HOMEPATH%\scripts\UpdateAnyConnectInterfaceMetric.ps1

Get-NetAdapter | Where-Object {$_.InterfaceDescription -Match "Cisco AnyConnect"} | Set-NetIPInterface -InterfaceMetric 6000

You can save it where you want, just make sure to use that path in step 13 below.

Create the scheduled task:

  1. Open 'Task Scheduler'
  2. Click "Create Task" on Right Sidebar
  3. Name: Update Anyconnect Adapter Interface Metric for WSL2
  4. Set Security Options
    • Check box: 'Run with highest priveleges'
  5. Select 'Triggers' Tab
  6. Click 'New' at bottom of Window
  7. Open 'Begin the task' drop-down
  8. Select 'On an Event'
  9. Configure Event:
    • option 1: Trigger on any Network Change
      • Log: 'Microsoft-Windows-NetworkProfile/Operational'
      • Source: 'NetworkProfile'
      • Event ID: '10000'
    • option 2: Trigger only when AnyConnect Client successfully connects to VPN
      • Anyconnect 4.x
        • Log: 'Cisco AnyConnect Secure Mobility Client'
        • Source: 'acvpnagent'
        • Event ID: '2039'
      • Anyconnect 5.x
        • Log: 'Cisco Secure Client - AnyConnect VPN'
        • Source: 'csc_vpnagent'
        • Event ID: '2039'
  10. Click 'OK'
  11. Select 'Actions' Tab
  12. Click 'New'
  13. Configure Action:
    • Action: 'Start a Program'
    • Program/script: 'Powershell.exe'
    • Add arguments: '-ExecutionPolicy Bypass -File %HOMEPATH%\scripts\UpdateAnyConnectInterfaceMetric.ps1'
  14. Click 'OK'
  15. Select 'Conditions' Tab
  16. Uncheck box:
    • Power -> Start the task only if the computer is on AC Power
  17. Click 'OK'

When AnyConnect finishes connecting, a Powershell window pops up for a couple seconds and WSL can reach the network.

@regwhitton
Copy link

@corymichaelmurray - I also had the issue with corporate policy preventing the script from running. However, I was able to put the contents of the script directly on the command line. See my comment above.

@MarcusLang-art
Copy link

MarcusLang-art commented Jun 19, 2022

It's not just the proxy that matters, but the pula proxy. Many factors have a big impact on the effectiveness of your pula proxy. If you misconfigure the pula for your project, you'll likely find that your proxies are blocked, and you can no longer access the target website. So it's much better than a regular proxy. I also advise you to check out web scraping. Based on your request, I've used their services, and I think that's exactly what would work for you. Also, didn't specify what proxies you plan to use: public, shared, or dedicated? They're different in quality, after all.

@narsimhamchelluri-ds
Copy link

narsimhamchelluri-ds commented Sep 8, 2022

Hi, I'm using Windows 11 with Cisco AnyConnect. As of a day or so ago my WSL connections to hosts that I access through the VPN has begun failing with the error "no route to host".

I don't believe this is related to DNS in my case because running nslookup <hostname> on either the Windows machine in PowerShell or on the bash prompt in Ubuntu under WSL gives me IPs on the 192.168.x.x range (and the IPs are the same in either case). I believe they are VPC hostnames. Moreover, when I run ipconfig /all there are no DNS Servers listed under the AnyConnect adapter. I am able to access these VPC hostnames from Windows proper when I am connected to the VPN and I cannot access them when I am not connected to the VPN.

When I first boot my machine, I tried to make sure that AnyConnect does not start by turning the "Cisco AnyConnect User Interface" setting off in Settings -> Apps -> Startup. I am a little concerned that the "User Interface" means that the app may have started ahead of some UI components but I am not really sure. I am thinking about this because of this comment.

After booting I open up a WSL terminal window.

Next up I start a PowerShell instance as Admin and run Get-NetAdapter | Where-Object {$_.InterfaceDescription -Match "Cisco AnyConnect"} | Set-NetIPInterface -InterfaceMetric 6000. I have ensured that this changes the correct AnyConnect IPv4 InterfaceMetric value to 6000.

Finally I run Restart-Service LxssManager from the same PowerShell. This closes my WSL terminal instance, so I open up another one.

In case it matters, I have taken steps to ensure that my /etc/resolv.conf is not auto-generated at WSL startup time. I believe the generateResolvConf = false behavior under the network INI option in /etc/wsl.conf in the version of WSL I use is buggy and I can't recall exactly how I managed to achieve this bit of behavior, but I did - the resolv.conf contents don't change between reboots, they don't contain comments about auto-generation, and they are what I have manually set them to, which is nothing interesting: a couple of nameserver entries from my WiFi adapter and a search entry with that same adapter's Connection-specific DNS Suffix as the value.

Still, after all of this, I am unable to access the VPC hostnames from WSL; I get "no route to host" errors. I reiterate that I can access these hostnames from Windows proper, and I was able to access them from WSL until very recently. In examining my bash history (I keep timestamps) I see that I ran apt-get upgrade within the last 24 hours so that may be it.

I have gone through several blog posts and GitHub Gist and Issue comment threads, and I'm at a complete loss. If anyone has any suggestions I would love to hear them. Thank you.

Cisco AnyConnect Version: 4.8.01090
Windows Version: 10.0.22000
wsl.exe Version: 10.0.22000.653 (found using this technique)

# dpkg -l '*wsl*'
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name           Version                Architecture Description
+++-==============-======================-============-=================================================================
ii  ubuntu-wsl     1.450.2                amd64        Ubuntu on Windows tools - Windows Subsystem for Linux integration
ii  wslu           2.3.6-0ubuntu2~20.04.0 all          collection of utilities for the Windows 10 Linux Subsystem

--

Edit: I have filed an issue with the WSL project. Feel free to follow along here: microsoft/WSL#8811

@Lucascoorek
Copy link

The easiest workaround for this problem is this package: https://github.com/sakai135/wsl-vpnkit 🚀

@573
Copy link

573 commented Sep 27, 2022

The easiest workaround for this problem is this package: https://github.com/sakai135/wsl-vpnkit 🚀

Absolutely 🚀 I can confirm that, tested and proofing it works - here is my steps to reproduce - not necessarily all needed:
EDIT: Striked out the points that are not needed as precautions (tested with the release as of this comments date)
My other WSL2 instance started already
AnyConnect running but disconnected
Installed https://github.com/sakai135/wsl-vpnkit and run in foreground all checks green
Connected via AnyConnect to VPN
ping google.com in my other WSL2 instance still worked 🦄
Outside WSL2:

  • Accessing enterprise bitbucket still worked
  • Accessing enterprise jira still worked
  • Doing full mvn build of enterprise software still worked
  • Accessing internal enterprise mailserver still worked

All that in no time using no brain at all 😆

Not working but not important in my case: Accessing internal enterprise ressource from within WSL2 instance via domain name if name server is enterprise one. Access the resource via IP if really needed.

@machuu
Copy link
Author

machuu commented Sep 27, 2022

@573 / @Lucascoorek,

At the time I wrote this, I tried wsl-vpnkit, but it didn't fix the routing issue. That was ~2 years ago, so things may have changed.

@573
Copy link

573 commented Sep 27, 2022

@573 / @Lucascoorek,

At the time I wrote this, I tried wsl-vpnkit, but it didn't fix the routing issue. That was ~2 years ago, so things may have changed.

Tried latest commit / release, so ...

@Sandv
Copy link

Sandv commented Oct 5, 2022

Wow. This works. Thank you so much!

@pypycodes
Copy link

Tested OK. Flawless

@bisand
Copy link

bisand commented Nov 4, 2022

It worked perfectly! Thanks!

@luisbunuel
Copy link

Have you tried the option in the Cisco AnyConnect client "Allow local (LAN) access when using VPN (if configured)" ?
It works for me, you dont need to modify the interfaces metrics and let WSL to manage the resolv.conf file.

@573
Copy link

573 commented Nov 18, 2022

Have you tried the option in the Cisco AnyConnect client "Allow local (LAN) access when using VPN (if configured)" ? It works for me, you dont need to modify the interfaces metrics and let WSL to manage the resolv.conf file.

This works when the admins in your enterprise enabled that switch and also in certain cases internal resources those visible only to enterprise members are invisible when using the switch.

@cwsmith-160
Copy link

In DNS Resolution the /etc/resolv.conf link should be removed, a new file should be created, and then be made immutable e.g.,

sudo rm /etc/resolv.conf
sudo bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf'
sudo chattr +i /etc/resolv.conf

sudo bash -c 'echo "[network]" > /etc/wsl.conf'
sudo bash -c 'echo "generateResolvConf = false" >> /etc/wsl.conf'

See the following for more details
microsoft/WSL#5420

@Tarun097
Copy link

Thanks @machuu , works for me

@5ebbe
Copy link

5ebbe commented Jan 12, 2023

Wonderful! This really fixed it for me (tried alot of other things that did nothing)!

A tip I have is to add '-Window Minimized' at the beginning of the arguments. This cuts down the time the powershell screen is shown whenever the event is triggered. I understand there are other ways but they seem to be more advanced than just adding an argument.

My goal would be it never show at all or just start minimized from the beginning. Any ideas on this will be greatly appreciated.

Thanks :)

@mrudinal
Copy link

Thank you very much. I was able to configure internet and dns resolution for my wsl2 instance.
This guide worked perfectly

@TW4177
Copy link

TW4177 commented Apr 20, 2023

Great instructions. I used @regwhitton's approach proactively, and found I needed to use """ around the Cisco AnyConnect match string in the scheduled task arguments (looks like it does a layer of de-quoting on them.) Thanks!

@Pit-Storm
Copy link

You shouldn't change the Interface-Metric due to the than different routing. See the following blogpost for explanation: https://janovesk.com/wsl/2022/01/21/wsl2-and-vpn-routing.html
TLDR: If it works, it doesn't mean that it doesn't have side effects. And it's not only solving the thing that you was intended to fix.

The problem of not using the correct DNS-Server is properly explained and the suggested solution should be used from networking point of view.

For the IP-Range problem you have only the following two options:

  1. Changing the routing table (see blogpost above)
  2. Changing the subnet-range that WSL is using

How second could work, is shown in this Microsoft Q&A: https://learn.microsoft.com/en-us/answers/questions/1123820/set-wsl2-subnet

TLDR: Change SubNet of WSL NAT-Router to a different one which does not collide with your Company-VPN subnet. To do so got to regedit and edit the following entries:
Path: Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss
Entries: NatGatewayIpAddress and NatNetwork
Values e.g.: 19.16.0.1 and 19.16.0.0/16

Again: Please don't change the InterfaceMetric!

@Shterneregen
Copy link

Shterneregen commented Jun 28, 2023

Thanks for the great instruction!

Base on this info I wrote script that creates windows task to automatically update metric on VPN startup (gist)

  • Create CreateScheduledTask.ps1 file with content below
$taskname="Fix VPN for WSL"
$scriptName = "UpdateAnyConnectInterfaceMetric.ps1"
$commandToUpdateMetric = "Get-NetAdapter | Where-Object {`$_.InterfaceDescription -Match 'Cisco AnyConnect'} | Set-NetIPInterface -InterfaceMetric 5500"

$profilePath = $env:USERPROFILE
$scriptPath = "$profilePath\$scriptName"

if(Test-Path -Path $scriptPath){
    Write-Output "File '$scriptPath' already exists"
} else {
    New-Item -ItemType File -Path $scriptPath -Value $commandToUpdateMetric
    Write-Output "File '$scriptPath' created successfully"
}

$triggerClass = Get-CimClass -ClassName MSFT_TaskEventTrigger -Namespace Root/Microsoft/Windows/TaskScheduler:MSFT_TaskEventTrigger
$trigger = New-CimInstance -CimClass $triggerClass -ClientOnly
$trigger.Subscription = 
@"
<QueryList><Query Id="0" Path="Cisco AnyConnect Secure Mobility Client"><Select Path="Cisco AnyConnect Secure Mobility Client">*[System[Provider[@Name='acvpnagent'] and EventID=2039]]</Select></Query></QueryList>
"@
$trigger.Enabled = $True 

$triggers = @()
$triggers += $trigger
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit 0

$user = Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -expand UserName
$action = New-ScheduledTaskAction -Execute "Powershell.exe" -Argument "-Command Get-Content '$scriptPath' | PowerShell.exe -noprofile -"

Register-ScheduledTask -TaskName $taskname -Trigger $triggers -User $user -Action $action -Settings $settings -RunLevel Highest -Force
  • Run as admin command in PowerShell (in the same folder with the script file)
Get-Content .\CreateScheduledTask.ps1 | PowerShell.exe -noprofile -
  • Restart Windows
  • Launch WSL
  • Launch VPN
  • Done! Metric automatically updated

@swe9
Copy link

swe9 commented Jul 19, 2023

TLDR: Change SubNet of WSL NAT-Router to a different one which does not collide with your Company-VPN subnet. To do so got to regedit and edit the following entries: Path: Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss Entries: NatGatewayIpAddress and NatNetwork Values e.g.: 19.16.0.1 and 19.16.0.0/16

Again: Please don't change the InterfaceMetric!

I was able to use those keys to put WSL into my local network. The only problem was that WSL kept getting assigned xxx.xxx.xxx.1 which happened to be my gateway on the local network. I tried several solutions from other postings, but ultimately ended up moving the gateway to xxx.xxx.xxx.9 and having DHCP start at 10. Everything seems to work nicely after that.

@vquanguit1102
Copy link

to be honest I don't know what I am doing but it works 👍

@vaskenis
Copy link

Thanks for the proposed solution, it works for me on Win11! 👍

@riosdav1
Copy link

The only solution that worked for me, thanks! But, I had to change %USERPATH% to $HOME.

@dswhite42
Copy link

dswhite42 commented Nov 15, 2023

You shouldn't change the Interface-Metric due to the than different routing. See the following blogpost for explanation:
...
For the IP-Range problem you have only the following two options:

  1. Changing the routing table (see blogpost above)

@Pit-Storm , just a note that Cisco AnyConnect (at least the way it's configured for us) is smart enough to detect an attempt to delete a route, and will automatically reinsert it back into the routing table the moment you try.

@dswhite42
Copy link

For users of Cisco AnyConnect 5.0+ (I have 5.0.03076), the Windows Event logging naming convention has changed. So if you're using Option 2 in step 9 of the instructions above (trigger the PowerShell script only after the VPN connects), the trigger should now look like this:

  • option 2: Trigger only when AnyConnect Client successfully connects to VPN
    • Log: 'Cisco Secure Client - AnyConnect VPN'
    • Source: 'csc_vpnagent'
    • Event ID: '2039'

@machuu
Copy link
Author

machuu commented Nov 15, 2023

@dswhite42,

Thanks for the 5.x info.
That step is now updated with 4.x vs 5.x Event Log details.

@cassucena
Copy link

Thanks so much ! this workaround worked fine for me!

@GrizzlyBoer
Copy link

Hello everyone,

The command "Get-NetAdapter | Where-Object {$_.InterfaceDescription -Match "Cisco AnyConnect"} | Set-NetIPInterface -InterfaceMetric 6000" works fine when VPN is freshly started.

However, if VPN is activated before the Windows logon, it doesn't work.
So VPN -> Windows Logon -> PS-Script -> WSL does not work.
I had to stop the VPN, start it again, and then it worked with the PS script. Does anyone have an idea what could be the reason for this and whether it can be circumvented?

@storkviola
Copy link

@GrizzlyBoer I have no idea, but I can confirm your workaround works for me as well.

@ZaidaEMtzMo
Copy link

@GrizzlyBoer and @storkviola: Are you using the task scheduler? Would it work if you changed the settings of the scheduler to "Run whether user is logged on or not" in the General tab?

@s-lindenau
Copy link

s-lindenau commented Dec 21, 2023

If you're also having problems with executing unsigned files like i had, you can transform the file to a direct command.
Full example in the comment by @regwhitton
Or if you want to do it yourself, see for reference: https://superuser.com/a/1080336

I would also recommend adding the minimize window parameter, as @5ebbe suggested!

Works great now, even on corporate devices with strict policies 👍
Edit: is does however seem to lose access to specific (other) sites, so i might have to check the comments by @573 for a permanent solution.

@GrizzlyBoer
Copy link

GrizzlyBoer commented Jan 30, 2024

@ZaidaEMtzMo Sorry for the late answer, we have been quite busy the last 2 months.

I've tried using the scheduler with the "Run whether user is logged on or not" and without but the problem stays the same.
If VPN is established before Windows Login it won't work until you reconnect VPN after login again.

BUT we found something different, it seems Podman Desktop has finally caught up to Docker Desktop with an option for "user-mode networking" when you create a new podman machine. If you activate it WSL, including all running instances, has full connectivity as soon as Podman is running.

At least Podman Desktop is free to use

@mikegerber
Copy link

Again: Please don't change the InterfaceMetric!

I agree. In our setup I had to set the WSL2 network to link-local addresses. Details + commands to set the IP range here:

https://gist.github.com/mikegerber/91fcea262028e09b2fd0969193c6c260

TL&DR: WSL2 and VPN networks should not overlap, set the IP address range and not the VPN interface metric.

@machuu
Copy link
Author

machuu commented Feb 28, 2024

@mikegerber,
Thanks, this does look like a cleaner and more permanent solution.
Adding a note and link to the top of this gist.

@ZaidaEMtzMo
Copy link

ZaidaEMtzMo commented Feb 28, 2024

@mikegerber Does this solution allow WSL to work when the VPN is on AND when the VPN is off?
I used the previous solution, so I would like to confirm this before I go and undo all the changes.

@mikegerber
Copy link

@mikegerber Does this solution allow WSL to work when the VPN is on AND when the VPN is off? I used the previous solution, so I would like to confirm this before I go and undo all the changes.

Yes. You need to check your DNS configuration though, because you can't use a DNS server from the VPN. Here WSL just auto-configured my laptop as DNS server:

$ cat /etc/resolv.conf
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 169.254.214.1

(169.254.214.0/24 is the WSL2 NAT network I configured (see my comment above), .1 is the NAT gateway = my laptop) I would recommend first checking if you have general IPv4 connectivity using ping -n 8.8.8.8 first, then fixing DNS if it needs fixing.

@mikegerber
Copy link

mikegerber commented Mar 1, 2024

(169.254.214.0/24 is the WSL2 NAT network I configured (see my comment above), .1 is the NAT gateway = my laptop) I would recommend first checking if you have general IPv4 connectivity using ping -n 8.8.8.8 first, then fixing DNS if it needs fixing.

I've added a note about DNS in my gist. WSL2 seems to bring a DNS proxy and autoconfigures it here, but I didn't find any documentation on it + I'm not a really a Windows expert.

@micheldiemer
Copy link

Anyone coming here mightwant to attach anoter HyperV switch to the WSL machine

Here is a procedure

microsoft/WSL#4799 (comment)

microsoft/WSL#4799 (comment)

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