Chapter 4. Internal Network Security and Remote Access (In Practice: Adopting and Deploying ZTNA & VPNs)

Learning objectives

  • Explain the lateral movement risks inherent in traditional perimeter VPNs (the castle-and-moat model) and compare them with the core principles and logical components (PE, PA, PEP) of the NIST SP 800-207 Zero Trust Architecture.
  • Explain how an outbound-only connector (cloudflared Cloudflare Tunnel) exposes internal services without opening any inbound firewall ports, and design a highly available connector deployment.
  • Describe how WireGuard-based mesh VPNs (such as Tailscale) separate the control plane from the data plane, establish direct peer-to-peer connections (NAT traversal) with DERP encrypted relay fallback, route to unmanaged resources via Subnet Routers, and enforce least-privilege ACLs.
  • Diagnose and resolve split tunneling routing issues (Include vs. Exclude modes) and RFC 1918 private subnet collisions with home Wi-Fi routers (192.168.x.x) during company-wide client rollouts.
  • Design Split-Horizon DNS and conditional DNS forwarding so internal-only domain names never leak into public authoritative DNS.
  • Combine Identity Provider integration (SSO, phishing-resistant MFA, and automatic account revocation via SCIM) with Device Posture checks (OS patch level, disk encryption, and MDM/EDR status) before granting access.
  • Distribute enterprise ZTNA TLS inspection root CA certificates to developer workstations so CLI tools and AI coding agents (NODE_EXTRA_CA_CERTS, SSL_CERT_FILE) function cleanly without disabling TLS verification (NODE_TLS_REJECT_UNAUTHORIZED=0).

Introduction

The most valuable crown jewels of an AI company rarely sit on its public marketing website. They live inside the internal network: staging databases that engineers query daily, internal admin tools holding proprietary prompts and evaluation datasets, self-hosted vLLM or Ollama GPU inference servers, internal engineering wikis, and deployment dashboards. How should you allow remote engineers working from home, contracted data annotators, and traveling product managers to connect to those internal systems safely?

The laziest approach is to attach public IP addresses to internal servers, open ports (SSH 22, PostgreSQL 5432, HTTP 8080) in the firewall, and rely on passwords alone—which is effectively an open invitation to automated internet-wide vulnerability scanners. The second approach, used for over two decades, is the traditional perimeter VPN (Virtual Private Network): placing a single VPN gateway appliance on the corporate firewall perimeter and handing every authenticated employee an internal network IP address.

Now consider what happens when an external contractor's personal laptop gets infected with info-stealing malware and connects to that traditional corporate VPN. Because a perimeter VPN treats any device that passes the initial login gate as a trusted citizen inside the internal network, the attacker can pivot sideways across the internal subnet (Lateral Movement)—scanning and accessing finance file servers and production database ports that the contractor had no business touching. Worse still, when the security team reacts by pushing a next-generation ZTNA agent to every developer laptop overnight without careful network and certificate engineering, the entire engineering organization wakes up paralyzed the next morning: "My home Wi-Fi printer stopped working!", "Internal .internal domains won't resolve!", and "Claude Code, npm install, and python are all crashing with UNABLE_TO_GET_ISSUER_CERT_LOCALLY certificate errors!"

This chapter answers both halves of that challenge: First, how do we protect internal resources with least privilege and zero open inbound ports? Second, when we actually roll out VPN and ZTNA clients across employee and developer machines, how do we prevent and resolve routing collisions, DNS leaks, and CLI TLS certificate failures?

Key concepts

Limits of traditional perimeter VPNs and Zero Trust (NIST SP 800-207)

Traditional corporate network security relied on the Castle-and-Moat model: the public internet outside the company is treated as hostile territory, while the internal corporate network (VPC or office LAN) inside the moat is assumed to be a safe, trusted zone. Remote workers present their credentials at the drawbridge (the VPN gateway) and are admitted inside the castle walls.

That castle-and-moat model breaks down in modern cloud operations for three fundamental reasons:

  1. Excessive implicit trust and Lateral Movement: Once a user's device passes the VPN gate, it is placed directly onto the internal network layer (L3/L4). Even if a marketing intern only needs to view a single internal web dashboard, their machine can scan and probe SSH ports and database ports on neighboring servers across the subnet. Compromising a single endpoint compromises the entire internal network.
  2. Inbound attack surface on the VPN appliance itself: To accept incoming connections from remote workers, a traditional VPN concentrator must expose a public IP address and open inbound ports (such as UDP 500/4500 or TCP 443) to the entire internet 24/7. In recent years, some of the most devastating enterprise ransomware and intrusion incidents have exploited zero-day vulnerabilities in public-facing VPN appliances.
  3. Hairpinning and backhauling bottlenecks in the cloud era: When your production and staging servers live in AWS Seoul and GCP Tokyo, forcing every remote employee's traffic to trombone through a physical VPN appliance in headquarters before going back out to the cloud (backhauling) adds severe latency and turns the VPN concentrator into a single point of failure (SPOF).

To replace this broken model, the US National Institute of Standards and Technology (NIST) published SP 800-207 (Zero Trust Architecture), which establishes the core tenet of Zero Trust:

