Skip to content

Portal | Level: L2: Operations | Topics: VPN & Tunneling, Firewalls, Linux Networking Tools | Domain: Networking

VPN & Tunneling - Primer

Why This Matters

Networks have boundaries. VPNs dissolve them. Whether you are connecting remote offices, giving developers access to staging environments, linking cloud VPCs, or just tunneling past a restrictive firewall — you need tunnels. Get them wrong and you either have no connectivity or, worse, a false sense of security with traffic flowing in the clear.

This primer covers WireGuard (the modern standard), OpenVPN (the incumbent), IPSec (the enterprise standard), and SSH tunnels (the pragmatic quick-fix). You will understand when to use each, how they work, and how to configure them without shooting yourself in the foot.


Tunnel Types: L2 vs L3

Layer 2 (TAP / Bridge):
┌──────────────────────────────────────────┐
│  Ethernet frames traverse the tunnel     │
│  Both sides appear to be on the same LAN │
│  Broadcasts work, ARP works              │
│  Used for: bridging sites, VXLAN         │
└──────────────────────────────────────────┘

Layer 3 (TUN / Routed):
┌──────────────────────────────────────────┐
│  IP packets traverse the tunnel          │
│  Each side has its own subnet            │
│  No broadcasts across the tunnel         │
│  Used for: most VPNs, site-to-site       │
└──────────────────────────────────────────┘

Most VPNs are Layer 3. Layer 2 tunnels are used when you need broadcast traffic (legacy protocols, DHCP across sites) or full ethernet bridging.


WireGuard

WireGuard is the modern VPN protocol: ~4000 lines of kernel code, simple configuration, excellent performance. It is now built into the Linux kernel (5.6+).

Who made it: WireGuard was created by Jason Donenfeld and merged into the Linux kernel in March 2020 (kernel 5.6). Linus Torvalds himself praised it: "Can I just once again state my love for it and hope it gets merged soon? Maybe the code isn't perfect, but I've skimmed it, and compared to the horrors that are OpenVPN and IPSec, it's a work of art." The ~4000 lines of code compares to ~100,000 for OpenVPN and ~400,000 for IPSec (strongSwan).

How WireGuard Works

┌───────────┐                              ┌───────────┐
│  Peer A    │     UDP (port 51820)         │  Peer B    │
│            │◄────────────────────────────▶│            │
│ Public Key │   Noise Protocol Framework   │ Public Key │
│ Private Key│   ChaCha20 + Poly1305       │ Private Key│
│ wg0: 10.0.0.1                            │ wg0: 10.0.0.2
└───────────┘                              └───────────┘

Key concepts: - Peers identify each other by public keys (not certificates) - All traffic is UDP (single port) - Cryptokey routing: "packets from this IP go to this peer" - Silent by design: does not respond to unauthenticated packets

WireGuard Configuration

Server (Peer A):

# /etc/wireguard/wg0.conf
[Interface]
PrivateKey = SERVER_PRIVATE_KEY_HERE
Address = 10.0.0.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# Client B
PublicKey = CLIENT_B_PUBLIC_KEY_HERE
AllowedIPs = 10.0.0.2/32

[Peer]
# Client C
PublicKey = CLIENT_C_PUBLIC_KEY_HERE
AllowedIPs = 10.0.0.3/32

Client (Peer B):

# /etc/wireguard/wg0.conf
[Interface]
PrivateKey = CLIENT_B_PRIVATE_KEY_HERE
Address = 10.0.0.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = SERVER_PUBLIC_KEY_HERE
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0          # route ALL traffic through VPN
# AllowedIPs = 10.0.0.0/24      # route only VPN subnet (split tunnel)
PersistentKeepalive = 25

Key Generation

# Generate key pair
wg genkey | tee privatekey | wg pubkey > publickey

# Or one-liner
wg genkey | tee /etc/wireguard/privatekey | wg pubkey > /etc/wireguard/publickey
chmod 600 /etc/wireguard/privatekey

# Generate preshared key (additional layer of security)
wg genpsk > preshared.key

Managing WireGuard

# Bring interface up/down
wg-quick up wg0
wg-quick down wg0

# Enable at boot
systemctl enable wg-quick@wg0

# Show status
wg show
wg show wg0

