VPN & Tunneling
VPNs create encrypted tunnels over the public internet — WireGuard for simplicity and speed, IPSec for enterprise interoperability, and SSH for developer ad-hoc access.
The Problem
Organizations need to connect remote users and branch offices to internal networks over the public internet. The internet is untrusted — anyone can intercept, modify, or inject traffic. VPNs create encrypted tunnels that make traffic invisible and tamper-proof, extending private network access across public infrastructure.
Mental Model
Like a private underground tunnel between two buildings — traffic inside the tunnel is invisible to anyone watching the street above. The tunnel entrance and exit are the VPN endpoints, and the encrypted packets are the sealed containers moving through.
Architecture Diagram
How It Works
A VPN (Virtual Private Network) creates an encrypted tunnel between two endpoints over an untrusted network. All traffic flowing through the tunnel is encrypted, authenticated, and invisible to anyone observing the network path between the endpoints. From the perspective of applications, it looks like they are on the same private network — even if they are physically separated by the entire internet.
VPN Fundamentals
Every VPN operates on the same basic principle: encapsulation and encryption. The original IP packet (with its private source and destination addresses) is wrapped inside a new IP packet with public addresses. The payload of the outer packet is encrypted, so anyone intercepting it sees only encrypted gibberish between two public IPs.
Original packet:
[IP Header: 10.0.1.100 → 10.0.2.50] [TCP] [Payload: "SELECT * FROM users"]
After VPN encapsulation:
[IP Header: 203.0.113.1 → 198.51.100.1] [ESP/UDP] [Encrypted: original packet]
An observer on the public internet sees traffic between two public IPs. They cannot see the original source, destination, protocol, or payload.
IPSec
IPSec is the traditional enterprise VPN protocol suite, built into every major OS and network device. It operates at Layer 3 (the IP layer) inside the kernel, making it transparent to applications.
IPSec has two key protocols:
IKE (Internet Key Exchange) handles authentication and key negotiation. IKEv2 (RFC 7296) is the modern version, supporting EAP authentication, MOBIKE (mobility and multihoming), and efficient rekeying. The IKE handshake establishes a Security Association (SA) — an agreement on encryption algorithm, keys, and lifetime.
ESP (Encapsulating Security Payload) handles the actual encryption and authentication of packets. ESP supports two modes:
- Tunnel mode: Encrypts the entire original IP packet and wraps it in a new IP header. Used for site-to-site and remote access VPNs.
- Transport mode: Only encrypts the payload, leaving the original IP header intact. Used for host-to-host encryption.
# Configure IPSec site-to-site with strongSwan (ipsec.conf)
conn office-to-aws
left=203.0.113.1 # Local public IP
leftsubnet=192.168.1.0/24 # Local private subnet
right=198.51.100.1 # Remote public IP (AWS VPN endpoint)
rightsubnet=10.0.0.0/16 # Remote private subnet (VPC CIDR)
ike=aes256-sha256-modp2048 # IKE cipher suite
esp=aes256-sha256 # ESP cipher suite
keyexchange=ikev2
auto=start
# Check tunnel status
sudo ipsec statusall
IPSec strengths: Universal support (every router, firewall, and cloud provider), mature implementation, hardware acceleration in most network devices.
IPSec weaknesses: Complex configuration (many knobs), stateful connection tracking, challenging NAT traversal (requires UDP encapsulation on port 4500).
WireGuard
WireGuard fundamentally rethought VPN design. Where IPSec has hundreds of configuration options and negotiation phases, WireGuard makes almost all decisions at compile time.
Key design principles:
- Cryptokey routing — Each peer is identified by its public key. The configuration maps public keys to allowed IP ranges. That is the entire access control model.
- Noise protocol framework — Key exchange uses Noise_IK, achieving a 1-RTT handshake. No negotiation — both sides use Curve25519 (key exchange), ChaCha20-Poly1305 (encryption), and BLAKE2s (hashing).
- Silence by design — WireGuard does not respond to unauthenticated packets. A port scan cannot even detect that WireGuard is running.
- Minimal attack surface — ~4,000 lines of code vs ~400,000 for OpenVPN+OpenSSL. Included in the Linux kernel since 5.6.
# WireGuard configuration — server (wg0.conf)
[Interface]
PrivateKey = SERVER_PRIVATE_KEY
Address = 10.0.0.1/24
ListenPort = 51820
# Each peer is a public key + allowed IPs
[Peer]
PublicKey = CLIENT_PUBLIC_KEY_1
AllowedIPs = 10.0.0.2/32
[Peer]
PublicKey = CLIENT_PUBLIC_KEY_2
AllowedIPs = 10.0.0.3/32
# Generate keys
wg genkey | tee privatekey | wg pubkey > publickey
# Bring up the interface
sudo wg-quick up wg0
# Check status
sudo wg show
# Shows: peer public keys, endpoints, last handshake, bytes transferred
# Performance comparison (typical on modern hardware)
# WireGuard: ~1 Gbps throughput, ~0.5ms added latency
# OpenVPN: ~300 Mbps throughput, ~2-5ms added latency
# IPSec: ~800 Mbps throughput (with hardware offload: line rate)
OpenVPN
OpenVPN is the most widely deployed open-source VPN. It runs in userspace, using TLS for the control channel (key exchange, authentication) and a custom protocol for the data channel (bulk encryption).
OpenVPN strengths: Runs on any port (can disguise as HTTPS on port 443 to bypass firewalls), supports TCP and UDP transports, extensive plugin system for authentication (LDAP, RADIUS, MFA), mature certificate-based PKI.
OpenVPN weaknesses: Userspace implementation limits throughput (~300 Mbps typical), complex configuration, large codebase dependent on OpenSSL.
# Connect with OpenVPN
sudo openvpn --config client.ovpn
# Check tunnel status
ip addr show tun0
ip route | grep tun0
SSH Tunnels
SSH tunnels are not a full VPN, but they are invaluable for ad-hoc encrypted access to specific services. Every developer should know these:
# Local port forwarding — access remote service through local port
# Access remote PostgreSQL (on 10.0.2.50:5432) via localhost:5432
ssh -L 5432:10.0.2.50:5432 bastion.example.com
psql -h localhost -p 5432 -U myuser mydb
# Remote port forwarding — expose local service to remote network
# Make a local dev server (localhost:3000) accessible on the bastion
ssh -R 8080:localhost:3000 bastion.example.com
# Dynamic port forwarding (SOCKS proxy) — route arbitrary traffic
ssh -D 1080 bastion.example.com
# Configure browser/app to use SOCKS5 proxy at localhost:1080
# Jump host (ProxyJump) — chain through multiple bastions
ssh -J bastion.example.com internal-server.private
Split Tunneling vs Full Tunneling
Full tunneling routes ALL traffic through the VPN, including internet-bound traffic. This is common in corporate environments where security teams want to inspect all traffic. The downside: bandwidth bottleneck at the VPN gateway, increased latency for internet traffic, and the VPN becomes a single point of failure.
Split tunneling routes only specific subnets through the VPN. Internet traffic goes directly to the ISP. This is more efficient but requires careful DNS configuration to prevent DNS leaks.
# WireGuard split tunnel — only route 10.0.0.0/8 through VPN
[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 10.0.0.0/8
Endpoint = vpn.example.com:51820
# WireGuard full tunnel — route everything through VPN
[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = vpn.example.com:51820
Tailscale and Mesh VPNs
Traditional VPNs use a hub-and-spoke model: all traffic flows through a central VPN gateway. Tailscale (and its open-source control plane alternative, Headscale) uses WireGuard to create a mesh network where every device can communicate directly with every other device.
The key innovations:
- NAT traversal — Tailscale uses STUN, DERP relays, and hole punching to establish direct WireGuard connections even behind restrictive NATs.
- Identity-based access — Instead of IP-based ACLs, Tailscale integrates with the organization's identity provider (Google, Okta, GitHub) and policies reference user identities.
- Zero configuration — Install the client, log in, and the device is on the network. No key management, no firewall rules, no gateway configuration.
- MagicDNS — Automatic DNS names for every device (e.g.,
laptop.tail12345.ts.net).
# Install and connect
tailscale up
# Check network status
tailscale status
# Access any device on the tailnet by name
ssh server.tail12345.ts.net
curl http://dev-box.tail12345.ts.net:3000
MTU Considerations
VPN encapsulation adds headers to every packet. If the inner packet plus VPN headers exceeds the path MTU (typically 1500 bytes), packets will be fragmented or dropped. This is one of the most common VPN debugging issues.
| VPN Type | Overhead | Recommended Inner MTU |
|---|---|---|
| WireGuard | 60 bytes (IPv4) / 80 bytes (IPv6) | 1420 |
| IPSec (ESP tunnel) | 50-70 bytes | 1400 |
| OpenVPN (UDP) | 48 bytes | 1420 |
| OpenVPN (TCP) | 64 bytes | 1400 |
# Test the effective MTU through the tunnel
ping -M do -s 1400 10.0.2.50 # Start high, reduce until it works
# Set the tunnel MTU explicitly
sudo ip link set wg0 mtu 1420
# On WireGuard config
[Interface]
MTU = 1420
Symptoms of MTU problems: small requests (API calls, pings) work fine, but large transfers (file uploads, database queries returning many rows) hang or fail. This is because small packets fit within the MTU, but large packets get silently dropped when they exceed it and Path MTU Discovery is blocked by a firewall.
Key Points
- •WireGuard has ~4,000 lines of code vs OpenVPN's ~100,000, making it dramatically easier to audit and less likely to have bugs.
- •IPSec operates at the kernel level (L3) and is invisible to applications. OpenVPN operates in userspace (L4) and uses a TUN/TAP adapter.
- •Split tunneling routes only private network traffic through the VPN, improving performance for internet-bound traffic.
- •WireGuard uses the Noise protocol framework for key exchange, achieving a single round trip handshake.
- •Site-to-site VPNs connect entire networks, while remote access VPNs connect individual devices to a network.
Key Components
| Component | Role |
|---|---|
| Tunnel Interface | Virtual network interface that encapsulates and encrypts all traffic before sending it through the physical network |
| IKE (Internet Key Exchange) | IPSec's protocol for authenticating peers and negotiating encryption keys, with IKEv2 being the modern version |
| ESP (Encapsulating Security Payload) | IPSec protocol that provides encryption and authentication for packet payloads in tunnel or transport mode |
| WireGuard Cryptokey Routing | Maps public keys to allowed IP ranges, creating a simple routing table that doubles as access control |
| Split vs Full Tunneling | Determines whether all traffic or only specific subnets flow through the VPN tunnel |
When to Use
Use site-to-site VPN (IPSec) to connect offices or data centers to cloud VPCs. Use WireGuard or Tailscale for remote access and developer environments. Use SSH tunnels for ad-hoc access to specific services. Consider zero trust networking as a complement or replacement for traditional VPN in modern architectures.
Tool Comparison
| Tool | Type | Best For | Scale |
|---|---|---|---|
| WireGuard | Open Source | Modern VPN with minimal codebase, excellent performance, and simple configuration | Small-Enterprise |
| OpenVPN | Open Source | Mature VPN with broad platform support, flexible authentication, and extensive plugin ecosystem | Small-Enterprise |
| Tailscale | Managed | Zero-config mesh VPN built on WireGuard with identity-based access control and NAT traversal | Small-Enterprise |
| AWS Site-to-Site VPN | Managed | IPSec VPN connecting on-premises networks to AWS VPCs with redundant tunnels | Enterprise |
Debug Checklist
- Check tunnel status: wg show (WireGuard), ipsec statusall (strongSwan), or check cloud console for VPN connection state.
- Verify routing: ip route show — ensure traffic for the private subnet routes through the tunnel interface (wg0, tun0).
- Test connectivity through the tunnel: ping an internal IP. If ping fails but the tunnel is up, check firewall rules on both sides.
- Check for MTU issues: ping -M do -s 1400 internal-host — reduce the size until it works, then set the tunnel MTU accordingly.
- Verify DNS resolution: nslookup internal-host — ensure DNS queries for internal domains resolve through the VPN, not the public DNS.
Common Mistakes
- Using PPTP in production. Its encryption (MS-CHAPv2) has been broken since 2012. Use WireGuard or IPSec IKEv2.
- Routing all traffic through the VPN (full tunnel) when only private network access is needed, creating a bottleneck.
- Not configuring DNS correctly for split tunnel — DNS queries leak to the ISP, revealing which internal services users access.
- Using pre-shared keys for IPSec instead of certificate-based authentication, making key rotation painful.
- Ignoring MTU issues. VPN encapsulation adds 40-80 bytes of overhead, which can cause silent packet drops if the inner MTU is not reduced.
Real World Usage
- •Tailscale uses WireGuard to create a mesh VPN where every device can reach every other device, with NAT traversal handled automatically.
- •AWS Site-to-Site VPN provides redundant IPSec tunnels between on-premises data centers and VPCs.
- •Cloudflare WARP uses WireGuard to encrypt consumer and enterprise traffic to Cloudflare's edge network.
- •Most corporate remote access VPNs use IPSec IKEv2 or SSL VPN (GlobalProtect, Cisco AnyConnect) for employee connectivity.
- •SSH tunnels are commonly used by developers to access internal databases and admin panels through a bastion host.