RDP Between Offices Without Open Ports: The Safe “RDP Only via VPN” Setup

Was this helpful?

You’ve got two offices, a handful of Windows servers, and a business requirement that translates to: “I need RDP to work, always.”
The last thing you need is TCP/3389 sitting on the public internet like a free buffet for botnets. Yet people still do it. Then they act surprised when their login page gets more visitors than the company website.

This guide is the sane middle path: keep RDP private, reachable only through a VPN, and operationally boring.
You’ll get a concrete setup, hardening rules that actually matter, and a fast diagnosis playbook for when “RDP is slow” becomes your new ringtone.

What “RDP only via VPN” really means

“RDP only via VPN” is not a vibe. It’s a concrete set of controls:

  • No public exposure of RDP. Not “security by obscurity” with a different port. Not “only whitelisted IPs” on the WAN. Just no.
  • RDP listens only on internal interfaces (or, more practically, firewall rules enforce that it’s reachable only from the VPN subnet).
  • VPN is the only ingress path from Office A to Office B for management traffic.
  • Identity and device controls on the VPN, ideally MFA and certificate-bound devices.
  • Logging you can use. Not a checkbox. Actual telemetry that tells you who connected, from where, and whether it failed.

Why “just open 3389” is professionally embarrassing

RDP is a high-value target. It’s not personal; it’s economics. Attackers like RDP because it’s ubiquitous, it yields interactive access, and it often leads directly to credential harvesting or ransomware.
Exposing it publicly means your defenses need to be perfect every day. Your adversary only needs you to be tired once.

Threat model in one paragraph

The main risks are credential stuffing, brute force attempts, exploitation of RDP-related flaws, lateral movement once inside, and plain old misconfiguration.
The VPN doesn’t make RDP “secure.” It makes RDP private, then you harden RDP and the surrounding network so compromise doesn’t become a company-wide event.

One quote worth keeping on a sticky note:
“Hope is not a strategy.” — Gen. Gordon R. Sullivan

Joke #1: Exposing 3389 to the internet is like leaving your office door open because you installed a better doormat. The burglars love the upgrade.

Interesting facts and a little history

Knowing the context helps you make better trade-offs. Here are some concrete points that tend to surprise people even in IT teams:

  1. RDP has been around since the late 1990s. It grew out of Microsoft’s Terminal Services era, long before “zero trust” became a slide deck staple.
  2. TCP/3389 became a scanning magnet because it’s predictable. Attack automation thrives on defaults. A lot of internet “background radiation” is just bots knocking on that door.
  3. Network Level Authentication (NLA) changed the game. NLA forces authentication before a full desktop session is created, reducing resource abuse and some classes of pre-auth attack surface.
  4. Credential theft, not protocol bugs, is the usual path. In many real incidents, RDP is simply the convenient entry point after passwords are guessed or reused.
  5. RDP “security” and “usability” fight constantly. Turning off NLA or allowing older TLS settings often happens because “one vendor app won’t work,” and that’s how the rot starts.
  6. VPNs shifted from hardware appliances to software-defined tunnels. IPsec boxes used to be the default; now WireGuard and modern IKEv2 stacks are common because ops teams want simpler, observable configurations.
  7. “Changing the RDP port” is not real security. It reduces random noise, not determined attackers, and it can break tooling and audits.
  8. RDP performance depends heavily on latency and packet loss. Bandwidth matters, but 30–60 ms latency and small loss rates can make the session feel like typing through pudding.

Architecture choices: site-to-site vs remote access

Option A: Site-to-site VPN between offices (recommended for “office to office” RDP)

If the requirement is “from Office A to servers in Office B,” a site-to-site VPN is the cleanest. It creates a controlled, private routing domain between the two networks.
You can then allow RDP only from specific source subnets or jump hosts over that VPN.

You get central control, fewer moving parts on endpoints, and predictable routing. You also get the responsibility to keep routing tight so your VPN doesn’t accidentally become a flat network that allows everything everywhere.

Option B: Remote-access VPN for individual users

This is “user laptops connect to HQ.” It’s often necessary, but it changes your problem.
Now you’re validating device posture, dealing with split-tunnel debates, and handling roaming networks. Not bad, just different.

Option C: RDP Gateway (useful, but not “no open ports”)