# Add a peer dynamically (without restarting)
wg set wg0 peer CLIENT_PUBLIC_KEY allowed-ips 10.0.0.4/32

# Remove a peer
wg set wg0 peer CLIENT_PUBLIC_KEY remove

AllowedIPs = Routing Table

Gotcha: WireGuard has no concept of "connection state." It is stateless by design. A peer is considered "connected" if it has sent a valid handshake recently. If a peer goes offline, WireGuard does not detect this -- it simply stops receiving packets. PersistentKeepalive = 25 sends a keepalive packet every 25 seconds, which is needed for peers behind NAT (to keep the NAT mapping alive) and for detecting dead peers.

This is the concept that trips people up. AllowedIPs serves two purposes:

  1. Outgoing: which destination IPs get routed through this peer
  2. Incoming: which source IPs are accepted from this peer
AllowedIPs = 0.0.0.0/0         # full tunnel (all traffic through VPN)
AllowedIPs = 10.0.0.0/24       # only VPN subnet
AllowedIPs = 10.0.0.2/32       # only this specific peer IP
AllowedIPs = 10.0.0.0/24, 192.168.1.0/24  # VPN + remote LAN

OpenVPN

OpenVPN is the battle-tested workhorse. More complex than WireGuard but extremely flexible and well-understood.

OpenVPN Architecture

┌───────────┐                              ┌───────────┐
│  Client    │   TLS (TCP/UDP 1194)        │  Server    │
│            │◄────────────────────────────▶│            │
│ Client cert│   OpenSSL / TLS channel     │ Server cert│
│ + CA cert  │   + data channel (AES-GCM)  │ + CA cert  │
│ tun0: 10.8.0.2                           │ tun0: 10.8.0.1
└───────────┘                              └───────────┘

Basic Server Config

# /etc/openvpn/server.conf
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh2048.pem
server 10.8.0.0 255.255.255.0
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 1.1.1.1"
keepalive 10 120
cipher AES-256-GCM
auth SHA256
persist-key
persist-tun
status /var/log/openvpn-status.log
log-append /var/log/openvpn.log
verb 3

UDP vs TCP

UDP (default, recommended):
  + Lower latency
  + No TCP-over-TCP meltdown
  + Better performance
  - May be blocked by firewalls

TCP (fallback):
  + Passes through most firewalls
  + Can run on port 443 (looks like HTTPS)
  - Higher latency
  - TCP-over-TCP causes retransmission storms

Always try UDP first. Use TCP only when UDP is blocked.

Under the hood: "TCP-over-TCP meltdown" is a real performance problem. When you encapsulate TCP traffic inside a TCP tunnel, both layers independently retry lost packets. A single packet loss triggers retransmission at both levels, causing exponential backoff storms. This is why WireGuard uses UDP exclusively and why OpenVPN defaults to UDP. If you must use TCP (restrictive firewalls), expect 20-40% throughput degradation under any packet loss.


IPSec

IPSec is the enterprise/carrier-grade VPN protocol. It is complex but deeply embedded in networking hardware (every router and firewall speaks IPSec).

IPSec Phases

Phase 1 (IKE SA):
  Peers authenticate each other
  Negotiate encryption algorithms
  Establish a secure management channel
  → Result: IKE Security Association

Phase 2 (IPSec SA):
  Negotiate data encryption parameters
  Define what traffic to protect (selectors)
  → Result: IPSec Security Association (the actual tunnel)

┌──────────────────────────────────────────────────────────┐
│                     Phase 1 (IKE)                        │
│  ┌─────────┐   IKE_SA_INIT   ┌─────────┐               │
│  │ Peer A   │◄──────────────▶│ Peer B   │               │
│  │          │   IKE_AUTH      │          │               │
│  └─────────┘                 └─────────┘               │
│                                                          │
│                     Phase 2 (Child SA)                   │
│  ┌─────────┐   CREATE_CHILD  ┌─────────┐               │
│  │ Peer A   │◄──────────────▶│ Peer B   │               │
│  │          │   ESP traffic   │          │               │
│  └─────────┘                 └─────────┘               │
└──────────────────────────────────────────────────────────┘

IPSec Modes

Transport Mode:
  Original IP header preserved
  Only payload encrypted
  Used for: host-to-host communication

