RPC Endpoint Mapper and Dynamic Port Ranges: Locking Down Windows Server Firewalls Without Breaking It

Opening TCP 135 on a Windows Server firewall does not enable RPC. Port 135 is only the endpoint mapper: the client asks it which port a given interface is listening on, and the answer is a dynamically allocated port somewhere in the 49152–65535 range. Block that range and every RPC call fails with a timeout that looks exactly like a dead server. This page covers how the mapper hands out ports, the registry keys that pin the range, how to scope firewall rules so they follow the service rather than the port, and what breaks when the range is narrowed too far. The same RPC plumbing applies on any hosted guest, including the Windows Server VPS images at windows-vps.org.

Why Port 135 Alone Never Works

An RPC client performs a three-step dance. It connects to the endpoint mapper on TCP 135, sends the interface UUID and version it wants, and receives back a port number. It then opens a second connection to that port, which was chosen at boot from the dynamic range. A firewall that permits 135 and denies 49152–65535 allows the first step and blocks the second, so the error surfaces as RPC server is unavailable after a 30–60 second timeout rather than a clean refusal.

# See what the mapper hands out right now
Get-NetTCPConnection -State Listen | Where-Object {$_.LocalPort -ge 49152} |
  Sort-Object LocalPort | Select LocalAddress,LocalPort,OwningProcess |
  ForEach-Object { $_ | Add-Member -PassThru NoteProperty Proc (Get-Process -Id $_.OwningProcess).Name }

Well-known RPC services get their own reserved ports. Everything else — DCOM servers, third-party backup agents, custom service hosts — lands in the dynamic range and moves after a reboot unless it is pinned.

Service Port Notes
Endpoint mapper 135 TCP Always required, both directions for cross-server RPC
WMI / DCOM (Winmgmt) 135 + dynamic WMI over DCOM cannot be pinned; use WinRM instead
Netlogon 445, 135, 49152+ Domain join and secure channel need the dynamic range
DFS Replication 5722, 135 Plus dynamic RPC for the management interface
Hyper-V management 2179, 135, dynamic VMMS uses dynamic ports for the VMI
Backup agents (most) 135 + vendor port Check vendor docs; many also need 445

Pinning the Dynamic Range

Windows Server 2008 and later default the ephemeral range to 49152–65535. Narrowing it to a block you can firewall is done with two registry values under HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters. The format is a 32-bit value where the high 16 bits are the start port and the low 16 bits are the count.

$start = 5000; $count = 1000
$val = ($start -shl 16) -bor $count
Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' -Name TcpipDynamicPortRange -Value $val -Type DWord
# netsh equivalent, same effect, reboot required
netsh int ipv4 set dynamicport tcp start=5000 num=1000
netsh int ipv4 set dynamicport udp start=5000 num=1000
netsh int ipv4 show dynamicport tcp

A range of 1000 ports is comfortable for a single-role server. Do not go below 500: port exhaustion under load produces WSAEADDRINUSE failures in event log and TCP connect errors that have nothing to do with the firewall.

Reserving a specific port for a specific RPC interface is a separate mechanism. Each interface registers under its own key and the Ports subkey, with a Ports REG_MULTI_SZ naming the interfaces:

$k = 'HKLM:\SOFTWARE\Microsoft\Rpc\Internet'
New-Item $k -Force | Out-Null
New-ItemProperty $k -Name Ports -PropertyType MultiString -Value @('5000-5999') -Force
New-ItemProperty $k -Name PortsInternetAvailable -Value 'Y' -PropertyType String -Force
New-ItemProperty $k -Name UseInternetPorts -Value 'Y' -PropertyType String -Force

This key is the one most documentation refers to, and it must agree with the Tcpip value — a mismatch causes the service to fall back to a random port outside both ranges.

Scoping Firewall Rules to the Service, Not the Range

The cleaner approach on modern Windows is to let the firewall follow the process. A rule with -Program and a dynamic local port matches only traffic owned by that executable, which means the dynamic range can stay open on the local side without exposing every RPC server on the box.

New-NetFirewallRule -DisplayName 'RPC - Backup Agent' -Direction Inbound -Action Allow `
  -Protocol TCP -LocalPort 135,49152-65535 -RemoteAddress 10.20.0.0/24 `
  -Program 'C:\Program Files\Vendor\agent.exe' -Profile Domain,Private

