LAN Access for a Team of 7 Without Open Ports: Cloudflare Zero Trust
Expose RDP to the world and it gets scanned within the first minute — letting the whole team access the LAN remotely without forwarding a single port on the router takes an outbound-only tunnel, with access controlled per person, per machine, and per port from one screen
I had a real requirement from an actual job: a team of 7 (some in other provinces, some abroad) needed access to machines on the internal LAN — RDP into Windows machines, the Proxmox web interface, and a few internal systems. I had one non-negotiable condition: no ports exposed to the internet at all (expose RDP to the world and it gets scanned within the first minute — this is real, and the logs confirm it).
I previously wrote about WireGuard (I still use it and still like it), but this time I chose Cloudflare Zero Trust (WARP + cloudflared) because the requirements were different: the users were not technical, handing out config files for them to import themselves would be a hassle, and I wanted to control access per person, per machine, and per port from one screen.
The team name, email addresses, and internal IPs in this article are all fictional (10.99.x.x).
Architecture
The key is that the tunnel runs outbound from inside the LAN to Cloudflare — the home/office router needs no port forwarding at all. Users install the WARP client and log in to the team, and that is it.
On the gateway inside the LAN (any Linux will do; I use a small VM):
cloudflared tunnel create lan-gw
cloudflared tunnel route ip add 10.99.1.0/24 lan-gw
cloudflared tunnel route ip add 10.99.2.0/24 lan-gw
cloudflared tunnel route ip add 10.99.3.0/24 lan-gw
cloudflared service install # รันเป็น systemd service
Lesson 1: I added emails to the policy, so why can't users log in?
I actually lost time on this, so I want to emphasize it: after I added the team's email addresses to the Access policy, they got this error when enrolling in WARP:
This account is restricted to members of the account
After tracing it, I found that the newly created Zero Trust team had no login method (Identity Provider) configured at all — the Access policy only says "which email addresses are allowed," but an IdP is needed for users to prove they own those email addresses first. The simplest fix, without relying on Google/Azure:
Zero Trust dashboard -> Settings -> Authentication -> เปิด One-time PIN
With that in place, team members enter their email address, receive a PIN by email, and get in. Adding one member requires all 3 layers, so keep this as a checklist:
- IdP — A login method is available (at least one OTP method)
- Access/enrollment policy — That person's email address is in the allow list
- Gateway policy — Defines which machines and ports they can access after login
Missing any one layer = no access, or worse, more access than intended.
Lesson 2: Gateway defaults to "allow everything" — you have to block it yourself
Once the tunnel is connected, everyone who can enroll can see every routed subnet unless Gateway network policies restrict access. This is not Zero Trust; it is an ordinary full-trust VPN. Here is how I set it up (following the principle of least privilege):
- Admin — All subnets, all ports
- 4 internal team members — 3 subnets, only ports 3389 (RDP), 8006 (Proxmox), 8090 (internal system)
- 3 outsourced team members — Only individually specified machines: 5 IPs, the same 3 ports
- Everyone — One internal application machine, port 7276
The rule structure in Gateway (Network policies), ordered from top to bottom:
1. Allow identity.email in {อีเมลแอดมิน} -> dst 10.99.0.0/16
2. Allow identity.email in {ทีม 4 คน} -> dst in {3 subnets} AND port in {3389,8006,8090}
3. Allow identity.email in {outsource 3 คน} -> dst in {5 IPs} AND port in {3389,8006,8090}
4. Allow ทุก identity -> dst 10.99.2.254 AND port 7276
5. Block (default-deny ปิดท้าย) -> dst 10.99.0.0/16
No Block rule at the end = everything that does not match gets straight through — effectively setting up the entire Zero Trust stack while leaving the back door wide open
This can also be done through the API if you frequently manage lots of users (I wrote a Python wrapper script to add/remove people with a single command):
curl -s -X PUT \
"https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/gateway/rules/<RULE_ID>" \
-H "Authorization: Bearer $CF_TOKEN" -H 'Content-Type: application/json' \
--data '{"name":"team-rdp","action":"allow",
"traffic":"net.dst.ip in {10.99.1.0/24 10.99.2.0/24} and net.dst.port in {3389 8006 8090}",
"identity":"identity.email in {\"[email protected]\" \"[email protected]\"}"}'
Lesson 3: Small issues I actually ran into
- A single-character typo in an email address = that person cannot enroll, and the user-facing error does not explain why — check the spelling before investigating anything else (I ran into this myself; the difference was a single l).
- AnyDesk/apps that use a wide range of ports need a port range added to the allow rule; otherwise, they connect but do not work.
- If the Windows WARP client is still logged in to another team, log out completely first; otherwise, it behaves strangely in ways that are hard to explain.
How does it compare with WireGuard, and which should you choose?
Small, technical teams
- Full control over endpoint machines
- Full speed, no intermediary
- A config file per machine — distribute and maintain it yourself
Mixed users, frequent additions/removals
- Audit logs + access control per port and per person
- No router changes, no open ports
- Users just install WARP + log in with email
I currently use both — WireGuard for my own system administration access, Zero Trust for the team, each with a clear role. Choose whichever fits your requirements and the situation on the ground. If I have left anything out or made a typo, I will come back and correct it later.
Want us to set this up?
Get help with server setup, configuration, migration, or troubleshooting. Save time for the work that matters.
Hit something like this? Stuck somewhere?
Come trade notes on the LinuxThai board



Comments 0
No comments yet — be the first