WireGuard VPN - Secure Remote Access

Resource Navigation: README | Firewall Basics | Network Segmentation | SSH Security Hardening


Summary

WireGuard is a modern, fast, and secure VPN protocol that provides encrypted remote access to your infrastructure with minimal configuration complexity. This comprehensive guide covers WireGuard installation and configuration for point-to-point connections, road warrior setups, site-to-site VPNs, multi-peer configurations, integration with firewalls and routing, DNS configuration, mobile client deployment, automated key management, high-availability VPN gateways, and monitoring. Learn production patterns for securing remote teams, jump host access, Kubernetes cluster connectivity, and zero-trust network architectures. Every pattern includes performance optimization, security hardening, and troubleshooting guides.

The Golden Rule: All remote access should go through encrypted VPN tunnels; never expose services directly to the internet.


Learning Objectives

By the end of this guide, you will be able to:


Prerequisites

Before setting up WireGuard, you should have:


Why WireGuard?

WireGuard vs. Traditional VPNs

WireGuard: ✅ Modern cryptography (Curve25519, ChaCha20, Poly1305) ✅ Minimal codebase (~4,000 lines vs. 400,000+ for OpenVPN) ✅ Fast (kernel space implementation) ✅ Simple configuration (vs. complex OpenVPN configs) ✅ Built into Linux kernel 5.6+ ✅ Low overhead (great for mobile) ✅ Roaming support (seamless network switching) ❌ Less mature than OpenVPN/IPSec ❌ No built-in user management (use peer keys)

OpenVPN: ✅ Mature and battle-tested ✅ Built-in user management ✅ Extensive features ✅ Works on any port/protocol ❌ Complex configuration ❌ Slower than WireGuard ❌ Large codebase (more attack surface) ❌ Higher CPU overhead

IPSec: ✅ Industry standard ✅ Hardware acceleration support ✅ Built into most OSes ❌ Complex configuration ❌ NAT traversal issues ❌ Large codebase ❌ Multiple competing implementations

Recommendation: WireGuard for modern infrastructure, OpenVPN for legacy/enterprise requirements.


Installation

Linux (Kernel 5.6+)

# Check kernel version
uname -r

# If kernel >= 5.6, WireGuard is built-in
# Just install tools

# Debian/Ubuntu
sudo apt update
sudo apt install wireguard wireguard-tools

# RHEL/CentOS 8+
sudo dnf install wireguard-tools

# Verify installation
which wg
wg --version

Linux (Older Kernels)

# Kernel < 5.6 requires kernel module

# Debian/Ubuntu
sudo apt install wireguard

# RHEL/CentOS 7
sudo yum install epel-release
sudo yum install kmod-wireguard wireguard-tools

# Load module
sudo modprobe wireguard
lsmod | grep wireguard

Windows

Download: https://www.wireguard.com/install/

1. Download WireGuard installer
2. Run installer (requires admin)
3. Launch WireGuard application
4. Import configuration or add tunnel manually

macOS

# Install via Homebrew
brew install wireguard-tools

# Or download from App Store:
# "WireGuard" by WireGuard Development Team

# Or download from website:
# https://www.wireguard.com/install/

Mobile

iOS: App Store → "WireGuard"
Android: Play Store → "WireGuard"

Free, open source, official clients

Key Generation

Generate Server Keys

# Create directory for keys
sudo mkdir -p /etc/wireguard/keys
cd /etc/wireguard/keys

# Generate server private key
wg genkey | sudo tee server_private.key
sudo chmod 600 server_private.key

# Generate server public key from private
sudo cat server_private.key | wg pubkey | sudo tee server_public.key

# View keys
cat server_private.key
cat server_public.key

Generate Client Keys

# For each client, generate key pair

# Client 1 (laptop)
wg genkey | sudo tee laptop_private.key
sudo cat laptop_private.key | wg pubkey | sudo tee laptop_public.key

# Client 2 (phone)
wg genkey | sudo tee phone_private.key
sudo cat phone_private.key | wg pubkey | sudo tee phone_public.key

# Client 3 (tablet)
wg genkey | sudo tee tablet_private.key
sudo cat tablet_private.key | wg pubkey | sudo tee tablet_public.key