RDP Gateway wraps RDP in HTTPS and centralizes access. It can be done securely, but it typically requires exposing 443 (at least) to the internet.
If your requirement is explicitly “no open inbound ports,” RDP Gateway is not your primary tool. It’s a different architecture.

Opinionated recommendation

For “between offices,” build a site-to-site VPN and enforce “RDP only from the VPN subnet” on the Windows firewalls.
Add a jump host (or better, two) and make admins RDP to the jump host, then onward. That’s how you keep RDP reachable while keeping blast radius small.

Recommended baseline: WireGuard site-to-site with strict RDP allowlists

Reference network layout

  • Office A LAN: 10.10.0.0/16
  • Office B LAN: 10.20.0.0/16
  • WireGuard tunnel subnet: 10.99.0.0/24
  • Office A VPN gateway: office-a-vpn (Linux)
  • Office B VPN gateway: office-b-vpn (Linux)
  • Windows jump host in Office B: jump-b01 (10.20.10.10)
  • RDP targets in Office B: DCs/app servers as needed

Routing strategy that won’t bite you later

Keep the tunnel narrow. Route only what you need. Avoid “0.0.0.0/0 through the tunnel” for site-to-site unless you enjoy explaining to finance why Teams calls started sounding like a submarine.

The clean model:

  • Office A routes 10.20.0.0/16 via the tunnel.
  • Office B routes 10.10.0.0/16 via the tunnel.
  • Windows firewall in Office B allows TCP/3389 only from 10.10.0.0/16 (or, better, only from Office A jump subnet).

NAT: avoid it unless you must

NAT inside a site-to-site VPN is how you create future mysteries. It can be required if subnets overlap (more on that later), but if you can avoid it, do.
Routed VPNs with distinct subnets are easier to reason about, easier to debug, and easier to audit.

WireGuard configuration skeleton

WireGuard is simple on purpose. That’s a feature. Most failures are not in WireGuard; they’re in routing and firewalling around it.

cr0x@server:~$ sudo cat /etc/wireguard/wg0.conf
[Interface]
Address = 10.99.0.1/24
ListenPort = 51820
PrivateKey = <redacted>

# Route Office B LAN through the tunnel
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT

[Peer]
PublicKey = <redacted>
AllowedIPs = 10.99.0.2/32, 10.20.0.0/16
PersistentKeepalive = 25

Note what matters: AllowedIPs is both routing and policy. If you accidentally allow 0.0.0.0/0, you just told your gateway to accept “everything” from that peer.
That might be fine in a lab. In production, it’s an incident waiting for a bored intern with Wireshark.

Hardening RDP: the parts that stop incidents

1) Restrict who can reach RDP (network controls)

Do not rely on “strong passwords” as your first barrier. Your first barrier is: only the VPN subnet (or a jump host) can even initiate a TCP handshake.
RDP that can’t be reached can’t be brute-forced.

2) Require NLA and modern encryption

Enable NLA. Enforce TLS. Disable legacy security layers if you can. Yes, you’ll find one ancient vendor product that whines. Your job is to make that their problem, not your breach.

3) Use jump hosts, not “RDP everywhere”

Create a management enclave: one or two hardened Windows hosts that are the only systems allowed to RDP to servers.
Admins RDP to the jump host via VPN, then RDP from jump host to internal targets.

This reduces credential exposure (fewer endpoints touching domain admin contexts) and makes logging more concentrated and useful.

4) Identity: least privilege, MFA, and sane admin workflows

MFA belongs on the VPN. If your VPN can’t do MFA, replace it or put an identity-aware front door in front of it.
On Windows, use separate admin accounts, remove unnecessary membership from local Administrators, and treat RDP access as a controlled permission, not a birthright.

5) Logging and alerting: decide what you will investigate

You want to know:

  • VPN peer up/down events and handshake churn.
  • Windows successful and failed logons via RDP (logon type 10).
  • Firewall denies for 3389 (so you can see misroutes).

Keep it actionable. Logging everything without retention and review is just expensive forgetfulness.

Hands-on tasks: commands, outputs, and decisions

Below are practical tasks you can run on Linux VPN gateways and Windows hosts to validate “RDP only via VPN,” diagnose slowness, and prove to yourself that you didn’t accidentally build a side-door.
Each task includes: command, sample output, what it means, and the decision you make from it.