New-NetFirewallRule -DisplayName 'RPC - Admin subnet' -Direction Inbound -Action Allow `
  -Protocol TCP -LocalPort 135 -RemoteAddress 10.20.0.0/24 -Profile Domain,Private

Restrict by -RemoteAddress before you restrict by port. The majority of RPC exposure on a VPS is not a port range problem, it is that 135 and 445 are reachable from the public internet. Scoping to the management subnet removes the risk without touching the range at all.

  • Use -Profile Domain,Private so the rules do not apply to a Public-classified NIC.
  • Prefer -InterfaceAlias over IP ranges when the box has one NIC and one management path.
  • Set -EdgeTraversalPolicy Block so no rule is silently opened by NAT traversal.
  • Group rules with -Group so Get-NetFirewallRule -Group can audit them as a unit.

What Breaks When the Range Is Over-Restricted

Each of these failures presents differently, and none of them names RPC in the error message.

Symptom What is actually failing Event evidence
Domain join fails with “RPC server unavailable” Netlogon secure channel needs dynamic RPC to the DC Netlogon 5719, 5783
WMI queries time out but WinRM works DCOM cannot reach the dynamic port DistributedCOM 10009
Backup job fails at “snapshot creation” VSS writer RPC call blocked VSS 8194, Application log
GPO not applying Group Policy client RPC to SYSVOL GroupPolicy 1058, 1129
Server Manager cannot add a role remotely WinRM fine, but the role install uses DCOM ServerManager 0

The domain join case is the one that locks people out of a fresh VPS rebuild: the machine can reach the DC on 389 and 445 and still fail to join because the Netlogon RPC call to the DC’s dynamic port was dropped. If you must narrow the range, keep a 2000-port block open between member servers and domain controllers and log the drops during the first reboot cycle.

Verifying What Is Actually Listening

Run the verification below before and after the change window and diff the two outputs. A port that appears in the listener list but has no matching allow rule is a service that will fail silently the next time it restarts.

Do not trust the rule list alone. Match listening sockets to processes, then match processes to rules. A listener on a port outside your allowed range is the thing to find.

# Listening RPC endpoints with owning process and command line
Get-NetTCPConnection -State Listen | Where-Object {$_.LocalPort -in 135 -or $_.LocalPort -ge 49152} |
  ForEach-Object {
    $p = Get-CimInstance Win32_Process -Filter "ProcessId=$($_.OwningProcess)"
    [pscustomobject]@{Port=$_.LocalPort; Proc=$p.Name; Path=$p.ExecutablePath}
  } | Sort-Object Port | Format-Table -AutoSize

# Which inbound rules reference each port
Get-NetFirewallPortFilter | Where-Object {$_.LocalPort -match '49152'} |
  ForEach-Object { $_ | Get-NetFirewallRule } | Select DisplayName,Enabled,Direction,Action

# Effective profile on each interface
Get-NetConnectionProfile | Select InterfaceAlias,NetworkCategory,IPv4Connectivity

Then watch the drops during a change window instead of guessing from user reports:

# Enable the firewall drop log, then tail it
netsh advfirewall set allprofiles logging droppedconnections enable
netsh advfirewall set allprofiles logging maxfilesize 8192
Get-Content "$env:SystemRoot\System32\LogFiles\Firewall\pfirewall.log" -Tail 40

Pair that with the Security log filter for 5152 (packet blocked) and 5157, and with the DistributedCOM 10009 entries that carry the CLSID and remote address. A 15-minute capture during a backup run or a GPO refresh tells you exactly which port a service needed, which is far faster than reading vendor documentation.

A Rollout Order That Does Not Lock You Out

Two additional checks belong in any change window: confirm the DNS and WMI providers still answer from the management subnet, and keep a copy of the previous rule set so a bad call can be undone in one command.

Ordering the Change Window

Change the range and the rules in this order, and keep an out-of-band console open for the whole window.

  1. Enable drop logging first, with the existing permissive rules. Collect a baseline for 24 hours.
  2. Add the new scoped rules alongside the old ones; both are active, nothing breaks.
  3. Reboot to confirm the pinned range is in effect: netsh int ipv4 show dynamicport tcp.
  4. Remove the permissive rules one group at a time, watching the drop log for 10 minutes after each.
  5. Reboot again and re-run the service checks — some RPC endpoints bind at boot before the range is read, and a second boot is where range misconfiguration shows up.

If the guest becomes unreachable during the window, the provider console is the rollback: re-enable the permissive rule set with netsh advfirewall set allprofiles state off only as a last resort, then fix and re-enable. Keeping the baseline drop log means you will know which rule to add rather than guessing. Current Windows Server builds and their default network posture are described on the Windows VPS product page.

Leave a Comment