# Secure permissions
sudo chmod 600 /etc/wireguard/keys/*_private.key

Point-to-Point VPN

Server Configuration

# /etc/wireguard/wg0.conf - Server configuration

[Interface]
# Server IP address within VPN
Address = 10.10.0.1/24

# Server private key
PrivateKey = <SERVER_PRIVATE_KEY>

# VPN listen port
ListenPort = 51820

# Enable IP forwarding and NAT (if routing traffic)
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

# Or with nftables:
# PostUp = nft add rule inet filter forward iifname wg0 accept; nft add rule inet nat postrouting oifname eth0 masquerade
# PostDown = nft delete rule inet filter forward iifname wg0 accept; nft delete rule inet nat postrouting oifname eth0 masquerade

# For routing only (no NAT):
# PostUp = sysctl -w net.ipv4.ip_forward=1
# PostDown = sysctl -w net.ipv4.ip_forward=0

# Clients
[Peer]
# Client (laptop) public key
PublicKey = <LAPTOP_PUBLIC_KEY>

# IP address assigned to client
AllowedIPs = 10.10.0.2/32

# Keep alive (for NAT traversal)
PersistentKeepalive = 25

Client Configuration

# laptop.conf - Client configuration

[Interface]
# Client IP address within VPN
Address = 10.10.0.2/24

# Client private key
PrivateKey = <LAPTOP_PRIVATE_KEY>

# DNS servers (optional)
DNS = 10.10.0.1, 1.1.1.1

[Peer]
# Server public key
PublicKey = <SERVER_PUBLIC_KEY>

# Server endpoint (public IP and port)
Endpoint = vpn.example.com:51820

# Routes through VPN (specific subnet only - split tunnel)
AllowedIPs = 10.10.0.0/24, 192.168.1.0/24

# Or route all traffic through VPN (full tunnel):
# AllowedIPs = 0.0.0.0/0, ::/0

# Keep connection alive (NAT traversal)
PersistentKeepalive = 25

Start VPN

# Server: Start WireGuard
sudo wg-quick up wg0

# Enable on boot
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0

# Check status
sudo wg show

# Client: Import configuration
sudo cp laptop.conf /etc/wireguard/
sudo wg-quick up laptop

# Or on systemd:
sudo systemctl enable wg-quick@laptop
sudo systemctl start wg-quick@laptop

# Verify connection
ping 10.10.0.1
sudo wg show

Road Warrior (Multi-Client) Setup

Server Configuration with Multiple Peers

# /etc/wireguard/wg0.conf - Road warrior server

[Interface]
Address = 10.10.0.1/24
PrivateKey = <SERVER_PRIVATE_KEY>
ListenPort = 51820

# Enable IP forwarding and NAT
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; iptables -A INPUT -p udp --dport 51820 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; iptables -D INPUT -p udp --dport 51820 -j ACCEPT

# Peer 1: Alice's laptop
[Peer]
PublicKey = <ALICE_LAPTOP_PUBLIC_KEY>
AllowedIPs = 10.10.0.2/32
PersistentKeepalive = 25

# Peer 2: Alice's phone
[Peer]
PublicKey = <ALICE_PHONE_PUBLIC_KEY>
AllowedIPs = 10.10.0.3/32
PersistentKeepalive = 25

# Peer 3: Bob's laptop
[Peer]
PublicKey = <BOB_LAPTOP_PUBLIC_KEY>
AllowedIPs = 10.10.0.4/32
PersistentKeepalive = 25

# Peer 4: Bob's phone
[Peer]
PublicKey = <BOB_PHONE_PUBLIC_KEY>
AllowedIPs = 10.10.0.5/32
PersistentKeepalive = 25

# Add more peers as needed (up to ~200 comfortable)

Enable IP Forwarding

# Enable IP forwarding permanently
sudo nano /etc/sysctl.conf

# Add or uncomment:
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

# Apply immediately
sudo sysctl -p

# Verify
cat /proc/sys/net/ipv4/ip_forward  # Should output: 1

Firewall Configuration

# Allow WireGuard port
sudo ufw allow 51820/udp

# Or with iptables
sudo iptables -A INPUT -p udp --dport 51820 -j ACCEPT

# Or with nftables
sudo nft add rule inet filter input udp dport 51820 accept

# See [Firewall Basics](ufw-firewall) for details

Mobile Client Configuration

Generate QR Code

# Install qrencode
sudo apt install qrencode

# Create client config file
cat > phone.conf <<EOF
[Interface]
Address = 10.10.0.3/24
PrivateKey = <PHONE_PRIVATE_KEY>
DNS = 10.10.0.1, 1.1.1.1

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF

# Generate QR code
qrencode -t ansiutf8 < phone.conf

# Or save to file
qrencode -o phone-qr.png -r phone.conf

# Display in terminal
cat phone.conf | qrencode -t ansiutf8

Mobile Setup

1. Open WireGuard app on phone
2. Tap "+" to add tunnel
3. Select "Create from QR code"
4. Scan QR code displayed on server
5. Name the tunnel (e.g., "Office VPN")
6. Toggle switch to connect
7. Verify connection

Android: May require "Always-on VPN" in settings
iOS: May require VPN configuration profile

Site-to-Site VPN

Scenario

Site A (Office)                    Site B (Data Center)
192.168.1.0/24                     10.0.0.0/24
         │                                │
         │                                │
    ┌────┴────┐                      ┌────┴────┐
    │ Gateway │ ←──── WireGuard ────→ │ Gateway │
    │ wg0:    │       (encrypted)     │ wg0:    │
    │10.10.0.1│                       │10.10.0.2│
    └─────────┘                       └─────────┘
         │                                │
         ↓                                ↓
  Devices can access ←────────────→ Devices can access
  10.0.0.0/24                       192.168.1.0/24

Site A Configuration

# /etc/wireguard/wg0.conf - Site A gateway

[Interface]
Address = 10.10.0.1/30
PrivateKey = <SITE_A_PRIVATE_KEY>
ListenPort = 51820

# Enable routing between networks
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# Site B gateway
PublicKey = <SITE_B_PUBLIC_KEY>
Endpoint = site-b.example.com:51820

# Allow Site B VPN IP and LAN subnet
AllowedIPs = 10.10.0.2/32, 10.0.0.0/24
PersistentKeepalive = 25

Site B Configuration

# /etc/wireguard/wg0.conf - Site B gateway

[Interface]
Address = 10.10.0.2/30
PrivateKey = <SITE_B_PRIVATE_KEY>
ListenPort = 51820

PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# Site A gateway
PublicKey = <SITE_A_PUBLIC_KEY>
Endpoint = site-a.example.com:51820

# Allow Site A VPN IP and LAN subnet
AllowedIPs = 10.10.0.1/32, 192.168.1.0/24
PersistentKeepalive = 25

Add Routes

# Site A: Route 10.0.0.0/24 through VPN
sudo ip route add 10.0.0.0/24 via 10.10.0.2 dev wg0

# Make persistent
echo "PostUp = ip route add 10.0.0.0/24 via 10.10.0.2 dev wg0" | sudo tee -a /etc/wireguard/wg0.conf
echo "PostDown = ip route del 10.0.0.0/24 via 10.10.0.2 dev wg0" | sudo tee -a /etc/wireguard/wg0.conf

# Site B: Route 192.168.1.0/24 through VPN
sudo ip route add 192.168.1.0/24 via 10.10.0.1 dev wg0

# Make persistent
echo "PostUp = ip route add 192.168.1.0/24 via 10.10.0.1 dev wg0" | sudo tee -a /etc/wireguard/wg0.conf
echo "PostDown = ip route del 192.168.1.0/24 via 10.10.0.1 dev wg0" | sudo tee -a /etc/wireguard/wg0.conf

# Test connectivity
# From Site A:
ping 10.0.0.10  # Server in Site B

# From Site B:
ping 192.168.1.10  # Server in Site A

Advanced Configuration

Split Tunnel vs. Full Tunnel

Split Tunnel (route only specific traffic through VPN):

# Client configuration - Split tunnel
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = vpn.example.com:51820

# Only route internal networks through VPN
AllowedIPs = 10.10.0.0/24, 192.168.0.0/16, 172.16.0.0/12

# Internet traffic goes direct (not through VPN)
PersistentKeepalive = 25

Full Tunnel (route all traffic through VPN):

# Client configuration - Full tunnel
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = vpn.example.com:51820

# Route all IPv4 and IPv6 traffic through VPN
AllowedIPs = 0.0.0.0/0, ::/0

PersistentKeepalive = 25

DNS Configuration

# /etc/wireguard/wg0.conf - Client with DNS

[Interface]
Address = 10.10.0.2/24
PrivateKey = <CLIENT_PRIVATE_KEY>

# Use VPN server's DNS
DNS = 10.10.0.1

# Or use multiple DNS servers
DNS = 10.10.0.1, 1.1.1.1, 8.8.8.8

# Or specify search domain
DNS = 10.10.0.1, example.com

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Peer-to-Peer (Mesh) Network

# Each peer can connect to multiple other peers

# Peer A configuration
[Interface]
Address = 10.10.0.1/24
PrivateKey = <PEER_A_PRIVATE>
ListenPort = 51820

[Peer]
# Peer B
PublicKey = <PEER_B_PUBLIC>
Endpoint = peer-b.example.com:51820
AllowedIPs = 10.10.0.2/32

[Peer]
# Peer C
PublicKey = <PEER_C_PUBLIC>
Endpoint = peer-c.example.com:51820
AllowedIPs = 10.10.0.3/32

# Peer A can now communicate with both B and C
# B and C can also peer with each other for full mesh

Dynamic Peer Management

# Add peer without restarting
sudo wg set wg0 peer <PUBLIC_KEY> allowed-ips 10.10.0.10/32

# Remove peer
sudo wg set wg0 peer <PUBLIC_KEY> remove

# Update endpoint
sudo wg set wg0 peer <PUBLIC_KEY> endpoint newserver.example.com:51820

# Save current configuration
sudo wg showconf wg0 > /etc/wireguard/wg0.conf

Monitoring and Management

Check VPN Status

# Show all WireGuard interfaces
sudo wg show

# Show specific interface
sudo wg show wg0

# Show detailed information
sudo wg show all dump

# Example output:
# interface: wg0
#   public key: <SERVER_PUBLIC_KEY>
#   private key: (hidden)
#   listening port: 51820
#
# peer: <CLIENT_1_PUBLIC_KEY>
#   endpoint: 203.0.113.42:54321
#   allowed ips: 10.10.0.2/32
#   latest handshake: 1 minute, 23 seconds ago
#   transfer: 15.2 MiB received, 8.7 MiB sent
#   persistent keepalive: every 25 seconds

Monitor Bandwidth

# Real-time bandwidth monitoring
watch -n 1 sudo wg show wg0

# Log bandwidth usage
#!/bin/bash
# /usr/local/bin/wg-monitor.sh

while true; do
    echo "$(date) - WireGuard Stats:"
    sudo wg show wg0 | grep -A 2 "peer:" | grep "transfer"
    sleep 60
done >> /var/log/wireguard-bandwidth.log

Automated Peer Management Script

#!/bin/bash
# wg-add-client.sh - Add new WireGuard client

CLIENT_NAME="$1"

if [ -z "$CLIENT_NAME" ]; then
    echo "Usage: $0 <client_name>"
    exit 1
fi

# Generate keys
CLIENT_PRIVATE=$(wg genkey)
CLIENT_PUBLIC=$(echo "$CLIENT_PRIVATE" | wg pubkey)

# Get next available IP
LAST_IP=$(sudo wg show wg0 allowed-ips | grep -oE '10\.10\.0\.[0-9]+' | cut -d. -f4 | sort -n | tail -1)
NEXT_IP=$((LAST_IP + 1))
CLIENT_IP="10.10.0.$NEXT_IP"

# Get server public key
SERVER_PUBLIC=$(sudo cat /etc/wireguard/keys/server_public.key)

# Add peer to server
sudo wg set wg0 peer "$CLIENT_PUBLIC" allowed-ips "$CLIENT_IP/32"

# Save configuration
sudo wg showconf wg0 > /etc/wireguard/wg0.conf

# Generate client configuration
cat > "/etc/wireguard/clients/$CLIENT_NAME.conf" <<EOF
[Interface]
Address = $CLIENT_IP/24
PrivateKey = $CLIENT_PRIVATE
DNS = 10.10.0.1, 1.1.1.1

[Peer]
PublicKey = $SERVER_PUBLIC
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF

echo "✓ Client $CLIENT_NAME created"
echo "  IP: $CLIENT_IP"
echo "  Config: /etc/wireguard/clients/$CLIENT_NAME.conf"
echo ""
echo "Generate QR code:"
echo "  qrencode -t ansiutf8 < /etc/wireguard/clients/$CLIENT_NAME.conf"

Security Hardening

Restrict Access by Source IP

# Only allow connections from specific countries/networks

# In firewall rules (before WireGuard starts)
PostUp = iptables -A INPUT -p udp --dport 51820 -s 203.0.113.0/24 -j ACCEPT
PostUp = iptables -A INPUT -p udp --dport 51820 -j DROP

Key Rotation

#!/bin/bash
# rotate-server-key.sh - Rotate WireGuard server key

# Generate new server key
NEW_PRIVATE=$(wg genkey)
NEW_PUBLIC=$(echo "$NEW_PRIVATE" | wg pubkey)

echo "New server public key: $NEW_PUBLIC"
echo ""
echo "⚠️  Update all client configurations with new server public key!"
echo ""
read -p "Press Enter after updating clients..."

# Backup old config
sudo cp /etc/wireguard/wg0.conf /etc/wireguard/wg0.conf.backup

# Update server private key
sudo sed -i "s/^PrivateKey = .*/PrivateKey = $NEW_PRIVATE/" /etc/wireguard/wg0.conf