Task 1: Confirm WireGuard interface state

cr0x@server:~$ sudo wg show
interface: wg0
  public key: qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq=
  listening port: 51820

peer: ppppppppppppppppppppppppppppppppppppppppp=
  endpoint: 203.0.113.20:51820
  allowed ips: 10.99.0.2/32, 10.20.0.0/16
  latest handshake: 34 seconds ago
  transfer: 1.28 GiB received, 2.02 GiB sent
  persistent keepalive: every 25 seconds

Meaning: Handshake is fresh; routing scope is limited to tunnel IP + Office B LAN. Transfer counters show real traffic.

Decision: If “latest handshake” is minutes/hours ago, fix reachability (UDP, NAT, endpoint IP) before touching RDP.

Task 2: Validate kernel routes for remote LAN

cr0x@server:~$ ip route show | grep 10.20.0.0
10.20.0.0/16 dev wg0 proto static scope link

Meaning: The gateway will send Office B traffic into the tunnel, not to the default gateway.

Decision: If this route is missing, RDP will never arrive. Fix WireGuard AllowedIPs or add explicit routes.

Task 3: Confirm forwarding is enabled (site-to-site requires it)

cr0x@server:~$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

Meaning: The Linux host will route packets between interfaces.

Decision: If it’s 0, enable forwarding; otherwise your VPN is an expensive decoration.

Task 4: Check firewall rules on the VPN gateway (forwarding path)

cr0x@server:~$ sudo iptables -S FORWARD
-P FORWARD DROP
-A FORWARD -i wg0 -o eth0 -d 10.20.0.0/16 -j ACCEPT
-A FORWARD -i eth0 -o wg0 -s 10.20.0.0/16 -j ACCEPT

Meaning: Default drop with explicit allow between wg0 and LAN. This is what “tight routing” looks like.

Decision: If FORWARD is default DROP without the allow rules, your handshake will be up but packets won’t pass. Add specific rules, not “ACCEPT all.”

Task 5: Verify NAT is NOT happening (unless you intended it)

cr0x@server:~$ sudo iptables -t nat -S | head
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT

Meaning: No NAT rules present. Good for clean routing and accurate logs.

Decision: If you see MASQUERADE rules for VPN traffic, confirm it’s required (overlapping subnets). If not required, remove it to avoid “who is this IP” confusion later.

Task 6: Test reachability to the RDP target port from Office A side

cr0x@server:~$ nc -vz 10.20.10.10 3389
Connection to 10.20.10.10 3389 port [tcp/ms-wbt-server] succeeded!

Meaning: The network path and Windows firewall permit TCP/3389 from your source.

Decision: If it times out, it’s routing/firewall. If it’s “refused,” the host is reachable but RDP service is down or blocked locally.

Task 7: Confirm RDP is not reachable from the public internet (negative test)

cr0x@server:~$ nc -vz 198.51.100.45 3389
nc: connect to 198.51.100.45 port 3389 (tcp) failed: Connection timed out

Meaning: Good. Public IP doesn’t answer on 3389. Timeouts are usually preferable to “refused” because it reveals less.

Decision: If it succeeds, stop. You have exposed RDP. Close it at the edge firewall and verify no port-forwarding or cloud security group rule is leaking it.

Task 8: Measure latency and loss over the tunnel (RDP feel depends on this)

cr0x@server:~$ ping -c 10 10.20.10.10
PING 10.20.10.10 (10.20.10.10) 56(84) bytes of data.
64 bytes from 10.20.10.10: icmp_seq=1 ttl=127 time=23.9 ms
64 bytes from 10.20.10.10: icmp_seq=2 ttl=127 time=24.1 ms
64 bytes from 10.20.10.10: icmp_seq=3 ttl=127 time=24.3 ms
64 bytes from 10.20.10.10: icmp_seq=4 ttl=127 time=24.0 ms
64 bytes from 10.20.10.10: icmp_seq=5 ttl=127 time=26.8 ms
64 bytes from 10.20.10.10: icmp_seq=6 ttl=127 time=24.2 ms
64 bytes from 10.20.10.10: icmp_seq=7 ttl=127 time=24.1 ms
64 bytes from 10.20.10.10: icmp_seq=8 ttl=127 time=24.3 ms
64 bytes from 10.20.10.10: icmp_seq=9 ttl=127 time=24.0 ms
64 bytes from 10.20.10.10: icmp_seq=10 ttl=127 time=24.4 ms

