2026-01-27 17:35:45 -05:00
---
name : vpn-setup
description : Configure WireGuard, OpenVPN, and cloud VPNs. Implement secure remote access and site-to-site connectivity. Use when setting up secure network tunnels.
license : MIT
metadata :
author : devops-skills
version : "1.0"
---
# VPN Setup
Configure secure VPN tunnels for remote access and site connectivity.
2026-03-24 18:02:50 -04:00
## When to Use This Skill
Use this skill when:
- Setting up secure remote access for employees or contractors
- Connecting on-premises networks to cloud environments (site-to-site)
- Encrypting traffic between data centers or regions
- Implementing a mesh VPN for distributed infrastructure
- Providing secure access to internal services without exposing them publicly
## Prerequisites
- Linux server with a public IP for VPN endpoint
- Root/sudo access on the VPN server
- Firewall rules allowing VPN traffic (UDP 51820 for WireGuard, UDP 1194 for OpenVPN)
- DNS configured for VPN hostname (optional but recommended)
- Understanding of IP subnetting and routing
2026-01-27 17:35:45 -05:00
## WireGuard
2026-03-24 18:02:50 -04:00
### Server Setup
2026-01-27 17:35:45 -05:00
2026-03-24 18:02:50 -04:00
```bash
# Install WireGuard (Ubuntu/Debian)
apt update && apt install -y wireguard
# Generate server keys
wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key
chmod 600 /etc/wireguard/server_private.key
# Generate pre-shared key (optional, adds post-quantum resistance)
wg genpsk > /etc/wireguard/psk.key
chmod 600 /etc/wireguard/psk.key
```
### Server Configuration
```ini
# /etc/wireguard/wg0.conf
2026-01-27 17:35:45 -05:00
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <server-private-key>
2026-03-24 18:02:50 -04:00
# Enable IP forwarding and NAT on startup
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
# DNS for clients
DNS = 10.0.0.1
# Peer: Alice (laptop)
2026-01-27 17:35:45 -05:00
[Peer]
2026-03-24 18:02:50 -04:00
PublicKey = <alice-public-key>
PresharedKey = <preshared-key>
2026-01-27 17:35:45 -05:00
AllowedIPs = 10.0.0.2/32
2026-03-24 18:02:50 -04:00
# Peer: Bob (mobile)
[Peer]
PublicKey = <bob-public-key>
PresharedKey = <preshared-key>
AllowedIPs = 10.0.0.3/32
# Peer: Office network (site-to-site)
[Peer]
PublicKey = <office-public-key>
PresharedKey = <preshared-key>
AllowedIPs = 10.0.0.4/32, 192.168.1.0/24
Endpoint = office.example.com:51820
PersistentKeepalive = 25
```
### Client Configuration
```ini
# Client config: alice.conf
[Interface]
Address = 10.0.0.2/24
PrivateKey = <alice-private-key>
DNS = 10.0.0.1
[Peer]
PublicKey = <server-public-key>
PresharedKey = <preshared-key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
```
### Split Tunneling Configuration
```ini
# Client config with split tunnel (only route internal traffic through VPN)
[Interface]
Address = 10.0.0.2/24
PrivateKey = <alice-private-key>
# No DNS override for split tunnel
[Peer]
PublicKey = <server-public-key>
PresharedKey = <preshared-key>
Endpoint = vpn.example.com:51820
# Only route specific subnets through VPN
AllowedIPs = 10.0.0.0/24, 172.16.0.0/16, 192.168.1.0/24
PersistentKeepalive = 25
```
### WireGuard Management Commands
```bash
# Start/stop interface
2026-01-27 17:35:45 -05:00
wg-quick up wg0
2026-03-24 18:02:50 -04:00
wg-quick down wg0
# Enable on boot
2026-01-27 17:35:45 -05:00
systemctl enable wg-quick@wg0
2026-03-24 18:02:50 -04:00
# Show connection status
wg show
wg show wg0
# Add a new peer dynamically
wg set wg0 peer <new-public-key> allowed-ips 10.0.0.5/32
# Remove a peer
wg set wg0 peer <public-key> remove
# Show transfer statistics
wg show wg0 transfer
# Generate QR code for mobile clients
apt install -y qr-encode
qrencode -t ansiutf8 < alice-mobile.conf
```
### Peer Management Script
```bash
#!/bin/bash
# wg-add-peer.sh - Add a new WireGuard peer
set -euo pipefail
PEER_NAME = " ${ 1 :?Usage: $0 <peer-name> } "
SERVER_CONF = "/etc/wireguard/wg0.conf"
CLIENTS_DIR = "/etc/wireguard/clients"
SERVER_PUBKEY = $( cat /etc/wireguard/server_public.key)
SERVER_ENDPOINT = "vpn.example.com:51820"
PSK = $( cat /etc/wireguard/psk.key)
# Find next available IP
LAST_IP = $( grep -oP 'AllowedIPs = 10\.0\.0\.\K[0-9]+' " $SERVER_CONF " | sort -n | tail -1)
NEXT_IP = $(( LAST_IP + 1 ))
mkdir -p " $CLIENTS_DIR "
# Generate client keys
wg genkey | tee " $CLIENTS_DIR / ${ PEER_NAME } _private.key" | wg pubkey > " $CLIENTS_DIR / ${ PEER_NAME } _public.key"
chmod 600 " $CLIENTS_DIR / ${ PEER_NAME } _private.key"
CLIENT_PRIVKEY = $( cat " $CLIENTS_DIR / ${ PEER_NAME } _private.key" )
CLIENT_PUBKEY = $( cat " $CLIENTS_DIR / ${ PEER_NAME } _public.key" )
# Add peer to server config
cat >> " $SERVER_CONF " << EOF
# Peer: ${PEER_NAME}
[Peer]
PublicKey = ${CLIENT_PUBKEY}
PresharedKey = ${PSK}
AllowedIPs = 10.0.0.${NEXT_IP}/32
EOF
# Generate client config
cat > " $CLIENTS_DIR / ${ PEER_NAME } .conf" << EOF
[Interface]
Address = 10.0.0.${NEXT_IP}/24
PrivateKey = ${CLIENT_PRIVKEY}
DNS = 10.0.0.1
[Peer]
PublicKey = ${SERVER_PUBKEY}
PresharedKey = ${PSK}
Endpoint = ${SERVER_ENDPOINT}
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF
# Reload WireGuard
wg syncconf wg0 <( wg-quick strip wg0)
echo "Peer ${ PEER_NAME } added with IP 10.0.0. ${ NEXT_IP } "
echo "Client config: ${ CLIENTS_DIR } / ${ PEER_NAME } .conf"
2026-01-27 17:35:45 -05:00
```
## OpenVPN
2026-03-24 18:02:50 -04:00
### Server Setup
2026-01-27 17:35:45 -05:00
2026-03-24 18:02:50 -04:00
```bash
# Install OpenVPN and Easy-RSA
apt install -y openvpn easy-rsa
# Initialize PKI
make-cadir /etc/openvpn/easy-rsa
2026-01-27 17:35:45 -05:00
cd /etc/openvpn/easy-rsa
2026-03-24 18:02:50 -04:00
2026-01-27 17:35:45 -05:00
./easyrsa init-pki
2026-03-24 18:02:50 -04:00
./easyrsa build-ca nopass
2026-01-27 17:35:45 -05:00
./easyrsa gen-req server nopass
./easyrsa sign-req server server
./easyrsa gen-dh
2026-03-24 18:02:50 -04:00
openvpn --genkey secret /etc/openvpn/ta.key
# Generate client certificate
./easyrsa gen-req client1 nopass
./easyrsa sign-req client client1
```
### Server Configuration
```ini
# /etc/openvpn/server.conf
port 1194
proto udp
dev tun
ca /etc/openvpn/easy-rsa/pki/ca.crt
cert /etc/openvpn/easy-rsa/pki/issued/server.crt
key /etc/openvpn/easy-rsa/pki/private/server.key
dh /etc/openvpn/easy-rsa/pki/dh.pem
tls-auth /etc/openvpn/ta.key 0
server 10.8.0.0 255.255.255.0
# Route client traffic through VPN
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
# Split tunnel: push specific routes instead
# push "route 172.16.0.0 255.255.0.0"
# push "route 192.168.1.0 255.255.255.0"
keepalive 10 120
# Cipher and auth
cipher AES-256-GCM
auth SHA256
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
# Hardening
tls-version-min 1.2
tls-cipher TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384
user nobody
group nogroup
persist-key
persist-tun
# Logging
status /var/log/openvpn/status.log
log-append /var/log/openvpn/openvpn.log
verb 3
# Max clients
max-clients 100
# Client isolation (clients cannot see each other)
client-to-client
```
### Client Configuration
```ini
# client1.ovpn
client
dev tun
proto udp
remote vpn.example.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert client1.crt
key client1.key
tls-auth ta.key 1
cipher AES-256-GCM
auth SHA256
verb 3
```
## Tailscale (Managed WireGuard)
```bash
# Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh
# Authenticate and connect
tailscale up
# Advertise subnet routes (act as a gateway)
tailscale up --advertise-routes= 192.168.1.0/24,172.16.0.0/16
# Enable as exit node (route all traffic)
tailscale up --advertise-exit-node
# Use an exit node
tailscale up --exit-node= <exit-node-ip>
# Check status
tailscale status
# Access control: tailscale ACL policy (in admin console)
# Example ACL policy
cat << 'EOF'
{
"acls": [
{"action": "accept", "src": ["group:engineering"], "dst": ["tag:servers:*"]},
{"action": "accept", "src": ["group:devops"], "dst": ["*:*"]},
{"action": "accept", "src": ["tag:monitoring"], "dst": ["tag:servers:9100"]}
],
"tagOwners": {
"tag:servers": ["group:devops"],
"tag:monitoring": ["group:devops"]
},
"groups": {
"group:engineering": ["alice@example.com", "bob@example.com"],
"group:devops": ["charlie@example.com"]
}
}
EOF
# Enable MagicDNS and set DNS
tailscale up --accept-dns
# SSH via Tailscale (no SSH keys needed)
tailscale up --ssh
2026-01-27 17:35:45 -05:00
```
## AWS Site-to-Site VPN
```bash
2026-03-24 18:02:50 -04:00
# Create Virtual Private Gateway
VGW_ID = $( aws ec2 create-vpn-gateway --type ipsec.1 --query 'VpnGateway.VpnGatewayId' --output text)
# Attach to VPC
aws ec2 attach-vpn-gateway --vpn-gateway-id " $VGW_ID " --vpc-id vpc-0abc123
# Create Customer Gateway (your on-prem device)
CGW_ID = $( aws ec2 create-customer-gateway \
2026-01-27 17:35:45 -05:00
--type ipsec.1 \
--bgp-asn 65000 \
2026-03-24 18:02:50 -04:00
--public-ip 203.0.113.10 \
--query 'CustomerGateway.CustomerGatewayId' --output text)
# Create VPN connection
VPN_ID = $( aws ec2 create-vpn-connection \
2026-01-27 17:35:45 -05:00
--type ipsec.1 \
2026-03-24 18:02:50 -04:00
--customer-gateway-id " $CGW_ID " \
--vpn-gateway-id " $VGW_ID " \
--options '{"StaticRoutesOnly":false}' \
--query 'VpnConnection.VpnConnectionId' --output text)
# Download configuration for your device
aws ec2 describe-vpn-connections --vpn-connection-ids " $VPN_ID "
# Enable route propagation
aws ec2 enable-vgw-route-propagation \
--gateway-id " $VGW_ID " \
--route-table-id rtb-0abc123
# Monitor VPN tunnel status
aws ec2 describe-vpn-connections \
--vpn-connection-ids " $VPN_ID " \
--query 'VpnConnections[0].VgwTelemetry'
2026-01-27 17:35:45 -05:00
```
2026-03-24 18:02:50 -04:00
## Troubleshooting
| Problem | Cause | Solution |
|---------|-------|----------|
| WireGuard handshake never completes | Firewall blocking UDP 51820 | Open UDP 51820 on server firewall and any intermediate firewalls |
| No internet through VPN | IP forwarding disabled or NAT missing | Enable `net.ipv4.ip_forward=1` ; verify PostUp iptables rules |
| DNS not resolving over VPN | DNS not pushed or local DNS conflicts | Set `DNS = ` in client config; check `/etc/resolv.conf` |
| OpenVPN TLS handshake fails | Certificate mismatch or expired | Verify CA cert matches; check certificate dates with `openssl x509 -dates` |
| Split tunnel leaks traffic | AllowedIPs too broad | Set only specific subnets in AllowedIPs; verify with `traceroute` |
| Tailscale node unreachable | ACL blocking traffic | Check Tailscale admin ACL policy; verify node is online with `tailscale status` |
| AWS VPN tunnel flapping | Idle timeout or DPD misconfigured | Enable DPD on customer gateway; send periodic keep-alive traffic |
| Slow VPN performance | MTU issues causing fragmentation | Set `MTU = 1420` in WireGuard config; test with `ping -M do -s 1400` |
2026-01-27 17:35:45 -05:00
## Best Practices
2026-03-24 18:02:50 -04:00
- Use WireGuard for modern deployments (simpler, faster, smaller attack surface)
- Implement MFA for VPN access where possible
- Rotate keys regularly (quarterly for WireGuard, annual for OpenVPN certs)
- Monitor VPN connections and alert on anomalies
- Segment VPN access by role using split tunneling or ACLs
- Use pre-shared keys with WireGuard for post-quantum resistance
- Keep VPN software updated to patch security vulnerabilities
- Log all VPN connection events for audit purposes
- Disable VPN access immediately when employees leave
- Test failover for site-to-site VPN connections
2026-01-27 17:35:45 -05:00
## Related Skills
- [zero-trust ](../zero-trust/ ) - Modern access patterns
- [ssl-tls-management ](../ssl-tls-management/ ) - Certificate management
2026-03-24 18:02:50 -04:00
- [firewall-config ](../firewall-config/ ) - Network access control