# Restart WireGuard
sudo systemctl restart wg-quick@wg0

echo "✓ Server key rotated successfully"
echo "  Backup saved to: /etc/wireguard/wg0.conf.backup"

Rate Limiting

# Limit connection attempts to prevent DoS
sudo iptables -A INPUT -p udp --dport 51820 -m state --state NEW -m recent --set
sudo iptables -A INPUT -p udp --dport 51820 -m state --state NEW -m recent --update --seconds 60 --hitcount 10 -j DROP

High Availability

Multiple VPN Servers (Failover)

# Client configuration with multiple servers

[Interface]
Address = 10.10.0.2/24
PrivateKey = <CLIENT_PRIVATE_KEY>
DNS = 10.10.0.1

# Primary server
[Peer]
PublicKey = <SERVER1_PUBLIC_KEY>
Endpoint = vpn1.example.com:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

# Backup server (manual failover)
# Uncomment if primary fails
# [Peer]
# PublicKey = <SERVER2_PUBLIC_KEY>
# Endpoint = vpn2.example.com:51820
# AllowedIPs = 0.0.0.0/0
# PersistentKeepalive = 25

Automated Failover Script

#!/bin/bash
# wireguard-failover.sh - Automatic VPN failover

PRIMARY="vpn1.example.com"
BACKUP="vpn2.example.com"
INTERFACE="wg0"