--- 10.20.10.10 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9018ms
rtt min/avg/max/mdev = 23.9/24.6/26.8/0.8 ms

Meaning: 25 ms with 0% loss will usually feel fine for RDP.

Decision: If you see loss > 1% or jitter spikes, fix the network before blaming RDP settings.

Task 9: Check MTU-related pain (common with VPNs)

cr0x@server:~$ ping -c 3 -M do -s 1372 10.20.10.10
PING 10.20.10.10 (10.20.10.10) 1372(1400) bytes of data.
1372 bytes from 10.20.10.10: icmp_seq=1 ttl=127 time=25.1 ms
1372 bytes from 10.20.10.10: icmp_seq=2 ttl=127 time=25.3 ms
1372 bytes from 10.20.10.10: icmp_seq=3 ttl=127 time=25.2 ms

--- 10.20.10.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 25.1/25.2/25.3/0.1 ms

Meaning: Path MTU supports ~1400-byte frames. That’s typically safe for WireGuard.

Decision: If this fails but smaller sizes work, lower WireGuard MTU (e.g., 1380) and re-test; MTU blackholes make RDP “randomly freeze.”

Task 10: Confirm Windows is listening on 3389 (from the Windows host via SSH or console)

cr0x@server:~$ ssh admin@10.20.10.10 "netstat -ano | findstr :3389"
  TCP    0.0.0.0:3389           0.0.0.0:0              LISTENING       1140
  TCP    [::]:3389              [::]:0                 LISTENING       1140

Meaning: TermService is listening on IPv4 and IPv6, PID 1140.

Decision: If nothing is listening, RDP service is stopped or disabled. Start the service and confirm policy didn’t disable RDP.

Task 11: Verify Windows firewall allows RDP only from the VPN/office subnet

cr0x@server:~$ ssh admin@10.20.10.10 "powershell -NoProfile -Command \"Get-NetFirewallRule -DisplayGroup 'Remote Desktop' | Get-NetFirewallAddressFilter | Select-Object -First 5\""
RemoteAddress : 10.10.0.0/16
RemoteAddress : 10.10.0.0/16
RemoteAddress : 10.10.0.0/16
RemoteAddress : 10.10.0.0/16
RemoteAddress : 10.10.0.0/16

Meaning: RDP rules are scoped to Office A subnet. That’s the “only via VPN” enforcement point most auditors care about.

Decision: If you see RemoteAddress : Any, tighten it. “Any” will inevitably become “anyone” through some future routing mishap.

Task 12: Validate NLA is enabled

cr0x@server:~$ ssh admin@10.20.10.10 "powershell -NoProfile -Command \"(Get-ItemProperty -Path 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp' -Name UserAuthentication).UserAuthentication\""
1

Meaning: 1 means NLA required.

Decision: If it’s 0, enable it unless you have a very specific compatibility reason. If a vendor insists on disabling NLA, push back hard.

Task 13: Check Windows event logs for RDP logons (success/failure)

cr0x@server:~$ ssh admin@10.20.10.10 "powershell -NoProfile -Command \"Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625} -MaxEvents 3 | Select-Object TimeCreated, Id, Message\""
TimeCreated            Id Message
-----------            -- -------
12/28/2025 08:34:11 4625 An account failed to log on.
12/28/2025 08:32:07 4625 An account failed to log on.
12/28/2025 08:29:52 4625 An account failed to log on.

Meaning: Failed logons exist. Now you correlate source IP, account name, and logon type. If these came from inside the VPN, you may have compromised credentials or overly broad access.

Decision: If you see bursts, tighten VPN access, enable MFA, and consider account lockout policy adjustments (carefully—lockouts can be weaponized as a DoS).

Task 14: Confirm the VPN is the only path (routing sanity check)

