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.

@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