while true; do
    # Check if primary is reachable
    if ping -c 1 -W 5 $PRIMARY > /dev/null 2>&1; then
        # Primary is up, ensure we're using it
        CURRENT=$(sudo wg show $INTERFACE | grep endpoint | awk '{print $2}' | cut -d: -f1)
        if [ "$CURRENT" != "$PRIMARY" ]; then
            echo "Failing back to primary: $PRIMARY"
            sudo wg set $INTERFACE peer <PUBLIC_KEY> endpoint $PRIMARY:51820
        fi
    else
        # Primary is down, switch to backup
        echo "Primary down, failing over to: $BACKUP"
        sudo wg set $INTERFACE peer <PUBLIC_KEY> endpoint $BACKUP:51820
    fi
    
    sleep 30
done

Troubleshooting

Connection Not Establishing

# Check WireGuard is running
sudo systemctl status wg-quick@wg0
sudo wg show

# Check firewall allows UDP 51820
sudo iptables -L INPUT -n | grep 51820
sudo ufw status | grep 51820

# Check IP forwarding enabled
cat /proc/sys/net/ipv4/ip_forward  # Should be: 1

# Check routing
ip route show
ip addr show wg0

# Test connectivity to server
ping vpn.example.com
nc -u -v vpn.example.com 51820