cr0x@server:~$ traceroute -n 10.20.10.10 | head -n 6
traceroute to 10.20.10.10 (10.20.10.10), 30 hops max, 60 byte packets
 1  10.99.0.1  1.032 ms  0.981 ms  0.965 ms
 2  10.20.0.1  22.412 ms  22.376 ms  22.341 ms
 3  10.20.10.10  23.918 ms  23.882 ms  23.851 ms

Meaning: Traffic enters the tunnel and lands in Office B. No weird detours through internet routes.

Decision: If hop 1 is your ISP router, you’re not going through the VPN. Fix routes and make sure split-tunnel assumptions match reality.

Task 15: Watch live traffic to confirm RDP flows are on the tunnel

cr0x@server:~$ sudo tcpdump -ni wg0 tcp port 3389 -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on wg0, link-type RAW (Raw IP), snapshot length 262144 bytes
08:41:22.112233 IP 10.10.50.25.51432 > 10.20.10.10.3389: Flags [S], seq 12223344, win 64240, options [mss 1360,sackOK,TS val 111 ecr 0], length 0
08:41:22.134455 IP 10.20.10.10.3389 > 10.10.50.25.51432: Flags [S.], seq 99887766, ack 12223345, win 65160, options [mss 1360,sackOK,TS val 222 ecr 111], length 0
08:41:22.134900 IP 10.10.50.25.51432 > 10.20.10.10.3389: Flags [.], ack 1, win 64240, options [TS val 112 ecr 222], length 0
08:41:22.145000 IP 10.10.50.25.51432 > 10.20.10.10.3389: Flags [P.], length 64
08:41:22.150000 IP 10.20.10.10.3389 > 10.10.50.25.51432: Flags [P.], length 128

Meaning: SYN/SYN-ACK on wg0 proves the session is traversing the VPN tunnel interface.

Decision: If you don’t see packets here but users claim they’re RDP’ing, they may be using another path (shadow IT remote tool, exposed edge, or a different VPN). Investigate and shut down the bypass.

Task 16: Identify bottleneck on the Linux gateway (CPU/interrupt pressure)

cr0x@server:~$ top -b -n 1 | head -n 15
top - 08:44:01 up 37 days,  2:11,  2 users,  load average: 0.62, 0.71, 0.68
Tasks: 141 total,   1 running, 140 sleeping,   0 stopped,   0 zombie
%Cpu(s):  7.2 us,  2.1 sy,  0.0 ni, 89.9 id,  0.1 wa,  0.0 hi,  0.7 si,  0.0 st
MiB Mem :   3932.1 total,    812.5 free,    701.1 used,   2418.5 buff/cache
MiB Swap:   1024.0 total,   1024.0 free,      0.0 used.   2978.2 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 1890 root      20   0  131456  24192  10432 S   6.0   0.6  18:22.11 wg-quick
 1022 root      20   0       0      0      0 S   1.3   0.0  12:10.23 ksoftirqd/0

Meaning: Plenty of CPU headroom; no sign the gateway is the bottleneck.

Decision: If CPU is pegged or softirq is high, you may be under-provisioned or suffering from NIC/driver issues. Scale the gateway or tune offloads carefully (and test).

Joke #2: The VPN is not “slow”; it’s merely taking a thoughtful, scenic route. Unfortunately your users are trying to sprint.

Fast diagnosis playbook

When someone says “RDP is down,” you don’t start by reinstalling Remote Desktop Services or rebooting the firewall like it’s a ritual.
You follow a tight sequence that finds the bottleneck in minutes.

First: prove the VPN path is alive

  1. Check the tunnel handshake. If the tunnel isn’t up, nothing else matters. Use wg show (Task 1) or your IPsec status tool.
  2. Check routes. Confirm the remote LAN routes to the tunnel (Task 2). Misroutes look exactly like “RDP is down.”
  3. Check forwarding + firewall on gateways. If the tunnel is up but forwarding drops traffic, you’ll see handshakes but no sessions (Tasks 3–4).

Second: prove the target host is reachable and listening

  1. TCP reachability test to 3389. Use nc -vz (Task 6). Timeout vs refused tells you where to look.
  2. Confirm the service is listening. netstat on the target (Task 10). No listener, no RDP.
  3. Confirm Windows firewall scoping. Ensure RDP rules are limited to VPN/office ranges (Task 11). A too-tight rule is as bad as a too-loose rule—just with fewer headlines.