"No actor, system, network, or service operating outside or within the security perimeter is trusted implicitly based solely on its physical or logical network location (such as having an internal IP address). Instead, access is granted strictly on a per-resource, per-session basis with least privilege after continuously verifying user identity, device security posture, and request context."

NIST SP 800-207 divides a Zero Trust system into three logical components.

NIST SP 800-207 component Architectural role Practical real-world equivalent
Policy Engine (PE) The decision brain that evaluates user identity (IdP), device health (MDM/EDR), time, location, and risk signals to decide whether to grant or deny access to a resource Cloudflare Access policy evaluator, Tailscale / Okta policy decision engine
Policy Administrator (PA) Translates the Policy Engine's decision into concrete action by issuing session tokens or signaling the enforcement point to open or terminate the communication path ZTNA control plane and coordination server
Policy Enforcement Point (PEP) The inline gatekeeper sitting between the client and the target resource that actually inspects packets/requests and lets only authorized sessions through Edge ZTNA proxy, cloudflared connector, or WireGuard peer interface

Comparing traditional perimeter VPNs with Zero Trust Network Access (ZTNA) side by side highlights the architectural shift.

Comparison criterion Traditional perimeter VPN Zero Trust Network Access (ZTNA)
Granularity of access Entire network or subnet (for example, admission into the 10.10.0.0/16 network) Individual application, host, and port (for example, HTTPS access to admin.internal only)
Lateral movement risk High (once inside the subnet, an attacker can scan neighboring hosts and ports) Extremely low (unauthorized resources are completely invisible and unreachable)
Inbound firewall ports Requires a public IP and open inbound ports on the VPN gateway Zero open inbound ports when paired with outbound-only connectors
Verification timing & signals Checked once at initial tunnel connection via username/password (and OTP) Evaluated continuously on every resource request using SSO identity + MFA + Device Posture

Outbound-only connectors: cloudflared (Cloudflare Tunnel) and eliminating inbound ports