# Check logs
sudo journalctl -u wg-quick@wg0 -f
sudo dmesg | grep wireguard

No Handshake

# Check peer configuration
sudo wg show wg0

# "latest handshake" should show recent time
# If "never" or old timestamp:

# 1. Verify public keys match
#    Server config peer = client public key
#    Client config peer = server public key

# 2. Check endpoint is correct
#    Client must have correct server IP:port

# 3. Verify firewall allows UDP traffic

# 4. Check time synchronization
date  # Must be accurate for handshake

# Force handshake
ping 10.10.0.1  # Ping server through VPN

Can't Reach Internal Networks

# Verify AllowedIPs includes target subnet
sudo wg show wg0 | grep "allowed ips"

# Check routing
ip route | grep wg0

# Verify IP forwarding on server
ssh vpn-server "cat /proc/sys/net/ipv4/ip_forward"

# Check NAT/firewall rules on server
ssh vpn-server "sudo iptables -L FORWARD -n -v"
ssh vpn-server "sudo iptables -t nat -L POSTROUTING -n -v"

# Test from server to internal network
ssh vpn-server "ping 192.168.1.10"

Performance Issues

# Check MTU settings
ip link show wg0

# Reduce MTU if needed (default 1420)
sudo ip link set wg0 mtu 1400