Third: diagnose “slow” with network physics, not vibes

  1. Ping and loss/jitter. Task 8. Loss makes RDP feel like it’s dropping keystrokes.
  2. MTU test. Task 9. MTU blackholes cause intermittent stalls that users describe as “it freezes sometimes.”
  3. Observe live tunnel traffic. Task 15. If traffic isn’t on wg0, the user may not be using the intended path.
  4. Check gateway load. Task 16. Encryption and forwarding are real work; don’t run it on a forgotten VM with 1 vCPU and hope.

What to do when the tunnel is up but RDP still fails

At that point, focus on Windows-side controls:

  • NLA enabled (Task 12)
  • Account permissions and group memberships (Remote Desktop Users vs local Administrators)
  • Event logs for failures (Task 13)
  • DNS correctness (clients connecting to the right IP)

Three corporate mini-stories from the trenches

1) The incident caused by a wrong assumption: “It’s internal, so it’s safe”

A mid-sized company had two offices and a site-to-site VPN. They did the “right thing” by not exposing RDP publicly.
They also did the “classic thing” by allowing RDP from “any internal subnet” because it was easier than managing rules.

The wrong assumption was subtle: they treated the VPN as if it magically made both sides trusted.
In practice, Office A had a pile of unmanaged devices: a vendor laptop that came and went, a few kiosk PCs, and a Wi‑Fi network that wasn’t as segmented as everyone believed.

A credential stuffing attempt against a single weak local admin password succeeded—not because RDP was public, but because it was reachable from too many internal places.
Once the attacker got a foothold on one server in Office B, they used it to query AD, spray credentials, and move laterally.

The postmortem wasn’t about exotic exploits. It was about reachability and identity boundaries.
They fixed it by introducing jump hosts and scoping RDP firewall rules to only the jump host subnet, plus enabling MFA on remote-access VPN for administrators.
The VPN stayed. The trust model changed.

2) The optimization that backfired: “Let’s split-tunnel everything for performance”

Another organization had legitimate complaints: RDP sessions felt sluggish during peak hours.
Someone proposed split tunneling for the entire office network: keep internet traffic local, only route corporate subnets over the VPN. Reasonable on paper.

The backfire came from two places. First, DNS became ambiguous: some clients resolved server names to public IPs due to a mixed DNS configuration and conditional forwarders that weren’t consistently applied.
Second, security monitoring assumed all admin traffic came through the tunnel interface and flagged almost nothing when sessions began arriving from unexpected sources.

The result was weird: some admins connected over the VPN as intended, others “accidentally” used an alternate path through a legacy third-party remote tool that was still installed on jump hosts.
RDP issues became intermittent because people weren’t using the same path. Support tickets sounded like folklore.

The fix wasn’t “no split tunneling ever.” It was: define it precisely, enforce DNS, and remove bypass tools.
They implemented conditional DNS with clear rules, restricted management traffic to the jump host VLAN, and added alerts when RDP came from outside expected subnets.
Performance improved, but only after they restored determinism.

3) The boring but correct practice that saved the day: “One rule, one source, one purpose”

A finance-heavy company ran a tight ship. Their network team insisted on painfully specific firewall rules:
RDP to servers was allowed only from two jump hosts, and those jump hosts were reachable only through VPN.

It felt bureaucratic. People complained because they couldn’t just RDP from their desktops “for five minutes.”
The security team shrugged and kept saying the same sentence: “Management access has a choke point.”

Then a contractor’s laptop got infected offsite and later connected to the office network. Malware tried the usual playbook: scan, spread, brute force.
It could see plenty of things, but it couldn’t reach RDP on servers because the Windows firewall rules were scoped narrowly to the jump hosts.

The incident response was still work—containment, reimaging, credential resets—but it never became a server takeover.
The “boring rule” didn’t stop the laptop from getting infected. It stopped the infection from becoming a business-ending event.
That’s the kind of boring you want.

Common mistakes: symptom → root cause → fix

1) Symptom: VPN shows “connected,” but RDP times out

Root cause: The tunnel is up, but forwarding or firewall rules drop traffic between wg0 and LAN (or IP forwarding is off).

Fix: Confirm routes (Task 2), enable forwarding (Task 3), and add explicit FORWARD allows (Task 4). Verify with tcpdump (Task 15).

