WireGuard and Windows Defender Firewall

If you set up WireGuard on a machine running Microsoft Windows, you should be able to access remote servers from that machine through WireGuard, without making any changes to that machine’s firewall. However, to allow other endpoints in the WireGuard network to access that Windows machine — for example, to access a fileshare, or simply to “ping” it — you usually will have to adjust that machine’s Windows Defender Firewall (WDF).

This guide will show you how to adjust the firewall settings on Windows to allow remote access from a WireGuard network, including:

This guide assumes you have already have set up a working WireGuard connection on the Windows machine. If not, consult one of the following tutorials:

Also note that you will have to run most of the example commands in this guide as an Administrator user.

Setting Firewall Profiles

Each network interface on a Windows machine belongs to one of the three following network categories (aka network profiles, aka firewall profiles):

Public

an untrusted network, such as the Wi-Fi at a coffee shop

Private

a trusted network, such as your home Local Area Network (LAN)

DomainAuthenticated

(aka Domain) a network on which the machine is authenticated with an Active Directory (AD) Domain Controller (DC)

When you add a new network interface, the Windows Network Location Awareness (NLA) service will probe it to determine whether it can connect to a local AD DC, and to certain public Internet services. If it finds a DC, it will categorize the network interface as Domain; otherwise, most of the time it will categorize the interface as Public.

Most preset WDF rules apply to a specific firewall profile; for example, Windows 10 and 11 have separate “File and Printer Sharing” preset rules for Public, Private, and Domain profiles — and only the Private profile rules are enabled by default.

So the simplest way to enable inbound access to many common network services (like filesharing or ping) on a Windows computer through WireGuard is to change the firewall profile of the WireGuard interface from Public to Private. There are several ways to do this (see the Set Network Location article from the Windows Ten Forums for a full walkthrough of the various options), but a convenient way is through the Set-NetConnectionProfile PowerShell command:

PS C:\> Get-NetConnectionProfile -InterfaceAlias wg0


Name             : wg0 3
InterfaceAlias   : wg0
InterfaceIndex   : 15
NetworkCategory  : Public
IPv4Connectivity : NoTraffic
IPv6Connectivity : NoTraffic



PS C:\> Set-NetConnectionProfile -InterfaceAlias wg0 -NetworkCategory Private

The above commands will display and then set the firewall profile of the WireGuard interface wg0. This setting is persisted in the Windows registry, under a key that includes the unique GUID for the interface:

C:\> reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles" /s /f wg0

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles\{3BA24925-42E0-4035-94DD-B563DBA69EB5}
    ProfileName    REG_SZ    wg0 3
    Description    REG_SZ    wg0

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles\{D5239A1F-B71E-4EF8-8EA6-C6A9CD1BDB9C}
    ProfileName    REG_SZ    wg0
    Description    REG_SZ    wg0

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles\{F7B2DF09-72C4-4E15-B3B8-BD531309A1DB}
    ProfileName    REG_SZ    wg0 2
    Description    REG_SZ    wg0

End of search: 6 match(es) found.

C:\> reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles\{4EF39DC0-8569-417D-801E-8C582B9D8565}" /v Category

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles\{4EF39DC0-8569-417D-801E-8C582B9D8565}
    Category    REG_DWORD    0x1

The Category field will be 0x0 for Public profiles, 0x1 for Private profiles, and 0x2 for Domain profiles.

In the above example, note that there are several different entries for the WireGuard wg0 interface (named wg0, wg0 2, and wg0 3). This is because every time any of the following changes are made to a WireGuard interface, a new interface GUID will be generated for the interface:

  • The interface name changes

  • A peer is added or removed

  • The AllowedIPs setting of any peer is changed

Beware that when this happens, the Windows NLA service will try to re-categorize the interface — and any profile setting you’ve made to a previous version of the interface will be ignored.

By PostUp Script

A convenient way to make sure that the current version of any WireGuard interface you’re using has the Private firewall profile set is to run the above Set-NetConnectionProfile command as a PostUp script. However, before doing so, you may have to take the following steps:

First, enable the execution of Pre/Post/Up/Down WireGuard scripts in general, by setting the following registry key:

C:\> reg add HKEY_LOCAL_MACHINE\SOFTWARE\WireGuard /v DangerousScriptExecution /t REG_DWORD /d 1 /f

