cyberhippo.ca

Closing Exposed Remote Desktop with WireGuard

Write-up · October 2026

I audited my own OPNsense firewall and found two old port forwards sending internet traffic to a Mac mini on my home network: VNC (Screen Sharing) and RDP. I replaced them with a WireGuard VPN, removed the forwards, checked the logs to see whether anyone got in, and then looked for every other way in from the internet.

Result: no successful logins from the internet. Bots had been trying for at least 30 hours. Remote desktop now only works over WireGuard, both ports are closed from outside, and the one app I publish to the internet sits behind an email login.

Starting point

1. The audit

I exported the firewall config and ran it through a small script that prints only the structure (interfaces, rules, NAT, VPN, DNS) and redacts hostnames, password hashes, keys, MAC addresses and public IPs. That made it safe to review and share.

The NAT section showed two forwards from the WAN to the Mac mini:

PortServiceRisk
TCP 5900VNC / macOS Screen SharingThe Mac's login screen was reachable by anyone on the internet
TCP 3389RDPSame idea, for Windows Remote Desktop

2. WireGuard first, then remove the forwards

I still needed remote access, so I set up the replacement before removing anything. That way I couldn't lock myself out.

PieceSetup
InstanceWireGuard on OPNsense, its own interface and a separate subnet for VPN clients
PeersOne per device, each with its own key pair and a single /32 address
Client configSplit tunnel: only home subnets go through the VPN
WAN ruleAllow UDP to the WireGuard port only
VPN ruleVPN clients can reach the LAN, nothing else

Getting the tunnel working took a few fixes:

SymptomCauseFix
No handshakeTwo peers had the same tunnel addressOne peer per device, unique address each
No handshakeThe laptop's config used an address and key the firewall didn't knowAdded the laptop's public key to its peer and matched the address
No traffic reaching WireGuardThe WAN rule had the port on the source sideMoved the port to the destination
Tunnel up, LAN unreachableThe allow rule was on the WAN interfaceRe-created it on the WireGuard interface, destination LAN net

Once Screen Sharing worked over the VPN from a phone hotspot, I deleted both forwards (Firewall → NAT → Destination NAT).

3. Did anyone get in?

macOS logs Screen Sharing authentication, so I pulled the history on the Mac mini:

log show --predicate 'process == "screensharingd"' --last 7d | grep -i auth

Even though I asked for 7 days, macOS only had about 31 hours of history. In that window:

Bar chart of connections to the Screen Sharing login per hour. About 50 to 75 per hour on the morning of Oct 27, a few per hour through the day, then about 320 to 335 per hour from 01:00 to 11:00 on Oct 28. It stops at 11:44, when the port forwards were removed.

Source (last octet masked)Attempts
187.14.82.x (two addresses)26, as root
77.239.124.x8, no username

The connections stop at 11:44, the minute I deleted the forwards.

The RDP forward turned out to point at nothing. macOS has no built-in RDP server (the Remote Desktop app on a Mac is only a client), and nothing was listening on 3389.

4. Verify from outside

I checked both ports with an external port checker. Both timed out.

One catch: the check has to run against the home IP. My tunnel is split, so browsing from a hotspot with the VPN on would have tested the hotspot's IP instead. I confirmed the IP the checker used matched the firewall's WAN address.

5. Other ways in

A port scan only finds things listening for inbound connections. Some tools connect out to a vendor and let traffic back in that way, so closing ports doesn't touch them. I went through each one:

PathFindingAction
Remote access appsAnyDesk installed as a startup service. Its log showed one incoming session, from my own laptop.Reviewed; to be removed now that WireGuard covers it
Cloudflare TunnelOne public hostname, pointing at a single app on the Mac miniPut Cloudflare Access in front of it (below)
Remaining port forwardsOne, for a media server with its own account loginKept
macOS sharing settingsRemote Management on, which controls Screen SharingTurned off the separate VNC password; access limited to my account

6. Cloudflare Access for the published app

The tunnel hides my home IP and exposes only one app, but that app's login page was still public. I wanted friends to be able to use it without a VPN, so I added a Cloudflare Access application on the free Zero Trust plan:

SettingValue
PolicyAllow, only listed email addresses
Login methodOne-time PIN by email
Instant authenticationOn, so there's no "choose a login method" screen
Session1 month

The first test sent me to the Cloudflare dashboard login instead of an email-code page. The account already had a "Cloudflare" identity provider, and one-time PIN is only the default when no provider exists. Adding one-time PIN and selecting only it fixed that. Now visitors enter their email, get a code, and only then reach the app.

What I learned

Next steps

References