2) Symptom: RDP works from some PCs but not others

Root cause: Multiple paths exist (split tunnel, alternate VPN, stale DNS, or direct internet path). Some clients aren’t actually using the site-to-site VPN route.

Fix: Use traceroute (Task 14) from failing and working clients, fix routing/DNS, and remove bypass remote tools. Enforce firewall scoping to known subnets.

3) Symptom: RDP connects but randomly freezes for seconds

Root cause: MTU blackhole or intermittent packet loss on the tunnel path.

Fix: Run MTU probes (Task 9) and loss/jitter checks (Task 8). Lower WireGuard MTU, fix WAN link issues, or prioritize VPN traffic on the edge.

4) Symptom: Users get credential prompts repeatedly, then “The logon attempt failed”

Root cause: NLA + policy mismatch, clock skew impacting Kerberos, or account restrictions. Sometimes it’s a user trying a local account that isn’t allowed for RDP.

Fix: Confirm NLA status (Task 12), check event logs (Task 13), validate time sync, and ensure the user is in Remote Desktop Users (or equivalent) and allowed by policy.

5) Symptom: After “tightening security,” nothing can RDP anymore

Root cause: Windows firewall remote address scope set too narrowly (wrong subnet), or you locked out the jump hosts themselves.

Fix: Validate firewall address filters (Task 11), confirm source IP ranges, and keep an emergency break-glass path (console access / out-of-band) to revert rules safely.

6) Symptom: RDP is reachable from the internet despite “we didn’t open it”

Root cause: Edge NAT/port-forwarding, cloud security group rule, or ISP modem “helpful” forwarding. Sometimes a second firewall is doing unexpected UPnP behavior.

Fix: Perform a negative test from outside (Task 7), then audit edge NAT and upstream devices. Disable UPnP. Treat any public RDP exposure as an incident and rotate creds.

7) Symptom: VPN is stable but RDP performance is awful only at certain times

Root cause: Bufferbloat or saturation on WAN, or the VPN gateway is CPU constrained under load.

Fix: Check gateway load (Task 16), implement QoS/traffic shaping on the edge, and size gateways with headroom. Don’t starve encryption on a tiny VM.

8) Symptom: RDP works, but audits complain “no MFA”

Root cause: MFA was placed on Windows login only (or nowhere), while the primary risk is reaching the service. VPN auth is the right control point.

Fix: Enable MFA on VPN authentication, restrict VPN access by group, and keep RDP reachable only from VPN/jump hosts.

Checklists / step-by-step plan

Step-by-step: build “RDP only via VPN” between two offices

  1. Inventory subnets and fix overlaps. If both offices use the same RFC1918 range, decide now whether you will renumber or do NAT. Renumbering is painful once; NAT is confusing forever.
  2. Choose the VPN type. For two fixed sites, pick site-to-site (WireGuard or IPsec/IKEv2). Favor what your team can operate at 3 a.m.
  3. Define tunnel IP range. Use a small /24 or /30 reserved for VPN endpoints (e.g., 10.99.0.0/24).
  4. Implement routing. Add routes for remote LANs to the tunnel; avoid default-route tunneling unless required.
  5. Lock down forwarding on gateways. Default drop, then allow only required traffic between sites (RDP to jump hosts, AD/LDAP if needed, monitoring, etc.).
  6. Deploy jump host(s). Harden them, patch them, monitor them. They are your choke point.
  7. Scope Windows firewall rules. Allow TCP/3389 only from the VPN subnet or jump host subnet. Prefer “allow from jump hosts only” for servers.
  8. Enable NLA and modern TLS. Don’t negotiate with ancient clients; upgrade them or isolate them.
  9. Put MFA on VPN access. Make VPN the gate. Also restrict which users/groups may connect.
  10. Centralize logs. Collect VPN events and Windows Security logs relevant to RDP.
  11. Run negative tests. Verify 3389 isn’t reachable publicly. Verify RDP fails when not on VPN.
  12. Document the path. Write down the expected source IPs, target IPs, and allowed ports. Future you will send you a thank-you note.