Next, allow the unrestricted execution of PowerShell scripts in general:

PS C:\> Set-ExecutionPolicy -ExecutionPolicy Unrestricted

Finally, edit the WireGuard tunnel to add the following PostUp script (to the [Interface] section of the configuration):

PostUp = powershell -Command "Set-NetConnectionProfile -InterfaceAlias wg0 -NetworkCategory Private"

The command execution will be recorded in WireGuard’s log, like the following:

2024-06-16 01:40.14.042: [TUN] [wg0] Executing: `powershell -Command "Set-NetConnectionProfile -InterfaceAlias wg0 -NetworkCategory Private"`

If the command has any output, its output will be displayed as additional entries. And if the command fails, the interface will fail to start up, with a log entry like the following:

2024-06-16 01:40:17.423: [TUN] [wg0] An error occurred while running a configuration script command: A generic command executable returned a result that indicates failure.

To Domain Authenticated

The Set-NetConnectionProfile command only works to switch the firewall profile between Public and Private — it does not allow a profile to be changed to (or from) Domain. If an AD DC is serving a domain on a WireGuard network, the NLA service on other Windows machines that connect to that network should automatically detect this, and set their WireGuard interface profile to Domain.

However, sometimes the NLA service doesn’t automaticaly switch the WireGuard interface to Domain; this is a known NLA issue. The most reliable fix for it, when it happens, is to toggle the WireGuard interface disabled, and then re-enable it again:

C:\> netsh interface set interface wg0 disabled
C:\> netsh interface set interface wg0 enabled

This will trigger the NLA service to re-probe for an AC DC, an re-try authentication.

Also, setting the negative cache period (the number of seconds a “miss” is cached) for the Netlogon service to 0 can help avoid triggering this issue:

C:\> reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters /v NegativeCachePeriod /t REG_DWORD /d 0 /f

Doing the same for the general DNS cache may also help:

C:\> reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters /v MaxNegativeCacheTtl /t REG_DWORD /d 0 /f

On a Domain Controller

On an AD DC, the DC should automatically start serving the domain on its WireGuard interface whenever you start up the tunnel. However, if you find that the DC doesn’t automatically start serving the domain, you may have to edit the registry setting for its interface profile (identifying the GUID for the active version of the interface as shown earlier), and change its Category field to 2 (Domain):

C:\> reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles\{4EF39DC0-8569-417D-801E-8C582B9D8565}" /v Category /t REG_DWORD /d 2 /f

Then reboot the DC.

Enabling Preset Firewall Rules

Once you have adjusted the WireGuard interface’s firewall profile to the value you want (Public, Private, or Domain), open up the Windows Defender Firewall with Advanced Security (WFAS) application. This will allow you to view and enable a variety of preset firewall rules that allow inbound access to the network services on the Windows machine:

Screenshot of WFAS Overview
Figure 1. Windows Defender Firewall with Advanced Security

By default, all inbound connections (connections initiated from another computer to this Windows machine) are blocked, but all outbound connections (connections initiated from this Windows machine to another computer) are allowed. Click the Inbound Rules item in its left-side panel. This lists all inbound preset rules, with a green checkmark next to the enabled rule. Most of these rules enable access to a specific network service for a specific firewall profile:

Screenshot of WFAS Inbound Rules
Figure 2. Inbound Rules

To enable a preset rule, right-click the rule, and select the Enable Rule menu item. Make sure you select the rule that corresponds to the firewall profile you want to allow — most preset rules have separate, otherwise-identical rules for each of the Public, Private, and Domain profiles; although some rules apply to several profiles (like both Public and Private, or both Private and Domain), or to all of them.

Changes will take effect immediately. A few common rules you may wish to enable (if not already enabled) are:

File and Printer Sharing (Echo Request - ICMPv4-In)

Allows the machine to respond to ping requests made to its IPv4 addresses. (An alternative rule that does the same thing is Core Networking Diagnostics - ICMP Echo Request (ICMPv4-In).)

File and Printer Sharing (Echo Request - ICMPv6-In)

Allows the machine to respond to ping requests made to its IPv6 addresses. (An alternative rule that does the same thing is Core Networking Diagnostics - ICMP Echo Request (ICMPv6-In).)

File and Printer Sharing (SMB-In)

Allows the machine to serve fileshares.

Remote Desktop - User Mode (TCP-In)

