VPN Server Setup
Configure WireGuard VPN servers for secure network connectivity between Zero clients and your infrastructure. This guide covers single-server and multi-region deployments.
Architecture: Zero uses WireGuard for its VPN layer due to its
modern cryptography, minimal attack surface, and excellent performance.
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ Client Devices │
│ (Linux, Windows, macOS, Android, iOS) │
└─────────────────────────────────────────────────────────────┘
│
│ WireGuard (UDP 51820)
▼
┌─────────────────────────────────────────────────────────────┐
│ VPN Gateway Cluster │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ US-East │ │ EU-West │ │ APAC │ │
│ │ Gateway │ │ Gateway │ │ Gateway │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Zero Backend │
│ (API, Database, File Sync) │
└─────────────────────────────────────────────────────────────┘ Single Server Setup
Install WireGuard
# Ubuntu/Debian
sudo apt update
sudo apt install wireguard wireguard-tools
# RHEL/Rocky/Alma
sudo dnf install wireguard-tools
# Verify installation
wg --version Generate Server Keys
# Generate server keypair
wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key
# Secure the private key
chmod 600 /etc/wireguard/server_private.key
# View keys
cat /etc/wireguard/server_private.key
cat /etc/wireguard/server_public.key Server Configuration
# /etc/wireguard/wg0.conf
[Interface]
Address = 10.100.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY_HERE
# Enable IP forwarding
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# Peer configuration is managed dynamically by Zero API
# Clients are added via: wg set wg0 peer PUBLIC_KEY allowed-ips IP/32 Enable IP Forwarding
# Permanent IP forwarding
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv6.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p Start VPN Service
# Start WireGuard
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
# Check status
sudo wg show API Integration
Zero manages VPN peers dynamically through its API:
VPN Management Service
# /opt/zero/vpn-agent/config.yaml
api_url: http://localhost:8080
api_token: your-vpn-service-token
wireguard_interface: wg0
subnet: 10.100.0.0/24
sync_interval: 30s
# IP allocation range
ip_pool:
start: 10.100.0.10
end: 10.100.0.254 Dynamic Peer Management
The VPN agent syncs with the Zero API to manage peers:
#!/bin/bash
# Example: Add peer from API response
# Fetch active device configurations
PEERS=$(curl -s -H "Authorization: Bearer $TOKEN" \
https://api.zero.io/api/v1/vpn/peers)
# Apply each peer
echo "$PEERS" | jq -c '.[]' | while read peer; do
PUBLIC_KEY=$(echo $peer | jq -r '.public_key')
ALLOWED_IP=$(echo $peer | jq -r '.allowed_ip')
wg set wg0 peer $PUBLIC_KEY allowed-ips $ALLOWED_IP/32
done Multi-Region Deployment
Regional Configuration
| Region | Subnet | Endpoint |
|---|---|---|
| US-East | 10.100.0.0/24 | us-vpn.zero.io:51820 |
| EU-West | 10.101.0.0/24 | eu-vpn.zero.io:51820 |
| APAC | 10.102.0.0/24 | apac-vpn.zero.io:51820 |
Geographic Routing
Use DNS-based routing to direct clients to the nearest VPN server:
# Cloudflare Load Balancer example
# vpn.zero.io resolves based on client location
# US clients → us-vpn.zero.io
# EU clients → eu-vpn.zero.io
# APAC clients → apac-vpn.zero.io Inter-Region Connectivity
# Site-to-site tunnel between regions
# US-East server (/etc/wireguard/wg1.conf)
[Interface]
Address = 10.200.0.1/24
ListenPort = 51821
PrivateKey = US_PRIVATE_KEY
[Peer]
# EU-West server
PublicKey = EU_PUBLIC_KEY
Endpoint = eu-vpn.zero.io:51821
AllowedIPs = 10.101.0.0/24, 10.200.0.2/32
PersistentKeepalive = 25
[Peer]
# APAC server
PublicKey = APAC_PUBLIC_KEY
Endpoint = apac-vpn.zero.io:51821
AllowedIPs = 10.102.0.0/24, 10.200.0.3/32
PersistentKeepalive = 25 High Availability
Active-Passive Setup
# Using keepalived for VIP failover
# /etc/keepalived/keepalived.conf
vrrp_instance VPN_VIP {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass secretpassword
}
virtual_ipaddress {
203.0.113.100/32 # Floating VIP
}
track_script {
check_wg
}
}
vrrp_script check_wg {
script "/usr/local/bin/check-wireguard.sh"
interval 2
weight -20
} Load Balancing (Active-Active)
# Using ECMP or cloud load balancer
# AWS Network Load Balancer
resource "aws_lb" "vpn" {
name = "zero-vpn"
internal = false
load_balancer_type = "network"
subnet_mapping {
subnet_id = aws_subnet.public.id
}
}
resource "aws_lb_target_group" "vpn" {
name = "zero-vpn"
port = 51820
protocol = "UDP"
vpc_id = aws_vpc.main.id
health_check {
protocol = "TCP"
port = 8080 # Health check endpoint
}
} Security Hardening
Firewall Rules
# Allow only WireGuard and health checks
sudo ufw default deny incoming
sudo ufw allow 51820/udp comment "WireGuard"
sudo ufw allow 8080/tcp from 10.0.0.0/8 comment "Health checks"
sudo ufw enable Connection Rate Limiting
# Limit new connections with iptables
iptables -A INPUT -p udp --dport 51820 -m state --state NEW \
-m recent --set --name wg_ratelimit
iptables -A INPUT -p udp --dport 51820 -m state --state NEW \
-m recent --update --seconds 60 --hitcount 10 --name wg_ratelimit \
-j DROP Automatic Key Rotation
Zero rotates client keys automatically. Server key rotation:
#!/bin/bash
# Rotate server keys quarterly
# Generate new keypair
NEW_PRIVATE=$(wg genkey)
NEW_PUBLIC=$(echo $NEW_PRIVATE | wg pubkey)
# Update configuration
sed -i "s/PrivateKey = .*/PrivateKey = $NEW_PRIVATE/" /etc/wireguard/wg0.conf
# Notify API of new public key
curl -X POST https://api.zero.io/api/v1/vpn/rotate-key \
-H "Authorization: Bearer $TOKEN" \
-d "{\"region\": \"us-east\", \"public_key\": \"$NEW_PUBLIC\"}"
# Restart WireGuard
systemctl restart wg-quick@wg0 Monitoring
Prometheus Metrics
# Using prometheus-wireguard-exporter
docker run -d \
--name wg-exporter \
--network host \
--cap-add NET_ADMIN \
-v /etc/wireguard:/etc/wireguard:ro \
mindflavor/prometheus-wireguard-exporter Available Metrics
wireguard_peer_rx_bytes- Bytes received per peerwireguard_peer_tx_bytes- Bytes transmitted per peerwireguard_peer_latest_handshake- Last handshake timestampwireguard_peers_count- Number of active peers
Alerting Rules
groups:
- name: wireguard
rules:
- alert: WireGuardPeerDisconnected
expr: time() - wireguard_peer_latest_handshake > 300
for: 5m
labels:
severity: warning
annotations:
summary: "WireGuard peer {{ $labels.peer }} disconnected"
- alert: WireGuardHighBandwidth
expr: rate(wireguard_peer_tx_bytes[5m]) > 100000000 # 100 MB/s
for: 10m
labels:
severity: warning
annotations:
summary: "High bandwidth usage on WireGuard peer" Troubleshooting
| Issue | Diagnosis | Solution |
|---|---|---|
| Client can't connect | sudo wg show | Check firewall, verify public key |
| Handshake fails | Check endpoint reachability | Verify UDP 51820 is open |
| No traffic after connect | ip route | Check IP forwarding, AllowedIPs |
| High latency | mtr vpn.zero.io | Use closer regional server |
Debug Commands
# Show WireGuard status
sudo wg show
# Check interface
ip addr show wg0
# View routing table
ip route | grep wg0
# Test connectivity
ping 10.100.0.1
# Packet capture
sudo tcpdump -i wg0 -n Next Steps
- Requirements - Full system requirements
- Security Model - How Zero secures data
- API Documentation - VPN management APIs