From VPN to Zero Trust: the perimeter is no longer real
https://sangtd.net/from-vpn-to-zero-trust-the-perimeter-is-no-longer-real/An engineer connects to the corporate VPN from a coffee shop. The VPN client authenticates, the tunnel comes up, and the laptop receives an IP address from the internal subnet. From that moment, the laptop can reach any server on the corporate network — the ERP system, the CI/CD pipeline, the production database, the internal wiki, the monitoring dashboard, the Git repository, the entire internal attack surface. The VPN tunnel is encrypted, so the link between the coffee shop and the corporate gateway is secure. But once traffic exits the VPN server onto the internal network, it is unencrypted and implicitly trusted.
This is not a flaw in the VPN implementation; it is a property of the VPN model itself. The VPN was designed in the 1990s when the corporate network was a single broadcast domain inside a locked building. In that world, the network perimeter was real: the building had a physical door, the server room had a locked rack, and any device connected to the internal switch was assumed to be legitimate. The VPN extended this trusted perimeter to remote users by creating an encrypted tunnel through the untrusted internet.
The perimeter no longer exists. The corporate network spans three cloud providers, a colocation facility, two branch offices, and a thousand employee devices that have never been inside a physical office. The building still has a door, but the door no longer defines the boundary of the network. A compromised employee laptop inside the VPN tunnel has the same access to the production database as the on-premises server rack — because the VPN does not distinguish between the two. It authenticated the tunnel, not the individual connections, and the implicit trust that made sense in 1995 is now the primary attack path into the infrastructure.
ZTNA inverts this trust model. Instead of placing the user inside the network and letting the network protect the application, ZTNA places a connector in front of the application and never exposes the application to the network at all. The user authenticates to an identity-aware proxy that evaluates the request — who the user is, what device they are on, whether the device meets security policies — before creating a per-session, encrypted connection to the application. The application sees only the connector, not the user’s device. The user sees only the application, not the network. The corporate subnet might as well not exist from the user’s perspective.
The difference between the two models is visible in how access is granted. In the VPN model, granting access to a new internal application means adding a DNS record and checking that the application is reachable from the VPN subnet. Every VPN-connected user can immediately discover and reach the application, because discovery is a network-layer operation — ARP, DNS, and routing all work as if the user were physically inside the office. In the ZTNA model, granting access means configuring the connector with the application’s internal URL, defining which users and groups can access it, and setting device posture requirements. The application becomes reachable only when all three conditions are met, and there is no network-level discovery because the application was never on the network to begin with.
Cloudflare Tunnel is the most accessible ZTNA implementation for teams already using Cloudflare. An agent called cloudflared runs on the application server and opens an outbound connection to Cloudflare’s edge network. The application binds to localhost — it has no public IP, no load balancer, no firewall rule allowing inbound traffic. The agent maintains a persistent connection to the edge, and Cloudflare’s Access engine evaluates every request against the configured policy before routing it through the tunnel. An attacker scanning the internet for the application finds nothing because the application never listens on a public port. There is nothing to scan.
The critical architectural property of Cloudflare Tunnel is that all connections are outbound-initiated. The agent reaches out to Cloudflare, not the other way around. This means the application server does not need a public IP at all — it can sit behind a carrier-grade NAT, a hotel Wi-Fi captive portal, or a firewall that blocks all inbound traffic. As long as the server can make outbound HTTPS connections to Cloudflare’s edge, the tunnel works. This property is valuable for deployment scenarios that are difficult for traditional VPN: a point-of-sale terminal in a retail store, a monitoring agent in a restricted government network, a CI/CD runner in a temporary cloud environment.
Tailscale approaches the same problem from a different direction. Instead of a centralized broker, Tailscale builds a mesh network on top of WireGuard where every device gets a unique IP address from Tailscale’s namespace and communicates directly with other devices using peer-to-peer WireGuard tunnels. The control plane handles key exchange, policy distribution, and NAT traversal coordination, but the actual data traffic flows directly between devices without passing through a central gateway. This makes Tailscale suitable for scenarios where devices need IP-level connectivity — SSH, database connections, development environments — with access control attached.
Tailscale’s ACLs are defined in a policy file distributed to every node in the tailnet. A rule that grants user “alice” access to the subnet containing application servers is enforced by each node locally: if alice’s device attempts to reach a server that the ACL does not permit, the target node drops the traffic at the WireGuard layer, before the application even sees the connection. The enforcement is distributed, not centralized, which means there is no single point where all traffic is inspected and no central gateway that becomes a bottleneck as the tailnet grows. The trade-off is that policy changes must propagate to every node before they take effect, and propagation latency depends on how frequently nodes check in with the control plane.
WireGuard itself is the cryptographic foundation for Tailscale and several other ZTNA solutions, but a raw WireGuard tunnel is not ZTNA. A WireGuard tunnel that connects a remote user to a corporate subnet and grants full network access behaves exactly like a traditional VPN — the tunnel is encrypted, but the user can reach any device on the subnet once inside. WireGuard becomes ZTNA only when paired with a control plane that dynamically configures the tunnel — which subnets are reachable, which ports are allowed, which users can connect — based on identity and device health rather than static configuration.
Netmaker and Headscale are open-source control planes that add this dynamic capability to WireGuard. Netmaker provides a web UI for managing WireGuard networks, automatic NAT traversal, and DNS-based service discovery. Headscale is an open-source implementation of Tailscale’s control server and is compatible with the official Tailscale client. Both allow teams to self-host the control plane, which is necessary in environments where a third-party service cannot manage access policy — regulated industries, air-gapped networks, or organizations that require the control plane to run on premises.
ZeroTier occupies a different category. It creates a virtual Layer 2 network where every device gets a MAC address and communicates using Ethernet frames — broadcast, ARP, and all. The security model is based on cryptographic membership in the network: a device that holds the network’s private key is trusted, and there is no per-connection authorization beyond the initial membership check. ZeroTier is useful in two specific scenarios: legacy applications that depend on broadcast discovery (older databases that use broadcast for replica discovery, license servers that broadcast for availability) and network-layer clustering that requires a shared MAC address. For everything else — HTTP APIs, gRPC services, database connections — the Layer 2 abstraction adds complexity without providing any security benefit over a Layer 3 solution, and the broadcast traffic can become a problem as the virtual network grows.
The operational differences between VPN and ZTNA are not theoretical. A VPN concentrator failure takes down every remote access session simultaneously. Every user loses connectivity to every internal application, the recovery requires restarting the concentrator and verifying that all tunnels re-establish, and the mean time to recovery is measured in minutes. A ZTNA connector failure takes down only the applications that connector serves. If each application or group of applications has its own connector — and they should — a single connector failure affects only that application group. The blast radius is contained by design.
Monitoring requirements differ as well. VPN monitoring tracks tunnel uptime, bandwidth utilization, and concurrent connection count. These metrics answer the question “is the VPN up?” but not “is the access model working as intended?” ZTNA monitoring tracks authentication failures, denied requests, and anomalous access patterns. A spike in denied requests from an account that was deprovisioned yesterday tells the operations team that the deprovisioning did not propagate. A connection attempt from an unknown device to an internal application tells the security team that an access policy rule is missing. These signals do not exist in a VPN model because a VPN never evaluates individual requests — it only evaluates the initial tunnel authentication.
The practical path from VPN to ZTNA is incremental and should start with a single application. Pick an internal tool that is accessed remotely — a Grafana dashboard, a CI/CD web interface, a database administration panel — and deploy a ZTNA connector in front of it. Configure the access policy for the team that uses the tool. Verify that the connection works, that latency is acceptable, and that the authentication flow does not frustrate users. Keep the VPN running for everything else. This phased approach avoids the worst failure mode of a migration: switching every application at once and discovering that one of them has a protocol dependency — a hardcoded IP, a legacy port requirement, a broadcast discovery mechanism — that the ZTNA connector cannot satisfy.
The second step is instrumentation. Every ZTNA connector logs authentication requests, policy evaluations, and denied connections. Build a dashboard that shows who is accessing what, from which device, from which location, and whether the access was permitted or denied. Use this data to refine the policy — remove permissions that are never exercised, add restrictions for patterns that look anomalous, and identify applications that are still exposed through the VPN but should be migrated next.
The third step is measuring the blast radius. When a user’s device is compromised, how many applications can the attacker reach? With a VPN, the answer is every application on the internal network — because the VPN granted network-level access. With ZTNA, the answer should be the specific applications that the user had permission to access, and that permission should be scoped to the minimum necessary for the user’s role. A decreasing blast radius — measured by periodically auditing what each user can reach — is the metric that confirms the migration is improving the security posture.
The VPN model was the right answer for a world where the network had a physical boundary and remote access was the exception. That world no longer exists. Every device is remote, every application is distributed, and the internal network is a shared medium where implicit trust is a liability. ZTNA does not eliminate the need for network-level encryption — the tunnel between the user and the connector is still encrypted — but it eliminates the implicit trust that made the VPN model vulnerable. The perimeter is no longer real, and the only thing that matters is the connection between a specific user and a specific application, authenticated and authorized individually, every time.