Allows RDP (Remote Desktop Protocol) connections to the machine. (The TCP version of this rule is required for RDP access; you may also want to enable the Remote Desktop - User Mode (UDP-In) rule for improved network performance in some situations.)

Windows Remote Management (HTTP-In)

Allows the execution of PowerShell commands on the machine from a remote computer (aka PowerShell remoting).

Adding Custom Firewall Rules

If you don’t find preset rules that match what you need, you can create custom firewall rules. Doing so via the New-NetFirewallRule command enables you to do so with some options that aren’t readily available through the WFAS UI, such as interface-specific rules.

One thing to keep in mind with WDF rules, however, is that, unlike other firewall systems, WDF rules do not support fine-grained ordering. “Block” rules always take precedence over “Allow” rules; so in practice, custom Block rules are rare — usually you keep in place the profile’s default setting of blocking all inbound connections that don’t match any rules, and then add discrete Allow rules to match each specific kind of connection that you do want to allow.

By Profile

If you move a Windows machine’s WireGuard interface to the Private profile, you may want to add some custom rules that apply to just the Private profile. For example, if you’ve disabled the preset rule that allows RDP access for all profiles, you might add the following rule to allow RDP access only to interfaces in the Private profile:

PS C:\> New-NetFirewallRule `
    -Name private3389tcp `
    -DisplayName "Remote Desktop TCP-In Private" `
    -Group "Custom" `
    -Enabled True `
    -Profile Private `
    -Program C:\Windows\system32\svchost.exe `
    -Direction Inbound `
    -Action Allow `
    -Protocol TCP `
    -LocalPort 3389

When you add new rules, use the -DisplayName and -Group fields to set descriptive names that will allow you to identify your custom rules in the WFAS UI; and use the -Name option to set a short name that will make it easy to update or remove the rule through PowerShell. For example, to remove the above rule, you can simply run the following command:

PS C:\> Remove-NetFirewallRule -Name private3389tcp

By Interface

For the tightest security, you may want to add some custom rules that allow access to network services exclusively through the machine’s WireGuard interface. For example, you might add the following rule to allow access to a custom web app listening on TCP port 8080 only through its WireGuard interface (where its WireGuard tunnel name is wg0):

PS C:\> New-NetFirewallRule `
    -Name wg0tcp8080 `
    -DisplayName "Web App 8080-In wg0" `
    -Group "Custom" `
    -Enabled True `
    -Direction Inbound `
    -Action Allow `
    -Protocol TCP `
    -LocalPort 8080 `
    -InterfaceAlias wg0

Following are a set of rules that allow access to common network services through the wg0 WireGuard interface:

# allow IPv4 ping
New-NetFirewallRule `
    -Name wg0ping4 `
    -DisplayName "Echo Request ICMPv4-In wg0" `
    -Group "Custom" `
    -Enabled True `
    -Program System `
    -Direction Inbound `
    -Action Allow `
    -Protocol ICMPv4 `
    -IcmpType 8 `
    -InterfaceAlias wg0

# allow IPv6 ping
New-NetFirewallRule `
    -Name wg0ping6 `
    -DisplayName "Echo Request ICMPv6-In wg0" `
    -Group "Custom" `
    -Enabled True `
    -Program System `
    -Direction Inbound `
    -Action Allow `
    -Protocol ICMPv6 `
    -IcmpType 128 `
    -InterfaceAlias wg0

# allow filesharing
New-NetFirewallRule `
    -Name wg0smb `
    -DisplayName "File and Printer Sharing SMB-In wg0" `
    -Group "Custom" `
    -Enabled True `
    -Program System `
    -Direction Inbound `
    -Action Allow `
    -Protocol TCP `
    -LocalPort 445 `
    -InterfaceAlias wg0

# allow RDP
New-NetFirewallRule `
    -Name wg0rdptcp `
    -DisplayName "Remote Desktop TCP-In wg0" `
    -Group "Custom" `
    -Enabled True `
    -Program C:\Windows\system32\svchost.exe `
    -Direction Inbound `
    -Action Allow `
    -Protocol TCP `
    -LocalPort 3389 `
    -InterfaceAlias wg0

# also allow RDP optimizations through UDP
New-NetFirewallRule `
    -Name wg0rdpudp `
    -DisplayName "Remote Desktop UDP-In wg0" `
    -Group "Custom" `
    -Enabled True `
    -Program C:\Windows\system32\svchost.exe `
    -Direction Inbound `
    -Action Allow `
    -Protocol UDP `
    -LocalPort 3389 `
    -InterfaceAlias wg0

# allow powershell remoting
New-NetFirewallRule `
    -Name wg0pwsh `
    -DisplayName "Remote Management PowerShell-In wg0" `
    -Group "Custom" `
    -Enabled True `
    -Program System `
    -Direction Inbound `
    -Action Allow `
    -Protocol TCP `
    -LocalPort 5985 `
    -InterfaceAlias wg0

For Full Access

If you want to allow unrestricted network access to the machine through the WireGuard tunnel, you can do so with the following command:

PS C:\> New-NetFirewallRule `
    -Name wg0all `
    -DisplayName "All-In wg0" `
    -Group "Custom" `
    -Enabled True `
    -Direction Inbound `
    -Action Allow `
    -InterfaceAlias wg0