# Make permanent in config
# [Interface]
# MTU = 1400

# Check bandwidth usage
sudo wg show wg0 | grep transfer

# Monitor latency
ping -i 0.2 10.10.0.1

# Check CPU usage
top -p $(pgrep wireguard)

Integration with Infrastructure

Kubernetes Cluster Access

# Access Kubernetes cluster through VPN

[Interface]
Address = 10.10.0.2/24
PrivateKey = <CLIENT_PRIVATE_KEY>
DNS = 10.10.0.1

[Peer]
PublicKey = <VPN_SERVER_PUBLIC_KEY>
Endpoint = vpn.example.com:51820

# Route to Kubernetes pod and service networks
AllowedIPs = 10.10.0.0/24, 10.244.0.0/16, 10.96.0.0/16
PersistentKeepalive = 25

# Now kubectl works through VPN
# kubectl --server=https://10.244.0.1:6443 get nodes

Jump Host Access

# SSH through VPN to jump host, then to internal servers

# ~/.ssh/config
Host jumphost
    HostName 10.10.0.1
    User admin
    IdentityFile ~/.ssh/id_ed25519

Host internal-*
    ProxyJump jumphost
    User admin
    IdentityFile ~/.ssh/id_ed25519

Host internal-web
    HostName 192.168.1.10

Host internal-db
    HostName 192.168.1.20

# Usage:
# Connect to VPN first
# Then: ssh internal-web

Docker Network

# docker-compose.yml - WireGuard container

version: '3.8'

services:
  wireguard:
    image: linuxserver/wireguard:latest
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/New_York
      - SERVERURL=vpn.example.com
      - SERVERPORT=51820
      - PEERS=5
      - PEERDNS=auto
    volumes:
      - ./config:/config
      - /lib/modules:/lib/modules
    ports:
      - 51820:51820/udp
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped

Security Best Practices

WireGuard Security Checklist

☑ Strong key management (rotate annually)
☑ Firewall restricts WireGuard port (UDP 51820)
☑ Rate limiting on VPN port
☑ PersistentKeepalive for NAT traversal
☑ Minimal AllowedIPs (split tunnel when possible)
☑ DNS through VPN (prevent leaks)
☑ Regular key rotation
☑ Inactive peers removed promptly
☑ Audit logs enabled
☑ Monitoring for unauthorized access
☑ Backup VPN server for failover
☑ Documentation for peer onboarding/offboarding
☑ Mobile device policies (MDM integration)
☑ Fail2ban protecting WireGuard (optional)

What's Next?

After setting up WireGuard VPN:

Network Security:

Access Control:

Advanced:


Additional Resources

Official Documentation

Tutorials & Guides

Tools


Change Log

Choose Theme

Your selection is saved locally.

Neural Cacophony
Aperture v2
Flux v1
Mosaic Chaos
Nexus v1
Nexus Zest
Prism v2
Synapse