Tunnel Mode:
  Entire original packet encrypted
  New IP header added
  Used for: site-to-site VPN (most common)

strongSwan (Linux IPSec)

# /etc/ipsec.conf
config setup
    charondebug="ike 2, knl 2, net 2"

conn site-to-site
    authby=secret
    left=203.0.113.1
    leftsubnet=10.1.0.0/24
    right=198.51.100.1
    rightsubnet=10.2.0.0/24
    ike=aes256-sha256-modp2048
    esp=aes256-sha256
    keyexchange=ikev2
    auto=start
# /etc/ipsec.secrets
203.0.113.1 198.51.100.1 : PSK "shared-secret-here"

SSH Tunnels

SSH tunnels are the duct tape of networking. Not meant for production VPNs, but invaluable for quick access to services behind firewalls.

Local Port Forwarding

Forward a local port to a remote service through the SSH server:

# Access remote database as if it were local
ssh -L 5432:db.internal:5432 bastion.example.com
# Now connect to localhost:5432 to reach db.internal:5432

# Syntax: ssh -L local_port:remote_host:remote_port ssh_server
┌──────────┐          ┌──────────┐          ┌──────────┐
│  Laptop  │──SSH────▶│ Bastion  │────────▶│ Database  │
│ :5432    │  tunnel  │          │  direct  │ :5432     │
└──────────┘          └──────────┘          └──────────┘

Remote Port Forwarding

Expose a local service to the remote network:

# Make local web server accessible on the bastion
ssh -R 8080:localhost:3000 bastion.example.com
# Now bastion:8080 reaches your localhost:3000

# Syntax: ssh -R remote_port:local_host:local_port ssh_server

Dynamic Port Forwarding (SOCKS Proxy)

# Create a SOCKS5 proxy through the SSH server
ssh -D 1080 bastion.example.com
# Configure your browser to use SOCKS5 proxy at localhost:1080
# All browsing traffic goes through the bastion

SSH Tunnel Options

# Background the tunnel (no shell)
ssh -fN -L 5432:db.internal:5432 bastion.example.com

# With keepalive (prevent timeout)
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 \
    -fN -L 5432:db.internal:5432 bastion.example.com

# With autossh (auto-reconnect on failure)
autossh -M 0 -o ServerAliveInterval=60 -o ServerAliveCountMax=3 \
    -fN -L 5432:db.internal:5432 bastion.example.com

Split Tunneling vs Full Tunneling

Full Tunnel:
  ALL traffic goes through the VPN
  + Simple, everything protected
  - VPN server handles all bandwidth
  - Slow for internet browsing
  WireGuard: AllowedIPs = 0.0.0.0/0

Split Tunnel:
  Only specific subnets go through the VPN
  + Internet traffic goes direct (fast)
  + Less load on VPN server
  - Some traffic is not protected
  WireGuard: AllowedIPs = 10.0.0.0/24, 192.168.0.0/16

Choose split tunneling for developer access to internal resources. Choose full tunneling for compliance requirements or untrusted networks.

Interview tip: A common interview question: "What is split tunneling and when would you use it?" Answer: split tunneling routes only specific subnets through the VPN while internet traffic goes direct. Use it for developer VPNs to avoid bottlenecking all web browsing through the corporate VPN. Avoid it when compliance requires all traffic to be inspected (PCI-DSS, HIPAA environments).


Protocol Comparison

                WireGuard    OpenVPN      IPSec       SSH Tunnel
──────────────  ──────────   ──────────   ──────────  ──────────
Performance     Excellent    Good         Good        Fair
Simplicity      Very simple  Moderate     Complex     Simple
Maturity        Newer        Battle-tested Decades    Ancient
Kernel support  In-kernel    Userspace    In-kernel   Userspace
NAT traversal   Built-in     Built-in     Needs help  Built-in
Port            UDP only     UDP or TCP   ESP/UDP     TCP
Use case        General VPN  Enterprise   Site-to-site Quick access
Audit surface   ~4000 LOC    ~100K LOC    Very large  Very large

When to Use What

Developer VPN / road warrior    → WireGuard
Enterprise with PKI/LDAP        → OpenVPN
Site-to-site (hardware routers) → IPSec
Quick access to one service     → SSH tunnel
Overlay networking (containers) → WireGuard or VXLAN

Wiki Navigation

Prerequisites