Skip to content

Instantly share code, notes, and snippets.

@mikegerber
Last active November 26, 2025 12:09
Show Gist options
  • Select an option

  • Save mikegerber/91fcea262028e09b2fd0969193c6c260 to your computer and use it in GitHub Desktop.

Select an option

Save mikegerber/91fcea262028e09b2fd0969193c6c260 to your computer and use it in GitHub Desktop.
Fix WSL2 vs VPN networking

The problem

WSL2 uses a random network from the 172.16.0.0/12 RFC1918 private IP address block. And our VPN uses that address block, too, with a route metric of 1 (= most preferred.)

This breaks networking for WSL2. Meh!

The solution

While messing around with the interface/route metric of the VPN network may work around the problem, it also reduces the priority of the VPN. We do not really want this. Additionally, changing the interface metric does not seem to be permanent, so it requires more work when it breaks again.

A better solution is configuring WSL2 to not use a network in the VPN network space at all. However, in our case, the VPN routed all the available RFC1918 address space... (Isn't IPv4 great!)

But we can use the link-local address space from 169.254.0.0/16 and so have at least a semi-elegant and permanent solution!

  1. These PowerShell commands set the NAT network used by WSL2 to a subnet of 169.254.0.0/16 - I chose 169.254.214.0/24 here - and need to be run as a Windows administrator:
Set-ItemProperty `
  -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss `
  -Name NatNetwork `
  -Value "169.254.214.0/24"
Set-ItemProperty `
  -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss `
  -Name NatGatewayIpAddress `
  -Value "169.254.214.1"
  1. Reboot (I couldn't be bothered to check if restarting some service suffices.)

  2. After the reboot, you a. should get an error message the first time you start your WSL2 (because it can't use the IP it used before the change) and b. networking should work, now with shiny new 169.254.x.y addresses.

Notes

  • The only thing that makes this "semi-elegant" is that I would prefer using a network from RFC1918.
  • To check the current values, run Get-Item -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss.
  • I've also seen DNS break a lot and would recommend checking IPv4 connectivity through the WSL2 NAT without DNS first (e.g. ping -n 8.8.8.8 or similar), then fixing DNS, if needed. My WSL just auto-configured 169.254.x.1 in /etc/resolv.conf, and that worked here. So WSL2 seems to have a built-in DNS proxy, but I couldn't find any documentation on it.
  • Our VPN set up does not route all traffic through it, so this might be not be a complete solution in that case. It would be interesting to see how a Cisco AnyConnect VPN with default route to the VPN sets this default route - what metric does the route have?
@ddamerjian
Copy link

right I know that, I already acknowledged that, what I am saying is that I tried your approach without that setting and it still doesnt work

@ddamerjian
Copy link

Come on guys I really really need your help and to step up to the plate and help me. I absolutely need access within Cisco, its at the point that well, I may have to stick with my hardware (Meraki) VPN which DOESNT WORK with T-Mobile Fixed Wireless Access which I am using and I might have to revert to FIOS which does allow me to use my hardware VPN, but FIOS is more expensive and TMO is the way to go. Please help me, I'll setup a Webex session if youre free, anyone, please.

@mark-os
Copy link

mark-os commented Jan 6, 2025

@ddamerjian did you figure it out?

@ddamerjian
Copy link

No I did not, if I had I would have mentioned it. Im high and dry.

@mark-os
Copy link

mark-os commented Jan 14, 2025

@ddamerjian I checked my config and it turns out I can only get this working with wsl-vpnkit

Have you tried that?

@ddamerjian
Copy link

ddamerjian commented Jan 14, 2025

wow, wow, wow, it actually worked, I dont believe it. I cannot thank you enough for finding this solution!!!!!

So the way it appears to work is that you have to keep the script running in the Powershell for it to continue to work, as soon as you stop the script the connectivity stops working. All good

PS C:\Users\ddamerji> wsl.exe -d wsl-vpnkit --cd /app wsl-vpnkit
+ VPNKIT_GATEWAY_IP=192.168.127.1
+ VPNKIT_HOST_IP=192.168.127.254
+ VPNKIT_LOCAL_IP=192.168.127.2
+ TAP_MAC_ADDR=5a:94:ef:e4:0c:ee
+ VMEXEC_PATH=/app/wsl-vm
+ GVPROXY_PATH=/app/wsl-gvproxy.exe
+ TAP_NAME=wsltap
+ CHECK_HOST=example.com
+ CHECK_DNS=1.1.1.1
+ DEBUG=0
+ set +x
+ WSL2_TAP_NAME=eth0
+ WSL2_GATEWAY_IP=172.29.96.1
+ '[' 0 -eq 0 ]
+ set +x
starting vm and gvproxy...
INFO[0000] waiting for packets...
time="2025-01-14T11:12:10-05:00" level=info msg="waiting for clients..."
time="2025-01-14T11:12:10-05:00" level=info msg="new connection from remote to 66840"
started vm and gvproxy
check: ✔️ ping success to IPv4 WSL 2 gateway / Windows host (172.29.96.1)
check: ✔️ ping success to IPv4 Windows host (192.168.127.254)
check: ✔️ ping success to IPv4 gateway (192.168.127.1)
check: ✔️ nslookup success for example.com A using 192.168.127.1
check: ✔️ nslookup success for example.com A using 172.29.96.1
check: ✔️ nslookup success for example.com A using 1.1.1.1
check: ✔️ ping success to IPv4 external host domain (example.com)
check: ✔️ ping success to IPv4 external host IP (1.1.1.1)
check: ✔️ nslookup success for example.com AAAA using 192.168.127.1
check: ✔️ nslookup success for example.com AAAA using 172.29.96.1
check: ✔️ nslookup success for example.com AAAA using 1.1.1.1
ping: bad address 'example.com'
check: ➖ ping fail to IPv6 external host (example.com)
check: ✔️ wget success for http://example.com
check: ✔️ wget success for https://example.com

There is also an option to Setup as a standalone script within Linux, I assume bypassing the need for the Powershell script, but it failed at the following step maybe bc the required file is not in that site I dont know, no big deal as most importantly I got it to work as documented.

cisco@LAPTOP-L0MJCF72:~$ wget https://github.com/sakai135/wsl-vpnkit/releases/download/$VERSION/wsl-vpnkit.tar.gz
--2025-01-14 10:52:24--  https://github.com/sakai135/wsl-vpnkit/releases/download/v0.4.x/wsl-vpnkit.tar.gz
Resolving github.com (github.com)... 140.82.113.4
Connecting to github.com (github.com)|140.82.113.4|:443... connected.
HTTP request sent, awaiting response... 404 Not Found
2025-01-14 10:52:24 ERROR 404: Not Found.
cisco@LAPTOP-L0MJCF72:~$

@jecsanb
Copy link

jecsanb commented Apr 9, 2025

wow, wow, wow, it actually worked, I dont believe it. I cannot thank you enough for finding this solution!!!!!

So the way it appears to work is that you have to keep the script running in the Powershell for it to continue to work, as soon as you stop the script the connectivity stops working. All good

PS C:\Users\ddamerji> wsl.exe -d wsl-vpnkit --cd /app wsl-vpnkit
+ VPNKIT_GATEWAY_IP=192.168.127.1
+ VPNKIT_HOST_IP=192.168.127.254
+ VPNKIT_LOCAL_IP=192.168.127.2
+ TAP_MAC_ADDR=5a:94:ef:e4:0c:ee
+ VMEXEC_PATH=/app/wsl-vm
+ GVPROXY_PATH=/app/wsl-gvproxy.exe
+ TAP_NAME=wsltap
+ CHECK_HOST=example.com
+ CHECK_DNS=1.1.1.1
+ DEBUG=0
+ set +x
+ WSL2_TAP_NAME=eth0
+ WSL2_GATEWAY_IP=172.29.96.1
+ '[' 0 -eq 0 ]
+ set +x
starting vm and gvproxy...
INFO[0000] waiting for packets...
time="2025-01-14T11:12:10-05:00" level=info msg="waiting for clients..."
time="2025-01-14T11:12:10-05:00" level=info msg="new connection from remote to 66840"
started vm and gvproxy
check: ✔️ ping success to IPv4 WSL 2 gateway / Windows host (172.29.96.1)
check: ✔️ ping success to IPv4 Windows host (192.168.127.254)
check: ✔️ ping success to IPv4 gateway (192.168.127.1)
check: ✔️ nslookup success for example.com A using 192.168.127.1
check: ✔️ nslookup success for example.com A using 172.29.96.1
check: ✔️ nslookup success for example.com A using 1.1.1.1
check: ✔️ ping success to IPv4 external host domain (example.com)
check: ✔️ ping success to IPv4 external host IP (1.1.1.1)
check: ✔️ nslookup success for example.com AAAA using 192.168.127.1
check: ✔️ nslookup success for example.com AAAA using 172.29.96.1
check: ✔️ nslookup success for example.com AAAA using 1.1.1.1
ping: bad address 'example.com'
check: ➖ ping fail to IPv6 external host (example.com)
check: ✔️ wget success for http://example.com
check: ✔️ wget success for https://example.com

There is also an option to Setup as a standalone script within Linux, I assume bypassing the need for the Powershell script, but it failed at the following step maybe bc the required file is not in that site I dont know, no big deal as most importantly I got it to work as documented.

cisco@LAPTOP-L0MJCF72:~$ wget https://github.com/sakai135/wsl-vpnkit/releases/download/$VERSION/wsl-vpnkit.tar.gz
--2025-01-14 10:52:24--  https://github.com/sakai135/wsl-vpnkit/releases/download/v0.4.x/wsl-vpnkit.tar.gz
Resolving github.com (github.com)... 140.82.113.4
Connecting to github.com (github.com)|140.82.113.4|:443... connected.
HTTP request sent, awaiting response... 404 Not Found
2025-01-14 10:52:24 ERROR 404: Not Found.
cisco@LAPTOP-L0MJCF72:~$

Mine gets stuck at the set +x

@kenetcode
Copy link

ddamerjian

I hope this helps you, this configuration makes it so you don't have to run the script every time, it does it automatically when starting your Linux in WSL.

Definitive Manual: wsl-vpnkit Automation with systemd

This manual details the process to install and automate wsl-vpnkit as a service within a Linux distribution in WSL. It covers both scenarios with internet connection and without it.

Prerequisites

  • A Linux distribution installed in WSL 2 (e.g., Ubuntu).
  • systemd enabled in your distribution.
  • To verify if systemd is active, run: ps -p 1 -o comm=
  • If the output is not systemd, edit /etc/wsl.conf (sudo nano /etc/wsl.conf) and add [boot] and systemd=true. Save, close, and restart WSL from PowerShell with wsl --shutdown.

Step Zero: What to do if you don't have internet in WSL?

This step is only if when attempting "Step 1" commands like wget or apt-get fail due to lack of connection. Here we'll establish a temporary connection.

  1. In Windows (which does have internet):
  1. In PowerShell (from Windows):
  • Navigate to the Downloads folder and then execute the commands.

pwsh

Move to the folder where the downloaded file is located

cd $env:USERPROFILE\Downloads

Import wsl-vpnkit as a temporary distro using the relative path

wsl --import wsl-vpnkit-temp --version 2 "$env:USERPROFILE\wsl-vpnkit-temp" "wsl-vpnkit.tar.gz"

Start the network service from this temporary distro

wsl.exe -d wsl-vpnkit-temp --cd /app wsl-vpnkit

  • VERY IMPORTANT! A new terminal will open. Don't close it, just minimize it. This window is what keeps your internet connection active.
  1. Continue with the Manual:
  • With the connection now working thanks to the previous step, open a new terminal of your main distro (Ubuntu) and continue with Step 1 described below.

Step 1: Installation and Script Preparation

Now, within your main distro (e.g., Ubuntu), download and prepare the necessary files.

  1. Create a dedicated folder:
    mkdir -p ~/wsl-vpnkit-script && cd ~/wsl-vpnkit-script

  2. Install dependencies:
    sudo apt-get update && sudo apt-get install -y iproute2 iptables iputils-ping dnsutils wget

  3. Download and extract wsl-vpnkit:
    wget https://github.com/sakai135/wsl-vpnkit/releases/download/v0.4.1/wsl-vpnkit.tar.gz
    tar --strip-components=1 -xf wsl-vpnkit.tar.gz app/wsl-vpnkit app/wsl-gvproxy.exe app/wsl-vm app/wsl-vpnkit.service
    rm wsl-vpnkit.tar.gz


Step 2: systemd Service Creation and Configuration

Configure systemd to manage the scripts as an automatic service.

  1. Copy the service file to the systemd directory:
    sudo cp ./wsl-vpnkit.service /etc/systemd/system/

  2. Edit the service file:
    sudo nano /etc/systemd/system/wsl-vpnkit.service

  3. Replace all the file content with the following configuration. Remember to change YOUR_USER to your Linux username.

  • If you're not sure of your username, you can see it in the terminal with the command: whoami

[Unit]
Description=wsl-vpnkit
After=network.target

[Service]

Working directory where the scripts are located

WorkingDirectory=/home/YOUR_USER/wsl-vpnkit-script

Command to execute the script and its variables

ExecStart=/bin/sh -c "VMEXEC_PATH=$(pwd)/wsl-vm GVPROXY_PATH=$(pwd)/wsl-gvproxy.exe ./wsl-vpnkit"

Restart the service if it fails

Restart=always
KillMode=mixed

[Install]
WantedBy=multi-user.target

  1. Save and Close: In nano, press Ctrl+O, Enter, and then Ctrl+X.

Step 3: Service Activation and Management

Activate the service so it starts automatically with your distro.

  1. Enable automatic startup:
    sudo systemctl enable wsl-vpnkit

  2. Start the service now:
    sudo systemctl start wsl-vpnkit

  3. Verify it's working:
    systemctl status wsl-vpnkit

Look for the green line that says active (running). If it appears, you have successfully completed!


Final note: If you completed "Step Zero", once the systemd service is active (running) in your main distro, you can now close the PowerShell window of the temporary service and, if desired, remove the temporary distro with the command in PowerShell: wsl --unregister wsl-vpnkit-temp.

@ddamerjian
Copy link

ddamerjian commented Aug 5, 2025 via email

@kenetcode
Copy link

Sí, gracias, sé que "puedo" hacer esto si quiero, la pregunta es con qué frecuencia uso mi WSL en este momento, y mi colega de hecho ha hecho lo que mencionaste (o algo similar) y lo ha conseguido, así que sé que puedo si quiero, pero lo más importante es que lo tengo funcionando como una distribución independiente que puedo iniciar en dos segundos, así que por razones prácticas estoy listo, pero gracias por participar, también conocíamos este enfoque y es bueno saberlo sobre Linux. ¡Gracias! dd De: Kenet Ortiz @.> Enviado: lunes, 4 de agosto de 2025 19:37 Para: kenetcode @.> Cc: Comentario @.***> Asunto: Re: mikegerber/Fix WSL2 vs VPN networking.md @kenetcode comentó sobre este gist.

________________________________ ddamerjian Espero que esto te ayude, esta configuración hace que no tengas que ejecutar el script cada vez, lo hace automáticamente al iniciar tu Linux en WSL. Manual definitivo: Automatización de wsl-vpnkit con systemd Este manual detalla el proceso para instalar y automatizar wsl-vpnkit como un servicio dentro de una distribución Linux en WSL. Cubre ambos escenarios con conexión a Internet y sin ella. Requisitos previos * Una distribución Linux instalada en WSL 2 (por ejemplo, Ubuntu). * systemd habilitado en tu distribución. * Para verificar si systemd está activo, ejecuta: ps -p 1 -o comm= * Si la salida no es systemd, edita /etc/wsl.conf (sudo nano /etc/wsl.conf) y agrega [boot] y systemd=true. Guarda, cierra y reinicia WSL desde PowerShell con wsl --shutdown.
________________________________ Step Zero: What to do if you don't have internet in WSL? This step is only if when attempting "Step 1" commands like wget or apt-get fail due to lack of connection. Here we'll establish a temporary connection. 1. In Windows (which does have internet): * Open a browser and download the wsl-vpnkit.tar.gz file from the official page: * Link: https://github.com/sakai135/wsl-vpnkit/releases/latest * Save the file to your Downloads folder. 1. In PowerShell (from Windows): * Navigate to the Downloads folder and then execute the commands. pwsh Move to the folder where the downloaded file is located cd $env:USERPROFILE\Downloads Import wsl-vpnkit as a temporary distro using the relative path wsl --import wsl-vpnkit-temp --version 2 "$env:USERPROFILE\wsl-vpnkit-temp" "wsl-vpnkit.tar.gz" Start the network service from this temporary distro wsl.exe -d wsl-vpnkit-temp --cd /app wsl-vpnkit * VERY IMPORTANT! A new terminal will open. Don't close it, just minimize it. This window is what keeps your internet connection active. 1. Continue with the Manual: * With the connection now working thanks to the previous step, open a new terminal of your main distro (Ubuntu) and continue with Step 1 described below.
________________________________ Step 1: Installation and Script Preparation Now, within your main distro (e.g., Ubuntu), download and prepare the necessary files. 1. Create a dedicated folder: mkdir -p ~/wsl-vpnkit-script && cd ~/wsl-vpnkit-script 2. Install dependencies: sudo apt-get update && sudo apt-get install -y iproute2 iptables iputils-ping dnsutils wget 3. Download and extract wsl-vpnkit: wget https://github.com/sakai135/wsl-vpnkit/releases/download/v0.4.1/wsl-vpnkit.tar.gz tar --strip-components=1 -xf wsl-vpnkit.tar.gz app/wsl-vpnkit app/wsl-gvproxy.exe app/wsl-vm app/wsl-vpnkit.service rm wsl-vpnkit.tar.gz
________________________________ Step 2: systemd Service Creation and Configuration Configure systemd to manage the scripts as an automatic service. 1. Copy the service file to the systemd directory: sudo cp ./wsl-vpnkit.service /etc/systemd/system/ 2. Edit the service file: sudo nano /etc/systemd/system/wsl-vpnkit.service 3. Replace all the file content with the following configuration. Remember to change YOUR_USER to your Linux username. * If you're not sure of your username, you can see it in the terminal with the command: whoami [Unit] Description=wsl-vpnkit After=network.target [Service] Working directory where the scripts are located WorkingDirectory=/home/YOUR_USER/wsl-vpnkit-script Command to execute the script and its variables ExecStart=/bin/sh -c "VMEXEC_PATH=$(pwd)/wsl-vm GVPROXY_PATH=$(pwd)/wsl-gvproxy.exe ./wsl-vpnkit" Restart the service if it fails Restart=always KillMode=mixed [Install] WantedBy=multi-user.target 1. Save and Close: In nano, press Ctrl+O, Enter, and then Ctrl+X.
________________________________ Step 3: Service Activation and Management Activate the service so it starts automatically with your distro. 1. Enable automatic startup: sudo systemctl enable wsl-vpnkit 2. Start the service now: sudo systemctl start wsl-vpnkit 3. Verify it's working: systemctl status wsl-vpnkit Look for the green line that says active (running). If it appears, you have successfully completed!
________________________________ Final note: If you completed "Step Zero", once the systemd service is active (running) in your main distro, you can now close the PowerShell window of the temporary service and, if desired, remove the temporary distro with the command in PowerShell: wsl --unregister wsl-vpnkit-temp. — Reply to this email directly, view it on GitHubhttps://gist.github.com/mikegerber/91fcea262028e09b2fd0969193c6c260#gistcomment-5707302 or unsubscribehttps://github.com/notifications/unsubscribe-auth/BMFBXUOLVNJSRVSW62GVVBL3L7VDHBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTEOBWHE3DCMRYU52HE2LHM5SXFJTDOJSWC5DF. You are receiving this email because you commented on the thread. Triage notifications on the go with GitHub Mobile for iOShttps://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Androidhttps://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

"You're welcome"

@dinolupo
Copy link

Besto solution ever! Thank you!

@ddamerjian
Copy link

ddamerjian commented Oct 31, 2025 via email

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