How do I disable routing table changes in WireGuard for Windows?

Solution 1:

One of my friends helped me solve this issue with the help of zx2c4 (Jason Donenfeld) from WireGuard. I am sharing our solution here for the next person with this same problem.

WireGuard for Windows added support for Table = off in v0.3.15. This makes it so WireGuard does not update the routing table when the tunnel is opened.

At this point we have a tunnel but windows will not route traffic through it even if it is the only adapter for the application. To solve this issue we need to add a default route to the adapter with a lower priority than our regular default route.

To do this we need to first enable DangerousScriptExecution in WireGuard. To do this we need to set the Registry Key HKEY_LOCAL_MACHINE\Software\WireGuard\DangerousScriptExecution to DWORD(1) using regedit. The key does not exist by default and needs to be created using regedit. Then WireGuard needs to be restarted.

Once this is done we need to run some PowerShell commands when we start and stop the tunnel to create our default route for the tunnel. This route needs to have a higher metric/cost (lower priority) than our normal default route. For this solution we used a metric value of 95 (10 higher than the highest automatic metric).

WireGuard has the ability to automatically execute windows commands specified in the PreUp, PostUp, PreDown, and PostDown options in the tunnel config. We use this to set up our routes automatically.

WireGuard sets the environment variable WIREGUARD_TUNNEL_NAME to the name of this exact tunnel when it is running commands. We can convert it to a PowerShell object by calling $wgInterface = Get-NetAdapter -Name %WIREGUARD_TUNNEL_NAME%.

Once we have the tunnel as a PowerShell object we can set up or take down our route in windows.

To create our route we call route add 0.0.0.0 mask 0.0.0.0 0.0.0.0 IF $wgInterface.ifIndex metric 95

To remove our route we call route delete 0.0.0.0 mask 0.0.0.0 0.0.0.0 if $wgInterface.ifIndex metric 95

When we combine all of this together we end up with the following under [Interface] in the tunnel config. PostUp, PreDown, and Table = off are what this solution gives.

[Interface]
PrivateKey = <...>
Address = <...>
DNS = <...>
PostUp = powershell -command "$wgInterface = Get-NetAdapter -Name %WIREGUARD_TUNNEL_NAME%; route add 0.0.0.0 mask 0.0.0.0 0.0.0.0 IF $wgInterface.ifIndex metric 95"
PreDown = powershell -command "$wgInterface = Get-NetAdapter -Name %WIREGUARD_TUNNEL_NAME%; route delete 0.0.0.0 mask 0.0.0.0 0.0.0.0 if $wgInterface.ifIndex metric 95"
Table = off

[Peer]
PublicKey = <...>
AllowedIPs = 0.0.0.0/0,::0/0
Endpoint = <...>