Suppose you run an internal admin web application (http://localhost:8080) or a staging SSH server inside a private VPC subnet and want to make it accessible only to authorized employees working remotely. Which inbound ports should you open in your AWS Security Group or on-premises firewall? The Zero Trust answer is: "Open zero inbound ports."

How can external employees reach an internal server if zero inbound ports are open on the firewall? The secret is reversing the direction in which the network connection is established (an Outbound-only Connector). The industry-standard implementation of this pattern is Cloudflare's cloudflared (Cloudflare Tunnel).

Let's trace how cloudflared works step by step:

  1. On your internal server (or private VPC subnet), you configure the firewall (Security Group) with zero inbound allow rules (Inbound allow: none). You allow outbound connections only (over port 443/7844 using QUIC or HTTP/2) to the outside internet.
  2. Inside the private network, you run the lightweight cloudflared daemon. On startup, cloudflared reaches from the inside out to multiple nearby Cloudflare edge data centers and establishes four persistent, encrypted outbound tunnel connections.
  3. Now, a remote employee opens their browser and navigates to https://staging-admin.example.com.
  4. The request lands first at the Cloudflare Edge (Cloudflare Access, acting as the PEP). Before forwarding a single byte into your network, Cloudflare verifies that the employee has signed in through your corporate Identity Provider (Google Workspace, Okta, Entra ID), completed MFA, and passed device posture checks.
  5. Only after authentication succeeds does Cloudflare forward the request down the pre-established outbound tunnel that cloudflared opened from inside your network, delivering it to http://localhost:8080 on the internal server!

This outbound-only architecture delivers three major security and operational advantages:

  • Complete elimination of public IPs and open inbound ports: Your internal server does not even need a public IPv4 address (it can sit safely behind a NAT gateway in a private subnet). Because there are zero open inbound ports listening on the internet, external port scanning and direct DDoS attacks against your origin are physically impossible.
  • Pre-authentication at the edge: Unauthenticated packets from attackers are dropped at the Cloudflare edge before they ever traverse the tunnel or touch your internal web framework.
  • High Availability (HA) via connector replicas: Because a single cloudflared process or host can restart, production tunnels always run two or more cloudflared connector replicas across separate hosts or Availability Zones (AZs) serving the same logical tunnel so traffic fails over seamlessly without interruption.

Mesh VPNs (WireGuard & Tailscale): Subnet routers, DERP relays, and least-privilege ACLs

Reverse-proxy ZTNA tunnels like Cloudflare Tunnel are ideal for web applications and individual endpoints, but engineering teams often need their local IDEs and terminals to talk directly to multiple internal microservices, gRPC endpoints, development Kubernetes clusters, and internal GPU inference nodes with ultra-low latency. For those east-west and developer-to-infrastructure workflows, WireGuard-based mesh overlay networks (such as Tailscale or NetBird) are the modern standard.

A WireGuard mesh VPN differs fundamentally from a legacy hub-and-spoke VPN concentrator.

Dimension Legacy centralized VPN (OpenVPN / IPsec concentrator) Modern WireGuard Mesh VPN (Tailscale, etc.)
Data traffic path Every packet trombones through one central VPN gateway (creating a bandwidth and latency bottleneck) The central server handles only the control plane (key exchange and ACL distribution), while encrypted data packets flow directly peer-to-peer (P2P) between the client and target node
Cryptographic protocol Heavy, complex legacy handshake negotiation Modern, lightweight WireGuard cryptography (fast roaming and minimal packet overhead)
IP addressing scheme Dynamic private IP leased from a shared pool (10.8.0.x) on each connection Every device and server receives a stable, dedicated CGNAT address (100.64.0.0/10) and MagicDNS hostname

When deploying a mesh VPN in production, you must understand three core mechanisms:

  1. NAT Traversal and DERP encrypted relay fallback: Even when both the developer's laptop and the target server sit behind NAT routers or firewalls, the mesh client uses STUN/ICE techniques (UDP hole punching) to establish a direct encrypted UDP path between the two peers. If a strict corporate firewall, hotel Wi-Fi, or cellular network blocks outbound UDP completely so direct P2P hole punching fails, the connection does not drop; instead, it automatically falls back to relaying packets over HTTPS (TCP port 443) through a DERP (Designated Encrypted Relay for Packets) server. Because WireGuard private keys never leave the endpoints, the packets remain end-to-end encrypted and the DERP relay cannot read the payload.
  2. Subnet Routers (Bastion Bridges): Managed cloud resources like AWS RDS PostgreSQL, network printers, or legacy appliances do not allow you to install a VPN agent binary directly on the box. To reach them, you run a small Linux instance or container inside the same private VPC subnet configured as a Subnet Router that advertises the private database subnet (--advertise-routes=10.50.10.0/24) into the mesh. Authorized developers can then connect to the managed RDS instance seamlessly through the Subnet Router (deployed in an HA pair of 2+ nodes).
  3. Tag- and role-based least-privilege ACLs (Access Control Lists): If joining the mesh VPN allowed every node to talk to every other node on every port, you would recreate the lateral movement risk of a legacy VPN. Instead, a Zero Trust mesh sets its baseline posture to Default Deny, and defines declarative JSON/HuJSON rules specifying which identity group (src) may connect to which server tag (dst, such as tag:staging-db or tag:gpu-worker) on which exact port:
{
  "acls": [
    {
      "action": "accept",
      "src": ["group:backend-devs"],
      "dst": ["tag:staging-db:5432", "tag:ai-inference:8000"]
     },
    {
      "action": "accept",
      "src": ["group:security-admins"],
      "dst": ["tag:prod-bastion:22"]
    }
  ]
}

With this ACL enforced at the WireGuard interface (the PEP), a backend developer can send packets only to port 5432 on staging databases and port 8000 on AI inference servers; any attempt to SSH into port 22 on a production bastion or probe a coworker's laptop is dropped immediately.

Split tunneling and resolving home router subnet collisions (RFC 1918)

On the first day an organization pushes a VPN or ZTNA client to every employee's laptop, the number-one cause of helpdesk tickets is routing table collisions caused by poorly planned Split Tunneling and RFC 1918 private IP ranges.

Client tunnel routing operates in one of two overarching modes:

  • Full Tunnel: Routes 100% of outbound traffic (0.0.0.0/0) from the employee's laptop through the corporate VPN gateway or Secure Web Gateway (SWG). While this gives security teams complete visibility over internet traffic, it forces video calls (Zoom), YouTube streams, and massive Docker/model downloads through the corporate gateway—consuming huge bandwidth and adding latency unless backed by a globally distributed edge SWG.
  • Split Tunnel: Routes only traffic destined for internal corporate resources into the tunnel, while allowing general public internet traffic to egress directly through the employee's local ISP connection. Split tunneling can be configured in two ways:
    • Include Mode: Explicitly lists the specific internal IP subnets (for example, 10.200.0.0/16) or internal domains that should enter the tunnel; everything else stays local. This is the cleanest and least disruptive mode for remote access to internal infrastructure.
    • Exclude Mode: Sends traffic into the tunnel by default, except for explicitly excluded IP ranges (such as local LAN subnets or video-conferencing IP blocks).

Here lies the infamous network engineering trap known as RFC 1918 Overlapping Subnet Collision!

Suppose a startup creates its cloud VPC using 192.168.0.0/16 or 192.168.1.0/24 (or advertises the entire 10.0.0.0/8 block into the VPN tunnel). Meanwhile, almost every home Wi-Fi router, coffee shop router, and mobile hotspot in the world uses 192.168.0.0/24 or 192.168.1.0/24 (with gateway 192.168.1.1) as its default local LAN subnet!

The instant a remote developer turns on the corporate VPN, one of two disasters occurs:

  1. If the laptop's OS routing table prioritizes the local Wi-Fi interface route, packets meant for an internal corporate server at 192.168.1.50 never enter the VPN tunnel—instead, they are sent to the developer's living-room smart TV or home router and fail to connect.
  2. Conversely, if the VPN client forcefully overrides 192.168.1.0/24 and hijacks all local traffic into the tunnel, the developer's wireless printer, local NAS, mobile test devices, and even their home router admin page become completely unreachable!

To prevent subnet collisions when designing cloud VPCs and VPN/ZTNA split tunnels, always follow three rules.

Design rule Broken configuration (causes collisions) Production best practice
1. Choosing internal VPC CIDRs Using common consumer router defaults like 192.168.0.0/24, 192.168.1.0/24, 10.0.0.0/24, or routing all of 10.0.0.0/8 Allocate non-overlapping RFC 1918 blocks from less common ranges (such as 10.240.0.0/16 or 172.28.0.0/16) sized to actual need
2. Scoping Split Tunnel Include lists Routing the entire 10.0.0.0/8 or 192.168.0.0/16 supernet when servers only occupy a single /24 Include only the exact, minimal CIDR blocks (10.240.10.0/24) where internal servers actually live
3. Connecting a legacy network that already collides Telling 200 remote employees to reconfigure their home Wi-Fi routers (impossible in practice) Bypass raw L3 IP collisions using domain-based ZTNA proxies (Cloudflare Tunnel), CGNAT (100.64.0.0/10) virtual IPs, or subnet NAT translation (4via6)

Split-Horizon DNS and preventing internal domain exposure

Suppose your internal staging database is named db.staging.internal.example.com and resolves to the private IP address 10.240.10.15. To make life convenient for developers, is it acceptable to register db.staging.internal.example.com → A 10.240.10.15 directly inside your publicly queryable authoritative DNS zone (such as a Route 53 Public Hosted Zone)?

No—that is a serious reconnaissance leak. Even though an external attacker cannot route packets directly to 10.240.10.15 over the public internet, publishing internal server hostnames and private IP addresses in public DNS allows attackers to enumerate your internal infrastructure (git-internal, payroll-db, vllm-gpu-01) and map your internal subnet topology via DNS queries or Certificate Transparency (CT) logs.

The proper architectural solution is Split-Horizon DNS (also called Split-Brain DNS), which serves different DNS answers depending on who is asking (and over which network path):

  1. Isolate internal records in a Private Hosted Zone: Register *.internal.example.com records exclusively inside an AWS Route 53 Private Hosted Zone (attached only to your VPC) or an internal DNS resolver. When someone on the public internet queries public DNS for db.staging.internal.example.com, they receive NXDOMAIN (domain does not exist).
  2. Configure Conditional DNS Forwarding (Split DNS) in the ZTNA/VPN client: Configure the ZTNA or mesh VPN client on employee laptops with a split DNS rule: "Send DNS queries ending in *.internal.example.com through the encrypted tunnel to the internal corporate DNS resolver (10.240.0.2), and send all other ordinary DNS queries (google.com, github.com) to the laptop's normal local DNS resolver."

With Split-Horizon DNS and conditional forwarding in place, authenticated employees resolve internal hostnames seamlessly, external attackers see zero internal hostnames in public DNS, and employees' everyday web browsing suffers zero DNS latency penalty.

IdP integration (SSO & MFA) and automatic offboarding with SCIM

Distributing a shared VPN username/password (vpnuser / company1234!) or a static .ovpn configuration file on an internal wiki for every employee to download is an operational disaster. Not only is it impossible to audit which individual accessed a sensitive server, but whenever a single engineer leaves the company, that former employee can keep connecting from home unless you rotate credentials on every single employee's laptop.

Enterprise remote access must therefore be tightly integrated with a central Identity Provider (IdP) such as Okta, Google Workspace, or Microsoft Entra ID:

  • Authenticate every user through OIDC or SAML Single Sign-On (SSO) backed by phishing-resistant Multi-Factor Authentication (MFA) such as WebAuthn passkeys or hardware security keys.
  • Connect your IdP to your ZTNA / mesh VPN platform using the SCIM (System for Cross-domain Identity Management) protocol. The moment HR or IT marks an employee or contractor as deactivated in the central IdP, SCIM automatically disables their ZTNA/VPN account and immediately revokes all active tunnel sessions and tokens within seconds.

Device posture and enforcing endpoint security health

Verifying who the user is (Identity) does not guarantee that the computer they are typing on (Device) is safe to connect to your internal network. Even if an engineer signs in with their legitimate corporate email and hardware MFA key, if they are logging in from an unpatched family desktop infected with malware or a laptop with disk encryption and endpoint protection turned off, the ZTNA policy engine must block the connection.

Device Posture is the continuous verification of an endpoint's security health by the ZTNA client both before establishing a session and while the session remains active.

Control pillar Required technical mechanism Real-world threat prevented
SSO & phishing-resistant MFA OIDC/SAML integration + WebAuthn passkeys or hardware security keys Blocks remote access via leaked passwords or real-time AitM phishing proxies
SCIM automated provisioning & deprovisioning SCIM protocol integration between HR/IdP and ZTNA to immediately disable accounts and terminate active tunnels upon offboarding Eliminates orphaned accounts belonging to former employees or contractors whose contracts have ended
Device Posture checks Verifies OS version and security patch level, full-disk encryption (FileVault/BitLocker), screen lock, corporate MDM enrollment certificate, and active EDR/antivirus status Prevents employees from connecting to internal infrastructure from unmanaged personal PCs or compromised devices
Short session TTLs & step-up re-authentication Periodic tunnel token expiration (for example, 12–24 hours) and fresh MFA prompts when connecting to production databases or SSH Limits the window of exposure if an unlocked laptop is lost or stolen

Deploying enterprise root CA certificates and fixing developer CLI & coding agent TLS errors

When organizations adopt ZTNA alongside a Secure Web Gateway (SWG—such as Cloudflare Gateway TLS Inspection or Zscaler) to scan outbound HTTPS traffic for data loss prevention (DLP) and malware, they enable HTTPS TLS Inspection.

During TLS inspection, the ZTNA gateway terminates outbound TLS connections, inspects the traffic, and re-signs the destination website's certificate on the fly using a private Enterprise Root CA certificate. IT administrators use an MDM tool (such as Jamf or Microsoft Intune) to install and trust this Enterprise Root CA inside the operating system's native trust store (macOS Keychain or Windows Certificate Store). Because web browsers like Chrome and Safari read the OS trust store, websites load in the browser without any certificate warnings.

Why, then, does every developer's terminal suddenly break with TLS errors? Because Node.js, Python (requests, urllib, pip), curl, git, Docker containers, and the AI coding agents built on top of them (such as Claude Code or Codex CLI) frequently ignore the operating system's native keychain and instead rely strictly on their own bundled Mozilla CA certificate list! When a developer runs npm install, pip install, or an AI coding agent in their terminal, the runtime sees the certificate signed by the corporate ZTNA gateway's Root CA, finds that CA missing from its built-in bundle, and aborts immediately with UNABLE_TO_GET_ISSUER_CERT_LOCALLY or SELF_SIGNED_CERT_IN_CHAIN.

Faced with a broken terminal, developers searching online forums are often tempted to paste the following catastrophic anti-patterns into their shell profiles:

// STRICTLY FORBIDDEN! (Completely disables TLS certificate verification against MITM attacks)
export NODE_TLS_REJECT_UNAUTHORIZED=0
git config --global http.sslVerify false
pip install --trusted-host pypi.org ...
curl -k https://...

Setting NODE_TLS_REJECT_UNAUTHORIZED=0 or http.sslVerify false does not mean "trust our corporate CA"—it means "turn off TLS certificate verification completely and blindly trust any forged certificate presented by an attacker on coffee-shop Wi-Fi!"

The correct engineering solution is to keep strict TLS verification turned ON while explicitly registering the corporate Root CA certificate file (.pem) with each runtime and CLI tool:

// BEST PRACTICE: Keep strict TLS verification enabled and add the enterprise Root CA (.pem)
// 1) Node.js and Node-based AI coding agents (APPENDS the corporate CA to Node's built-in CA list)
export NODE_EXTRA_CA_CERTS="/etc/ssl/certs/company-ztna-root-ca.pem"

// 2) Python (requests, pip, httpx) and OpenSSL/curl tools
// IMPORTANT: SSL_CERT_FILE and REQUESTS_CA_BUNDLE OVERRIDE (replace) the default CA bundle!
// You must point them to a COMBINED PEM file containing both public root CAs and your corporate root CA.
export SSL_CERT_FILE="/etc/ssl/certs/combined-ca-bundle.pem"
export REQUESTS_CA_BUNDLE="/etc/ssl/certs/combined-ca-bundle.pem"
export CURL_CA_BUNDLE="/etc/ssl/certs/combined-ca-bundle.pem"

// 3) Git CLI client
git config --global http.sslCAInfo "/etc/ssl/certs/combined-ca-bundle.pem"

Pay close attention to the crucial operational difference between these two environment variable families:

  • NODE_EXTRA_CA_CERTS appends (adds) your enterprise CA onto Node.js's existing built-in list of public CAs. Therefore, pointing NODE_EXTRA_CA_CERTS to a file containing only your single corporate Root CA certificate works cleanly.
  • By contrast, SSL_CERT_FILE and REQUESTS_CA_BUNDLE completely replace (override) the default public CA bundle! If you point SSL_CERT_FILE or REQUESTS_CA_BUNDLE to a file containing only your single corporate Root CA certificate, any direct HTTPS connection to a server that is not intercepted by your corporate gateway will fail because all public CAs (DigiCert, Let's Encrypt, GlobalSign) were wiped from the trust list. Therefore, for Python and OpenSSL tools, you must concatenate the system's standard public root CA bundle and your corporate Root CA into a single combined PEM file (combined-ca-bundle.pem). Finally, for specialized third-party binaries or OS update services (such as Apple software updates or certain Docker registries) that enforce hard-coded Certificate Pinning, configure a targeted TLS Inspection Bypass (Do Not Inspect) rule in the ZTNA admin console.

Going deeper

Keyless access with short-lived SSH certificates and session auditing

In many engineering organizations, granting SSH access to internal Linux servers or GPU clusters still involves collecting each developer's static ~/.ssh/id_ed25519.pub public key and pasting it into ~/.ssh/authorized_keys across dozens of machines. That legacy practice creates two major operational vulnerabilities: first, when an engineer leaves the company or loses a laptop, scrubbing their static public key from hundreds of servers is slow and error-prone; second, when ten engineers all log in as the shared OS user ubuntu or ec2-user, system logs cannot easily attribute which individual human ran a destructive command.

Modern ZTNA platforms (such as Cloudflare Access for Infrastructure, Tailscale SSH, or HashiCorp Vault SSH CA) replace static authorized_keys files with Short-lived SSH Certificates or identity-aware SSH:

  1. Individual developer public keys are never copied onto servers; instead, every server is configured once to trust only the organization's central SSH Certificate Authority public key (TrustedUserCAKeys).
  2. When a developer initiates an SSH connection, the ZTNA client verifies their SSO identity, MFA, and Device Posture in real time, and mints a short-lived SSH certificate valid for only a few hours (such as 4 to 8 hours).
  3. Once that short window expires, the certificate becomes useless automatically—leaving zero static keys to revoke—and every SSH login and command audit log records the exact authenticated SSO identity (minsu@company.com) alongside the local OS username.

Balancing developer productivity and security controls: A phased ZTNA rollout strategy

Even the best ZTNA architecture will cause a company-wide mutiny if the security team flips on Full Tunnel routing, strict device posture blocking, and TLS inspection for every employee simultaneously on Monday morning. A successful enterprise rollout follows a four-phase gradual migration plan.

Rollout phase Target scope and configuration Key validation metrics before advancing
Phase 1: Eliminate inbound ports & proxy web apps Place browser-based internal tools (wikis, admin dashboards) behind cloudflared outbound tunnels + SSO (Access) and close all public inbound firewall ports (zero client install needed yet) Zero open inbound ports on external scans; 100% browser SSO sign-in success
Phase 2: Pilot agent deployment (Include Split Tunnel) Install the ZTNA/mesh client on 10–20 security and platform engineers' laptops. Configure Include-mode Split Tunneling with non-overlapping CIDRs (10.240.0.0/16) and Split-Horizon DNS Zero home Wi-Fi or printer routing collisions; verify DERP relay fallback and Subnet Router latency
Phase 3: Automate developer CA bundles & audit-mode posture Use MDM scripts to pre-deploy the corporate Root CA, combined PEM bundle, and shell variables (NODE_EXTRA_CA_CERTS, SSL_CERT_FILE). Run Device Posture rules in Audit / Log-only mode to remediate non-compliant laptops first Zero CLI, Docker, or AI coding agent TLS certificate errors; >98% fleet posture compliance
Phase 4: Fleet-wide enforcement & legacy VPN decommission Switch Device Posture rules from Audit to Block (Enforce), verify automated SCIM offboarding revocation, and power down the legacy perimeter VPN appliance permanently 100% block rate for unmanaged/unpatched devices; legacy VPN gateway retired

Workplace scenarios

Gaon Robotics: Fixing home Wi-Fi outages and internal DNS leaks by redesigning split tunneling and Split-Horizon DNS

Gaon Robotics, which develops AI control software for warehouse robots, rolled out a mesh VPN client to 80 developers' MacBooks to secure access to internal GPU servers and development databases. By mid-afternoon on rollout day, half of the remote engineers were flooding Slack with complaints: "Whenever I turn on the VPN, my home Wi-Fi router admin page, wireless printer, and local robot hardware simulator (192.168.0.x) stop responding completely!" That same afternoon, an external bug-bounty researcher submitted a report showing that querying public DNS revealed internal server hostnames and private IP addresses such as gpu-h100-01.gaon.io and hr-db.gaon.io.

When the infrastructure team investigated, they uncovered two textbook design mistakes. First, because an old server room subnet had used 192.168.0.0/24, the Subnet Router had been configured to advertise the entire 192.168.0.0/16 range into the VPN—colliding directly with the 192.168.0.0/24 LAN subnet used by developers' home Wi-Fi routers. Second, the team had registered internal-only private IP A records directly inside their public authoritative DNS zone for convenience.

Over the weekend, the team re-architected their routing and DNS setup:

  1. Non-overlapping CIDRs and precision Include Split Tunneling: They standardized cloud VPC subnets on 10.220.0.0/16, moved legacy server room hosts onto dedicated CGNAT mesh addresses (100.64.x.x) and non-overlapping subnets, and completely removed the 192.168.0.0/16 route advertisement. Split tunneling was locked to Include Mode covering only 10.220.0.0/16 and 100.64.0.0/10.
  2. Split-Horizon DNS: They deleted all 40 internal A records from the public DNS zone, moved them into a private internal DNS zone under *.corp.gaon.io, and configured the VPN client's Split DNS feature so only queries for corp.gaon.io were forwarded to the internal DNS resolver inside the tunnel.
  3. Role-based least-privilege ACLs: They replaced the default allow-all mesh rule with granular ACLs so the robotics firmware team could reach only the simulation servers and the AI model team could reach only the GPU cluster (tag:gpu-cluster).

By Monday morning, remote developers could use their local robot hardware and home printers seamlessly while connected to the VPN, and external DNS queries for internal hostnames returned nothing.

Nuri Cloud: Resolving coding agent SSL errors during ZTNA rollout and automating offboarding session revocation

Nuri Cloud, an enterprise AI consultancy preparing for ISMS-P certification and enterprise client audits, deployed Cloudflare Zero Trust (Tunnel + Access + Gateway TLS Inspection). Moving their internal admin dashboards behind cloudflared outbound tunnels allowed them to close every public inbound port on their AWS Security Groups cleanly.

However, the moment they enabled the ZTNA endpoint client with TLS Inspection turned on across engineering laptops, development ground to a halt. While Chrome worked fine, every Node.js dev server, Python pip/poetry command, and CLI coding agent failed with Error: unable to get local issuer certificate whenever it called an external LLM API. Seeing several developers paste export NODE_TLS_REJECT_UNAUTHORIZED=0 into their .zshrc files triggered an immediate security intervention.

The security and platform teams partnered to ship a standardized MDM developer configuration package.

Friction point Unsafe workaround (prohibited) Nuri Cloud's standardized engineering fix
Node.js & AI coding agent CLIs NODE_TLS_REJECT_UNAUTHORIZED=0 (disables TLS verification) Configured the managed shell profile with export NODE_EXTRA_CA_CERTS="/Library/Application Support/NuriZTNA/nuri-root-ca.pem" pointing to the MDM-deployed enterprise Root CA
Python, curl, and git CLIs pip --trusted-host, git http.sslVerify=false Automatically generated nuri-combined-ca-bundle.pem (combining the OS public CA bundle with the corporate Root CA) and pointed SSL_CERT_FILE, REQUESTS_CA_BUNDLE, and git http.sslCAInfo to it
Certificate-pinned system services Turning off the ZTNA agent completely Added strict certificate-pinned domains (such as Apple OS update servers and select container registries) to the ZTNA Do Not Inspect bypass list
Revoking contractor access at contract end Manually deleting accounts a week after departure Integrated the corporate IdP (Okta) with ZTNA via SCIM so deactivating a user in HR/IdP automatically revokes active tunnel sessions and Access tokens within 1 minute

With the MDM package deployed, every engineer ran their coding agents and package managers without a single insecure TLS bypass flag, and Nuri Cloud passed its external security audit with zero findings.

Common mistakes

  1. "Once a user connects to the corporate VPN and gets an internal 10.x.x.x IP address, it is safe to open all internal server firewalls to the entire VPN subnet." That is the exact definition of excessive implicit trust and lateral movement risk in legacy perimeter VPNs. If a single employee or contractor laptop is compromised by malware, the attacker can pivot sideways across every internal server on that subnet; under Zero Trust (NIST SP 800-207), you must continuously verify identity and device posture and restrict access strictly to the specific hosts and ports required for the user's role via least-privilege ACLs.
  2. "Even when using Cloudflare Tunnel (cloudflared), you still have to open inbound port 443 on your corporate firewall so external users can reach the web server." False. Because the cloudflared connector daemon running inside your private network initiates an outbound encrypted connection to the Cloudflare edge first and receives authenticated traffic back over that pre-established outbound tunnel, your corporate firewall and Security Group require zero open inbound ports.
  3. "Choosing 192.168.0.0/16 or 192.168.1.0/24 for a cloud VPC CIDR is convenient because everyone is familiar with those numbers." Because almost every consumer home Wi-Fi router, coffee-shop network, and mobile hotspot uses 192.168.0.0/24 or 192.168.1.0/24 as its default local LAN subnet, routing those ranges into a corporate VPN triggers immediate RFC 1918 overlapping subnet collisions for remote workers—breaking either internal server connections or local LAN devices such as wireless printers.
  4. "As long as an internal server hostname resolves to a private IP (10.x.x.x), registering it in public authoritative DNS is completely harmless because external users cannot route to private IPs anyway." Even if external attackers cannot connect to the private IP directly over the internet, listing internal server hostnames and private subnet blocks inside public DNS hands attackers a free reconnaissance map of your internal infrastructure and network topology. Always hide internal domain names using Split-Horizon DNS (private internal DNS zones paired with conditional DNS forwarding in the ZTNA client).
  5. "When Node.js or Python throws an SSL certificate error behind corporate ZTNA TLS inspection, the standard fix is setting NODE_TLS_REJECT_UNAUTHORIZED=0 or verify=False." Disabling TLS certificate verification completely leaves the developer's workstation wide open to man-in-the-middle (MITM) attacks on any public network. Instead, keep strict certificate verification enabled and configure NODE_EXTRA_CA_CERTS (to append the corporate Root CA in Node.js) and a combined public-plus-corporate CA bundle via SSL_CERT_FILE / REQUESTS_CA_BUNDLE (for Python and OpenSSL tools).
  6. "Deactivating a departing employee's email account automatically kills any active VPN tunnel already connected on their laptop." Legacy VPNs that rely on long-lived client certificates or separate local VPN credentials can remain connected until the static certificate expires. You must integrate your central IdP with your ZTNA platform via SSO and SCIM provisioning and enforce short session lifetimes with immediate active-session revocation.

Summary at a glance

Core topic Key takeaway for the exam and production
Legacy VPN vs. ZTNA Perimeter VPNs expose internal subnets to lateral movement; ZTNA (NIST SP 800-207) grants per-resource, per-session least-privilege access based on identity and device posture
Outbound tunnels (cloudflared) Initiates outbound encrypted connections from inside the private network to the edge, requiring zero open inbound firewall ports; deploy 2+ connector replicas across AZs for HA
Mesh VPNs (WireGuard / Tailscale) Separates control and data planes (direct P2P UDP with DERP encrypted TCP 443 relay fallback), reaches unmanaged DBs (RDS) via Subnet Routers, and enforces tag-based port ACLs
Split Tunnel & RFC 1918 collisions Avoid home router default subnets (192.168.0.0/24, 192.168.1.0/24); assign non-overlapping VPC CIDRs (10.240.0.0/16) and route only required blocks via Include mode
Split-Horizon DNS Register internal domains (*.internal) inside private DNS zones only, and configure conditional DNS forwarding in the ZTNA client so internal names never leak into public DNS
IdP, SCIM & Device Posture Require SSO + phishing-resistant MFA, enforce OS patch/disk encryption/MDM/EDR posture checks, and use SCIM to automatically revoke accounts and active sessions upon offboarding
Endpoint Root CAs & CLI tools Never set NODE_TLS_REJECT_UNAUTHORIZED=0. Use NODE_EXTRA_CA_CERTS (appends corporate CA in Node) and a combined PEM bundle for SSL_CERT_FILE and REQUESTS_CA_BUNDLE

Check the official docs

Self-check

4-1. Compare a traditional perimeter VPN with NIST SP 800-207 Zero Trust Network Access (ZTNA) in terms of the scope of access granted after authentication succeeds, and name the primary propagation risk that occurs on a traditional VPN when a single connected endpoint is compromised.

4-2. When exposing an internal admin web server located in a private VPC subnet exclusively to authorized remote employees, explain in terms of connection establishment direction why using cloudflared (Cloudflare Tunnel) requires zero open inbound ports on your corporate firewall or Security Group.

4-3. After an organization numbered its cloud VPC subnet as 192.168.1.0/24 and deployed a VPN client to all employee laptops, remote workers reported that they could either no longer reach internal servers or could no longer print to their home wireless printers. Explain the root cause of this conflict and how the network CIDR and split tunnel should be redesigned.

4-4. Explain the security reconnaissance risk of registering an internal development database hostname (db.dev.internal.corp pointing to a private IP) inside a public authoritative DNS zone, and describe in two sentences how Split-Horizon DNS resolves this issue.

4-5. After enabling corporate ZTNA TLS inspection, Node.js-based AI coding agents and Python requests scripts on developer laptops fail with TLS certificate verification errors. Explain why developers must not fix this by setting NODE_TLS_REJECT_UNAUTHORIZED=0, and state the exact environment variable names—and the difference in PEM file contents—required for Node.js versus Python.

4-6. Design task: You need to grant 15 external contractors access strictly to an internal staging PostgreSQL database (an AWS RDS instance on port 5432 where agents cannot be installed directly), block them from reaching any other internal servers or SSH ports (port 22), and guarantee that their access terminates automatically the instant their contract ends. Describe the (1) RDS connectivity component, (2) host/port restriction mechanism, and (3) automated offboarding protocol you should configure.

Hands-on tasks

Exercise

Design an internal ZTNA / mesh VPN migration architecture and least-privilege ACL (50 minutes)

  1. Assume your organization operates three internal resources inside a private cloud network:
    • Internal operations admin web portal (http://10.240.10.20:3000, accessed via web browser only)
    • Staging RDS PostgreSQL database (10.240.20.50:5432, managed service where agents cannot be installed)
    • AI inference GPU server (10.240.30.10, exposing SSH on port 22 and an inference API on port 8000)
  2. Build an architecture table showing how to expose each resource with zero open public inbound ports using cloudflared tunnels and mesh VPN Subnet Routers (including high-availability replica counts).
  3. Write a JSON ACL policy granting least-privilege access for three identity groups: group:ops (admin web portal), group:backend (RDS port 5432 + inference API port 8000), and group:ml-infra (GPU server SSH port 22 + inference API port 8000).

Completion criteria: All three resources require zero public inbound ports, the managed RDS instance is reached via a redundant Subnet Router (or private tunnel), and the ACL explicitly prevents group:backend from connecting to port 22 (SSH) on the GPU server.

Exercise

Create a fleet-wide ZTNA rollout checklist and a developer CA setup script (40 minutes)

  1. Define an Include-mode Split Tunnel CIDR list that avoids colliding with remote workers' home networks (192.168.0.0/16) along with a Split-Horizon DNS conditional forwarding rule for *.internal.company.io.
  2. List the four mandatory Device Posture checks to verify before granting access, along with the required IdP integration settings (SSO, MFA, and SCIM).
  3. Write a macOS/Linux shell script (setup-dev-ca.sh) that combines the corporate Root CA (company-root-ca.pem) with the system's default public CA bundle (/etc/ssl/cert.pem) into combined-ca-bundle.pem, and exports the right environment variables so Node.js, Python, curl, and git trust the certificate without disabling TLS verification.

Completion criteria: The script contains zero insecure bypass flags (such as NODE_TLS_REJECT_UNAUTHORIZED=0 or curl -k), and accurately distinguishes NODE_EXTRA_CA_CERTS from the combined-bundle variables SSL_CERT_FILE, REQUESTS_CA_BUNDLE, and http.sslCAInfo.