This may be useful temporarily when setting up a new system, before you’ve figured out which specific ports and protocols need to be exposed. The best practice for security, however, is to inventory the (usually very short) list of network services running on the machine to which remote machines will actually need access, and add a rule for each individually.

Listing Rules

Viewing the details of WDF rules can be a little cumbersome — the Get-NetFirewallRule command only lists rules’ basic information:

PS C:\> Get-NetFirewallRule -Name wg0tcp8080


Name                          : wg0tcp8080
DisplayName                   : Web App 8080-In wg0
Description                   :
DisplayGroup                  : Custom
Group                         : Custom
Enabled                       : True
Profile                       : Any
Platform                      : {}
Direction                     : Inbound
Action                        : Allow
EdgeTraversalPolicy           : Block
LooseSourceMapping            : False
LocalOnlyMapping              : False
Owner                         :
PrimaryStatus                 : OK
Status                        : The rule was parsed successfully from the store. (65536)
EnforcementStatus             : NotApplicable
PolicyStoreSource             : PersistentStore
PolicyStoreSourceType         : Local
RemoteDynamicKeywordAddresses :
PolicyAppId                   :

To see the full details, you need to run several additional filter commands:

PS C:\> Get-NetFirewallRule -Name wg0tcp8080 | Get-NetFirewallAddressFilter


LocalAddress  : Any
RemoteAddress : Any



PS C:\> Get-NetFirewallRule -Name wg0tcp8080 | Get-NetFirewallApplicationFilter


Program : Any
Package :



PS C:\> Get-NetFirewallRule -Name wg0tcp8080 | Get-NetFirewallInterfaceFilter


InterfaceAlias : wg0



PS C:\> Get-NetFirewallRule -Name wg0tcp8080 | Get-NetFirewallInterfaceTypeFilter


InterfaceType : Any



PS C:\> Get-NetFirewallRule -Name wg0tcp8080 | Get-NetFirewallPortFilter


Protocol      : TCP
LocalPort     : 8080
RemotePort    : Any
IcmpType      : Any
DynamicTarget : Any



PS C:\> Get-NetFirewallRule -Name wg0tcp8080 | Get-NetFirewallSecurityFilter


Authentication     : NotRequired
Encryption         : NotRequired
OverrideBlockRules : False
LocalUser          : Any
RemoteUser         : Any
RemoteMachine      : Any



PS C:\> Get-NetFirewallRule -Name wg0tcp8080 | Get-NetFirewallServiceFilter


Service : Any

To filter by a rule’s extended details, start with one of the filters, and then use the Get-NetFirewallRule command (or other filters) to include other rule fields in the result list. For example, the following command will list the custom rules you’ve added that apply specifically to the wg0 interface:

PS C:\> Get-NetFirewallInterfaceFilter |
    Where-Object -FilterScript { $_.InterfaceAlias -Eq "wg0" } |
    Get-NetFirewallRule |
    Format-Table -Property Name, DisplayName

Name       DisplayName
----       -----------
wg0tcp8080 Web App 8080-In wg0
wg0ping4   Echo Request ICMPv4-In wg0
wg0ping6   Echo Request ICMPv6-In wg0
wg0smb     File and Printer Sharing SMB-In wg0
wg0rdptcp  Remote Desktop TCP-In wg0
wg0rdpudp  Remote Desktop UDP-In wg0
wg0pwsh    Remote Management PowerShell-In wg0