VPN and Tunneling
VPN Fundamentals
Section titled “VPN Fundamentals”Tunneling, Encryption, and Authentication
Section titled “Tunneling, Encryption, and Authentication”A VPN creates an encrypted tunnel between two endpoints over an untrusted network (the internet). The three core functions are:
1. Tunneling: Encapsulate private network packets inside public network packets2. Encryption: Scramble the encapsulated data so it cannot be read in transit3. Authentication: Verify the identity of both tunnel endpointsVPN Types
Section titled “VPN Types”| Type | Layer | Use Case | Example |
|---|---|---|---|
| Remote access | L3 (IP) | Employees connecting to corporate | WireGuard, OpenVPN |
| Site-to-site | L3 (IP) | Connecting office networks | IPsec, WireGuard |
| SSL/TLS VPN | L7 (app) | Browser-based access | OpenVPN (TLS mode) |
| SSH tunneling | L7 (app) | Ad-hoc port forwarding | SSH local/remote/dynamic |
WireGuard
Section titled “WireGuard”WireGuard is a modern, minimalist VPN protocol that uses the Noise protocol framework for key Exchange and ChaCha20 for encryption.
Core Concepts
Section titled “Core Concepts”WireGuard uses cryptokey routing: each peer has a public/private key pair, and each peer”s Allowed IP addresses define which packets are routed through the tunnel.
# /etc/wireguard/wg0.conf (server)
[Interface]PrivateKey = <server_private_key>Address = 10.0.0.1/24ListenPort = 51820PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADEPostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]# Client 1PublicKey = <client1_public_key>AllowedIPs = 10.0.0.2/32
[Peer]# Client 2PublicKey = <client2_public_key>AllowedIPs = 10.0.0.3/32# /etc/wireguard/wg0.conf (client)
[Interface]PrivateKey = <client_private_key>Address = 10.0.0.2/24DNS = 10.0.0.1
[Peer]PublicKey = <server_public_key>Endpoint = vpn.example.com:51820AllowedIPs = 10.0.0.0/24, 192.168.1.0/24 # tunnel and LANPersistentKeepalive = 25Key Generation
Section titled “Key Generation”# Generate server key pairwg genkey | tee server_private.key | wg pubkey > server_public.key
# Generate client key pairwg genkey | tee client_private.key | wg pubkey > client_public.key
# Secure the private keyschmod 600 server_private.key client_private.keyAllowedIPs
Section titled “AllowedIPs”The AllowedIPs directive is the routing decision point:
- On the server,
AllowedIPs = 10.0.0.2/32means “accept packets FROM this IP only” - On the client,
AllowedIPs = 10.0.0.0/24means “route packets TO this subnet through the tunnel” AllowedIPs = 0.0.0.0/0routes ALL traffic through the tunnel (full VPN)AllowedIPs = 10.0.0.0/24routes only VPN subnet traffic (split tunnel)
Performance
Section titled “Performance”WireGuard is significantly faster than OpenVPN and IPsec:
| Protocol | Throughput (approx) | CPU Usage | Code Size | Handshake Time |
|---|---|---|---|---|
| WireGuard | Near line rate | Very low | ~4000 LOC | ~1 RTT |
| OpenVPN | 100-600 Mbps | Moderate | ~100K LOC | 2-4 RTTs |
| IPsec | 200-800 Mbps | Moderate | ~400K LOC | 2 RTTs |
# Enable WireGuardwg-quick up wg0
# Check statuswg show
# Add a peer at runtimewg set wg0 peer <public_key> allowed-ips 10.0.0.4/32
# Bring downwg-quick down wg0IPsec (Internet Protocol Security) operates at the network layer (L3) and is implemented in the Kernel. It consists of two protocols:
- IKE (Internet Key Exchange): manages key exchange (IKEv1 is deprecated; use IKEv2)
- ESP (Encapsulating Security Payload): provides encryption and authentication for IP packets
IKEv2 Configuration (strongSwan)
Section titled “IKEv2 Configuration (strongSwan)”# /etc/ipsec.conf (server)config setup charondebug="ike 2, knl 2, cfg 2, net 2, esp 2, dmn 2, mgr 2"
conn %default keyexchange=ikev2 ike=aes256gcm16-sha384-ecp384! esp=aes256gcm16-sha384! dpdaction=clear dpddelay=300s rekey=no
conn rw left=10.0.0.1 leftcert=server.crt leftsubnet=0.0.0.0/0 leftfirewall=yes right=%any rightauth=eap-mschapv2 rightsourceip=10.10.10.0/24 auto=add# /etc/ipsec.secrets (server): RSA server.keyalice : EAP "alice_password"IPsec Modes
Section titled “IPsec Modes”| Mode | Encapsulation | Use Case |
|---|---|---|
| Transport | Original IP header preserved | Host-to-host communication |
| Tunnel | Entire IP packet encapsulated | Site-to-site, remote access VPN |
SA and SPI
Section titled “SA and SPI”IPsec uses Security Associations (SAs) to define the security parameters for a connection. Each SA is identified by a Security Parameter Index (SPI) in the packet header. There are two SAs per Connection: one inbound, one outbound.
# View IPsec SAsip xfrm stateip xfrm policy
# strongSwan statusswanctl -lOpenVPN
Section titled “OpenVPN”TLS Mode Configuration
Section titled “TLS Mode Configuration”# server.confport 1194proto udpdev tun
ca ca.crtcert server.crtkey server.keydh dh.pem
server 10.8.0.0 255.255.255.0
keepalive 10 120cipher AES-256-GCMauth SHA256
# Push configuration to clientspush "redirect-gateway def1 bypass-dhcp"push "dhcp-option DNS 10.8.0.1"push "route 192.168.1.0 255.255.255.0"
persist-keypersist-tunstatus openvpn-status.logverb 3# client.confclientdev tunproto udpremote vpn.example.com 1194
ca ca.crtcert client.crtkey client.key
remote-cert-tls servercipher AES-256-GCMauth SHA256
persist-keypersist-tunverb 3Pre-Shared Key Mode
Section titled “Pre-Shared Key Mode”# Simpler but no individual client authentication# server.confdev tunsecret ta.keyserver 10.8.0.0 255.255.255.0
# client.confdev tunremote vpn.example.comsecret ta.keyPush Options
Section titled “Push Options”# Route specific subnets through the VPNpush "route 10.0.0.0 255.255.0.0"
# Route all traffic through the VPNpush "redirect-gateway def1"
# Exclude specific routes from VPNpush "route 192.168.1.0 255.255.255.0 net_gateway"
# Set DNSpush "dhcp-option DNS 10.8.0.1"SSH Tunneling
Section titled “SSH Tunneling”Local Port Forwarding
Section titled “Local Port Forwarding”Forward a local port to a remote host through an SSH connection:
# Access a remote MySQL server as if it were localssh -L 3306:mysql.internal:3306 user@bastion.example.com
# Now connect to localhost:3306 → tunneled to mysql.internal:3306mysql -h 127.0.0.1 -P 3306 -u app -pRemote Port Forwarding
Section titled “Remote Port Forwarding”Forward a remote port to a local host:
# Make a local web server accessible from the remote hostssh -R 8080:localhost:3000 user@remote.example.com
# On remote.example.com: curl http://localhost:8080 → reaches your local :3000Dynamic Port Forwarding (SOCKS Proxy)
Section titled “Dynamic Port Forwarding (SOCKS Proxy)”Create a SOCKS proxy that routes all traffic through the SSH connection:
# Create a SOCKS5 proxy on local port 1080ssh -D 1080 -C -q -N user@bastion.example.com
# Use the SOCKS proxycurl --socks5 localhost:1080 http://internal-service:8080/api
# Configure browser to use SOCKS5 proxy at localhost:1080Jump Hosts
Section titled “Jump Hosts”# Access an internal host through a bastionssh -J bastion.example.com user@internal-host
# With local port forwarding through a jump hostssh -L 5432:internal-db:5432 -J bastion.example.com user@bastion.example.com
# ProxyJump in ~/.ssh/configHost internal-db HostName 10.0.0.50 ProxyJump bastion
Host bastion HostName bastion.example.com User deploySSH Configuration
Section titled “SSH Configuration”Host vpn-socks HostName bastion.example.com User deploy DynamicForward 1080 ServerAliveInterval 60 ServerAliveCountMax 3 Compression yes IdentityFile ~/.ssh/id_ed25519Site-to-Site VPN
Section titled “Site-to-Site VPN”Hub-and-Spoke
Section titled “Hub-and-Spoke” ┌──────────┐ │ Hub VPN │ │ 10.0.0.1 │ └────┬──┬───┘ │ │ ┌────────┘ └────────┐ │ │┌───┴────┐ ┌────┴───┐│Spoke 1 │ │Spoke 2 ││10.0.0.2│ │10.0.0.3│└────────┘ └────────┘WireGuard hub config:
[Interface]PrivateKey = <hub_private_key>Address = 10.0.0.1/24ListenPort = 51820
[Peer]PublicKey = <spoke1_public_key>AllowedIPs = 10.0.0.2/32, 192.168.1.0/24
[Peer]PublicKey = <spoke2_public_key>AllowedIPs = 10.0.0.3/32, 192.168.2.0/24Every node connects to every other node. Scales as O(n^2) with the number of nodes:
# With 10 nodes, each node has 9 peer configurations# Manage with automation (wg-quick, Ansible, Terraform)VPN vs Zero-Trust
Section titled “VPN vs Zero-Trust”| Aspect | VPN | Zero-Trust |
|---|---|---|
| Network model | Perimeter-based | Identity-based |
| Access model | Full network access on connect | Least-privilege per resource |
| Trust | Trusted once connected | Never trust, always verify |
| Scalability | Hub-and-spoke bottlenecks | Scales per service |
| User experience | Connect, then access all | Authenticate per application |
When to Use Each
Section titled “When to Use Each”Use VPN when:- You need full network-level access (file shares, internal DNS, SSH)- You are connecting entire networks (site-to-site)- You have legacy systems that cannot integrate with zero-trust
Use zero-trust when:- You are primarily accessing cloud SaaS applications- You need fine-grained access control per resource- You have a distributed workforce without a central networkSplit Tunneling
Section titled “Split Tunneling”Split tunneling routes only specific traffic through the VPN, sending the rest directly to the Internet:
Without split tunnel: All traffic → VPN → Internet (slower, uses VPN bandwidth)
With split tunnel: Corporate traffic → VPN → Internal resources General traffic → Direct → Internet (faster)WireGuard client config for split tunnel:
[Interface]PrivateKey = <client_private_key>Address = 10.0.0.2/24
[Peer]PublicKey = <server_public_key>Endpoint = vpn.example.com:51820AllowedIPs = 10.0.0.0/24, 192.168.1.0/24 # Only corporate subnetsPersistentKeepalive = 25DNS Leak Prevention
Section titled “DNS Leak Prevention”When using split tunnel, DNS queries for non-VPN domains may go to the ISP DNS, leaking information:
# WireGuard: force DNS through VPN[Interface]DNS = 10.0.0.1 # VPN DNS server
# Or use DNS over HTTPS locallyDNS = 1.1.1.1#cloudflare-dns.comKill Switch
Section titled “Kill Switch”A kill switch blocks all traffic if the VPN connection drops, preventing data leaks:
# iptables kill switch (Linux)# Allow only VPN interface trafficiptables -P OUTPUT DROPiptables -A OUTPUT -o lo -j ACCEPTiptables -A OUTPUT -o wg0 -j ACCEPTiptables -A OUTPUT -d vpn.example.com -p udp --dport 51820 -j ACCEPTMTU Issues
Section titled “MTU Issues”VPN encapsulation adds overhead to each packet, which can cause fragmentation if the original MTU is Too large:
Ethernet MTU: 1500 bytesIP header: 20 bytesUDP header: 8 bytesWireGuard header: 32 bytes---------------------------VPN MTU: 1440 bytes (1500 - 20 - 8 - 32)# Set MTU on WireGuard interface# wg0.conf:[Interface]MTU = 1420 # conservative for most paths
# Detect MTU issues (connection works but large transfers hang)ping -M do -s 1400 vpn-server # should workping -M do -s 1500 vpn-server # may fail (fragmentation needed)Troubleshooting
Section titled “Troubleshooting”MTU Issues
Section titled “MTU Issues”# Path MTU discoveryping -M do -s <size> <host># Binary search for the largest size that works
# Fix: set appropriate MTU on the VPN interfaceNAT Traversal
Section titled “NAT Traversal”WireGuard and OpenVPN may not work behind restrictive NAT/firewalls:
# WireGuard: PersistentKeepalive keeps the NAT mapping alivePersistentKeepalive = 25
# OpenVPN: explicit NAT settingnat yes # in server configKeepalive
Section titled “Keepalive”# SSH: keep the connection aliveServerAliveInterval 60ServerAliveCountMax 3
# WireGuard: keep NAT mappings alivePersistentKeepalive = 25
# OpenVPN: keepalivekeepalive 10 120 # ping every 10s, restart after 120s silenceCommercial vs Self-Hosted
Section titled “Commercial vs Self-Hosted”| Solution | Type | Ease of Use | Privacy | Cost |
|---|---|---|---|---|
| WireGuard | Self-hosted | Medium | Full | Server cost |
| OpenVPN | Self-hosted | Medium | Full | Server cost |
| Tailscale | Managed WG | Easy | Partial | Free tier + paid |
| Headscale | Self-hosted | Medium | Full | Server cost |
| Cloudflare Tunnel | Managed | Easy | Partial | Free tier + paid |
Tailscale
Section titled “Tailscale”Tailscale builds on WireGuard with a coordination server for NAT traversal and key distribution:
# Install and connecttailscale up
# List connected peerstailscale status
# Advertise routes (for site-to-site)tailscale up --advertise-routes=192.168.1.0/24
# ACLs configured in the admin console (Tailscale-specific)Headscale
Section titled “Headscale”Open-source, self-hosted Tailscale control server:
# Headscale provides the coordination server# Clients use standard WireGuard# ACLs defined in a config fileCommon Pitfalls
Section titled “Common Pitfalls”Forgetting to Enable IP Forwarding
Section titled “Forgetting to Enable IP Forwarding”VPN servers must forward packets between interfaces:
# Enable IP forwarding (Linux)sysctl -w net.ipv4.ip_forward=1
# Persistent: add to /etc/sysctl.conf# net.ipv4.ip_forward = 1DNS Resolution Failure After VPN Connect
Section titled “DNS Resolution Failure After VPN Connect”If DNS queries fail after connecting to the VPN, the client is not using the correct DNS server. Configure the VPN to push the correct DNS settings, or manually configure the client”s DNS resolver.
Not Using PersistentKeepalive
Section titled “Not Using PersistentKeepalive”Without PersistentKeepaliveNAT mappings expire and the VPN connection drops silently. This is The most common cause of “VPN was working yesterday but stopped today.”
MTU Mismatch
Section titled “MTU Mismatch”If large file transfers work but web browsing hangs, or SSH works but SCP hangs, suspect MTU issues. Reduce the MTU on the VPN interface to 1360-1420 and test.
Using the Same Keys Across Environments
Section titled “Using the Same Keys Across Environments”WireGuard private keys must be unique per peer. Reusing keys across servers or clients causes Routing conflicts and security vulnerabilities. Generate a new key pair for every peer.
Summary
Section titled “Summary”This topic covers the essential concepts and techniques related to vpn and tunneling, including key principles and practical applications.
Key concepts include:
- core concepts and definitions
- key principles and frameworks
- practical applications
- common techniques and methods
- evaluation and critical analysis
A thorough understanding of these concepts, combined with regular practice and review, is essential for mastery of this topic.
Worked Examples
Section titled “Worked Examples”Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.