Forwarding WireGuard Connections on Windows

WireGuard generally “just works” out-of-the-box on Windows with a Point-to-Point connection, or as a “Point” in a conventional Point-to-Site connection, or as a “Spoke” in a Hub-and-Spoke connection; and if you want to expose a network service (like a web server) running on such a point or spoke, you can do that by making one or two adjustments to its Windows Defender Firewall.

But if you want to use a Windows machine as the “Hub” in a Hub-and-Spoke connection, or to expose a “Site” in a Point-to-Site or Site-to-Site connection, you need to make further configuration changes to various Windows networking settings. This article will show you how:

Hub Forwarding

Enabling packet forwarding on a WireGuard hub is the simplest kind of forwarding to configure. For example, if you’ve set up WireGuard on a Windows machine like the “Host C” hub in the WireGuard Hub and Spoke Configuration guide:

WireGuard Hub and Spoke Example

You can enable packet forwarding by running the following PowerShell Set-NetIPInterface command as an Administrator on Host C:

PS> Set-NetIPInterface -InterfaceAlias wg0 -Forwarding Enabled

The above command enables both IPv4 and IPv6 forwarding. Alternatively, you could enable each version separately by running the following netsh commands:

> netsh interface ipv4 set interface wg0 forwarding=enabled
> netsh interface ipv6 set interface wg0 forwarding=enabled

Both sets of commands above are the equivalent of turning on the IP forwarding kernel parameters on a Linux machine:

# sysctl -w net.ipv4.conf.wg0.forwarding=1
# sysctl -w net.ipv6.conf.wg0.forwarding=1

To check the current forwarding state of each network interface on a Windows machine, run the following PowerShell command:

PS> Get-NetIPInterface | Select IfIndex,InterfaceAlias,AddressFamily,ConnectionState,Forwarding | Sort-Object -Property IfIndex | Format-Table

IfIndex InterfaceAlias              AddressFamily ConnectionState Forwarding
------- --------------              ------------- --------------- ----------
      1 Loopback Pseudo-Interface 1          IPv4       Connected   Disabled
      1 Loopback Pseudo-Interface 1          IPv6       Connected   Disabled
      7 Ethernet                             IPv6       Connected   Disabled
      7 Ethernet                             IPv4       Connected   Disabled
     15 wg0                                  IPv6       Connected    Enabled
     15 wg0                                  IPv4       Connected    Enabled

Site Gateway

Enabling packet forwarding for a WireGuard site gateway is only a little more complicated than for a hub. You need to run the same commands on the site gateway as on the hub — but for both the WireGuard interface (wg0) and the site-facing Ethernet adapter (usually named Ethernet):

PS> Set-NetIPInterface -InterfaceAlias wg0 -Forwarding Enabled
PS> Set-NetIPInterface -InterfaceAlias Ethernet -Forwarding Enabled
PS> Get-NetIPInterface | Select IfIndex,InterfaceAlias,AddressFamily,ConnectionState,Forwarding | Sort-Object -Property IfIndex | Format-Table

IfIndex InterfaceAlias              AddressFamily ConnectionState Forwarding
------- --------------              ------------- --------------- ----------
      1 Loopback Pseudo-Interface 1          IPv4       Connected   Disabled
      1 Loopback Pseudo-Interface 1          IPv6       Connected   Disabled
      7 Ethernet                             IPv6       Connected    Enabled
      7 Ethernet                             IPv4       Connected    Enabled
     15 wg0                                  IPv6       Connected    Enabled
     15 wg0                                  IPv4       Connected    Enabled

Run these commands on the WireGuard host for both sites in a Site-to-Site connection (eg both “Host α” and “Host β” in the WireGuard Site to Site Configuration guide), or on the site host of a Point-to-Site connection (eg “Host β” in the WireGuard Point to Site With a Site Gateway guide).

Note that you will still have to update the routing tables on the LAN router (or each individual computer you want to expose) at the site in order for traffic from the other computers at the site to actually be routed to the WireGuard site host. For example, in the WireGuard Point to Site With a Site Gateway guide, the WireGuard network is using the 10.0.0.0/24 address block, and the WireGuard site host (Host β) has an address of 192.168.200.2 on the LAN:

WireGuard Site Gateway Example

So on the LAN router at Site B, you would have to add a route like the following:

# ip route add 10.0.0.0/24 via 192.168.200.2 dev eth1

Point to Site Masquerading

Setting up masquerading (aka Source Network Address Translation or SNAT) on a WireGuard site host for a Point-to-Site connection is only a little more complicated. You simply have to use the New-NetNat command and specify the address block of source addresses that should be masqueraded.