Operational checklist: what to monitor continuously

  • VPN tunnel uptime/handshake freshness and churn
  • Packet loss/jitter between gateways
  • Windows failed logons (4625) with logon type 10 spikes
  • Firewall deny logs for 3389 from unexpected sources
  • Patch compliance on jump hosts and RDP targets

Security checklist: what to prohibit explicitly

  • No public inbound to 3389, ever
  • No “Any” remote address scope in Windows RDP firewall rules
  • No shared admin accounts for RDP
  • No unmanaged endpoints with admin VPN access
  • No “temporary” exceptions without an expiry date and an owner

FAQ

1) Is “RDP only via VPN” enough, or do I still need RDP hardening?

You still harden RDP. The VPN limits who can reach the port; RDP hardening limits what happens when someone reaches it.
Do both. Defense in depth is not a slogan; it’s how you survive staff turnover.

2) Should I use WireGuard or IPsec for site-to-site?

Use what your team can operate reliably. WireGuard is simpler and tends to be easier to debug. IPsec is widely supported in appliances and can be “set and forget” when done right.
The protocol matters less than correct routing, firewall scoping, and MFA on access paths.

3) Can I just change the RDP port instead of using a VPN?

No. Changing ports reduces random scans; it doesn’t address targeted attacks, credential stuffing, or configuration drift.
If you must expose something publicly, expose a hardened identity-aware gateway—not raw RDP.

4) Do I need a jump host if the VPN is already restricted?

If you have more than a couple servers, yes. Jump hosts create a choke point for access control, patching, and logging.
They also reduce the number of endpoints that ever handle privileged credentials.

5) What’s the best way to restrict RDP on Windows?

Scope Windows Firewall rules for “Remote Desktop” to trusted source ranges (VPN subnet or jump hosts).
Don’t rely solely on edge firewalls; local rules prevent accidental exposure through future routing changes.

6) Why does RDP feel slow even though bandwidth is fine?

RDP is latency and loss sensitive. A fast link with jitter and packet loss will feel worse than a slower but stable link.
Check ping loss/jitter and MTU issues first (Tasks 8 and 9).

7) Should we enable split tunneling?

For site-to-site, you already have “split” behavior by routing only the required subnets. For remote access, split tunneling is a policy decision:
it can reduce load and improve performance, but it complicates monitoring and increases the risk of unintended paths. If you do it, be explicit and test DNS and routing hard.

8) What if both offices use the same subnet (overlap)?

Overlapping subnets are the classic hidden landmine. The clean fix is renumbering one site.
If you can’t, you’ll need NAT inside the VPN and careful mapping (for example, translate one office’s 192.168.1.0/24 into a non-overlapping virtual range). Document it aggressively and expect extra troubleshooting time forever.

9) How do we prove to auditors that RDP is not exposed publicly?

Provide firewall policy evidence (edge rules show no 3389 inbound), plus external negative tests (Task 7), plus Windows firewall scoping (Task 11).
Auditors like layered controls because they assume mistakes happen. They are correct.

10) Can we avoid RDP entirely?

Sometimes. For server administration, PowerShell Remoting, Windows Admin Center, or management tools can reduce interactive RDP usage.
But you’ll still want a “break glass” interactive path for emergencies—just keep it VPN-only and tightly scoped.

Conclusion: practical next steps

The safest “RDP between offices” setup is not clever. It’s disciplined: site-to-site VPN, strict routing, Windows firewall scoping, jump hosts, MFA at the VPN, and logs you actually read.
No open 3389. No exceptions that live forever. No mystery paths.

Do this next, in order:

  1. Run the negative test from outside and confirm public RDP is dead (Task 7).
  2. Scope Windows RDP firewall rules to VPN/jump sources (Task 11) and verify with a reachability test (Task 6).
  3. Implement (or tighten) site-to-site VPN routing and forwarding rules (Tasks 1–4).
  4. Validate latency/loss and MTU to prevent “it freezes sometimes” tickets (Tasks 8–9).
  5. Set up the choke point: jump hosts, MFA, and centralized logs (Task 13 as your starting signal).

Make it boring. Then keep it boring. That’s how production stays alive.

← Previous
MikroTik IPsec IKEv2 Between Offices: How to Make It Stable (and Why It Flaps)
Next →
ZFS Slop Space: Why Pools Feel Full Before 100%

Leave a comment