For example, if you’ve set up WireGuard on a Windows machine to be a site host like “Host β” in the WireGuard Point to Site Configuration guide, 10.0.0.0/24 is the address block of WireGuard addresses that the site host should masquerade to the site LAN:

WireGuard Point-to-Site Example

To enable this, run the following PowerShell command as an Administrator on Host β:

PS> New-NetNat -Name wg0nat -InternalIPInterfaceAddressPrefix 10.0.0.0/24

Unlike with Linux, you do not have to separately enable packet forwarding; Windows will automatically allow forwarding from any source addresses in the specified address block (such as 10.0.0.1, the WireGuard “Point” in the Point to Site Configuration example), and will automatically rewrite the source address of those forwarded connections to use the address of whatever interface the connection is forwarded out (such as 192.168.200.2, the LAN address of Host β above).

Run the following PowerShell command to see the current list of SNAT configurations:

PS> Get-NetNat

Name                             : wg0nat
ExternalIPInterfaceAddressPrefix :
InternalIPInterfaceAddressPrefix : 10.0.0.0/24
IcmpQueryTimeout                 : 30
TcpEstablishedConnectionTimeout  : 1800
TcpTransientConnectionTimeout    : 120
TcpFilteringBehavior             : AddressDependentFiltering
UdpFilteringBehavior             : AddressDependentFiltering
UdpIdleSessionTimeout            : 120
UdpInboundRefresh                : False
Store                            : Local
Active                           : True

Note that the wg0nat name in the above example is completely arbitrary — it doesn’t have to include the WireGuard interface name. It’s relevant only for the purpose of updating or deleting existing SNAT configurations; for example, run the following command to remove the SNAT added above:

PS> Remove-NetNat -Name wg0nat -Confirm:$false

Point to Site Port Forwarding

Enabling port forwarding (aka Destination Network Address Translation or DNAT) on a WireGuard host takes a few more steps. The first step is to determine the original destination address and port that will be used by incoming packets which should be forwarded, and the destination address and port to which those packets should be rewritten.

With the example from the WireGuard Point to Site With Port Forwarding guide, “Host β” should forward incoming TCP port 80 packets from its site LAN, using its LAN address of 192.168.200.2, to TCP port 80 of WireGuard “Endpoint A”, which has an IP address of 10.0.0.1 on the WireGuard network:

WireGuard Port Forwarding Example

To set up this DNAT, first run the following netsh command as Administrator on Host β:

> netsh interface portproxy add v4tov4 listenaddress=192.168.200.2 listenport=80 connectaddress=10.0.0.1 connectport=80

For IPv6 addresses, use the v6tov6 subcommand instead of v4tov4. Unlike with Linux, you do not need to separately enable packet forwarding, and you do not need to separately enable SNAT — connections that match this DNAT rule will automatically be forwarded, and have their source address translated to match the interface out of which they’ll be forwarded (10.0.0.2 in this example).

Next, add a firewall rule on Host β with the New-NetFirewallRule PowerShell command to allow the incoming connection to be accepted before it is translated:

PS> New-NetFirewallRule `
    -Name wg0http `
    -DisplayName "HTTP-In wg0" `
    -Group "Custom" `
    -Enabled True `
    -Direction Inbound `
    -Action Allow `
    -Protocol TCP `
    -LocalPort 80

The above firewall rule will allow connections to TCP port 80 from anywhere. You might, however, want add a -RemoteAddress option to constrain the source machines from which Host β will forward; for example, you might add the following -RemoteAddress 192.168.200.0/24 option to forward connections only from Host β’s own LAN:

PS> New-NetFirewallRule `
    -Name wg0http `
    -DisplayName "HTTP-In wg0" `
    -Group "Custom" `
    -Enabled True `
    -Direction Inbound `
    -Action Allow `
    -Protocol TCP `
    -LocalPort 80 `
    -RemoteAddress 192.168.200.0/24

The combination of the above port-proxy command and firewall rule will allow the example Host β to forward HTTP connections from its site LAN through WireGuard to Endpoint A.

To list the active port-proxies, run the following command:

> netsh interface portproxy show all

Listen on ipv4:             Connect to ipv4:

Address         Port        Address         Port
--------------- ----------  --------------- ----------
192.168.200.2   80          10.0.0.1        80

To delete the port-proxy set up above, run the following command with the listen address and port to delete:

> netsh interface portproxy delete v4tov4 listenaddress=192.168.200.2 listenport=80

And to delete the firewall rule set up above, run the following PowerShell command with the name of the rule to delete:

PS> Remove-NetFirewallRule -Name wg0http