Claude
Skills
Sign in
Back

wireguard

Included with Lifetime
$97 forever

WireGuard VPN setup on Linux. Covers server config, client config, key management, NAT/IP forwarding, peer management, split tunnel, full tunnel, and site-to-site. USE WHEN: user mentions "wireguard", "wg0", "wg-quick", "wireguard vpn", "wireguard server setup", "wireguard peer", "wireguard keys", "wireguard client", "wireguard nat", "wireguard iptables", "wireguard split tunnel", "wireguard qr code", "wg genkey", "wireguard site-to-site", "wireguard keepalive" DO NOT USE FOR: OpenVPN setups - different tool and protocol, IPsec / IKEv2 VPNs, Tailscale or Netbird (they use WireGuard underneath but have their own CLIs), Cloud VPN gateways (AWS VPN, GCP Cloud VPN)

Cloud & DevOps

What this skill does

# WireGuard Core Knowledge

## Concepts

| Term | Meaning |
|---|---|
| Interface | Virtual network adapter (`wg0`). One per VPN tunnel. |
| Private key | Generated per peer; never shared. |
| Public key | Derived from private key; exchanged with remote peers. |
| Peer | Any other WireGuard node (server or client). |
| AllowedIPs | IP ranges allowed through the tunnel to/from this peer. Acts as routing table + firewall. |
| Endpoint | `IP:port` where this peer is reachable (optional on server for dynamic clients). |
| PersistentKeepalive | Sends keepalive every N seconds — required for clients behind NAT. |
| PreSharedKey | Optional symmetric key for additional post-quantum protection. |

---

## Server Setup

### Install WireGuard

```bash
# Ubuntu / Debian
sudo apt update && sudo apt install -y wireguard wireguard-tools

# RHEL / Rocky / Alma
sudo dnf install -y epel-release && sudo dnf install -y wireguard-tools

# Arch
sudo pacman -S wireguard-tools

# Verify kernel module loaded (Linux 5.6+: built in)
sudo modprobe wireguard && echo "WireGuard module OK"
```

### Generate Server Keys

```bash
# Create key directory with tight permissions
sudo mkdir -p /etc/wireguard
sudo chmod 700 /etc/wireguard

# Generate server key pair
wg genkey | sudo tee /etc/wireguard/server_private.key | \
  wg pubkey | sudo tee /etc/wireguard/server_public.key

sudo chmod 600 /etc/wireguard/server_private.key
cat /etc/wireguard/server_public.key    # Share this with clients
```

### Server Config — `/etc/wireguard/wg0.conf`

```ini
[Interface]
# Server private key (keep secret)
PrivateKey = <SERVER_PRIVATE_KEY>

# VPN subnet address for the server itself
Address = 10.10.0.1/24

# UDP port WireGuard listens on
ListenPort = 51820

# IP forwarding rules — applied when wg-quick brings up the interface
# Replace eth0 with your actual outbound interface (ip route | grep default)
PostUp   = sysctl -w net.ipv4.ip_forward=1
PostUp   = iptables -A FORWARD -i %i -j ACCEPT
PostUp   = iptables -A FORWARD -o %i -j ACCEPT
PostUp   = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# IPv6 forwarding (optional)
PostUp   = sysctl -w net.ipv6.conf.all.forwarding=1
PostUp   = ip6tables -A FORWARD -i %i -j ACCEPT
PostUp   = ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

PreDown  = iptables -D FORWARD -i %i -j ACCEPT
PreDown  = iptables -D FORWARD -o %i -j ACCEPT
PreDown  = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
PreDown  = ip6tables -D FORWARD -i %i -j ACCEPT
PreDown  = ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

# Save/restore rules between restarts
SaveConfig = false          # Set true only during testing; keeps config clean in prod

# --- Peers (one block per client) ---

[Peer]
# Client: Alice's laptop
PublicKey = <ALICE_PUBLIC_KEY>
# Unique IP assigned to Alice's device in the VPN subnet
AllowedIPs = 10.10.0.2/32
# No Endpoint here — Alice connects from dynamic IPs
# PresharedKey = <OPTIONAL_PSK>   # Uncomment for additional security

[Peer]
# Client: Bob's phone
PublicKey = <BOB_PUBLIC_KEY>
AllowedIPs = 10.10.0.3/32

[Peer]
# Site-to-site: Office network (192.168.50.0/24)
PublicKey = <OFFICE_GW_PUBLIC_KEY>
AllowedIPs = 10.10.0.10/32, 192.168.50.0/24
Endpoint = office-gw.example.com:51820
PersistentKeepalive = 25
```

### Enable IP Forwarding Permanently

```bash
# /etc/sysctl.conf or /etc/sysctl.d/99-wireguard.conf
echo "net.ipv4.ip_forward = 1"       | sudo tee /etc/sysctl.d/99-wireguard.conf
echo "net.ipv6.conf.all.forwarding = 1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
```

### Start and Enable via systemd

```bash
sudo systemctl enable --now wg-quick@wg0
sudo systemctl status wg-quick@wg0

# Check interface
sudo wg show wg0
```

---

## Client Config Generation

### Generate Client Keys

```bash
# Run on the client machine (or generate server-side and distribute securely)
wg genkey | tee client_private.key | wg pubkey > client_public.key
chmod 600 client_private.key

# Optional preshared key (same for both sides)
wg genpsk > client_psk.key
```

### Client Config — Full Tunnel (all traffic through VPN)

```ini
# /etc/wireguard/wg0.conf  (on client)
[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address    = 10.10.0.2/32          # Unique VPN IP assigned to this client
DNS        = 10.10.0.1             # Server acts as DNS resolver; or use 1.1.1.1

[Peer]
PublicKey  = <SERVER_PUBLIC_KEY>
Endpoint   = vpn.example.com:51820
# Full tunnel: all traffic (including internet) through VPN
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25           # Required when client is behind NAT
# PresharedKey = <PSK>
```

### Client Config — Split Tunnel (only internal subnets through VPN)

```ini
[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address    = 10.10.0.2/32
DNS        = 10.10.0.1

[Peer]
PublicKey  = <SERVER_PUBLIC_KEY>
Endpoint   = vpn.example.com:51820
# Split tunnel: only route private subnets through VPN
AllowedIPs = 10.10.0.0/24, 192.168.1.0/24, 172.16.0.0/12
# Internet traffic goes directly (NOT through VPN)
PersistentKeepalive = 25
```

---

## QR Code for Mobile Clients

```bash
# Install qrencode
sudo apt install -y qrencode

# Print QR code to terminal (scan with WireGuard iOS/Android app)
qrencode -t ansiutf8 < /etc/wireguard/clients/alice.conf

# Or export to PNG
qrencode -o alice-vpn.png < /etc/wireguard/clients/alice.conf
```

---

## Peer Management (Hot Reload — No Restart Required)

### Add a Peer Without Restart

```bash
# Generate new peer keys
wg genkey | tee /etc/wireguard/clients/carol_private.key | \
  wg pubkey > /etc/wireguard/clients/carol_public.key

CAROL_PUBKEY=$(cat /etc/wireguard/clients/carol_public.key)

# Add peer to running interface
sudo wg set wg0 peer "$CAROL_PUBKEY" allowed-ips 10.10.0.4/32

# Persist to config file
sudo wg-quick save wg0    # ONLY works if SaveConfig=true
# OR append to conf manually:
echo -e "\n[Peer]\nPublicKey = $CAROL_PUBKEY\nAllowedIPs = 10.10.0.4/32" \
  | sudo tee -a /etc/wireguard/wg0.conf
```

### Remove a Peer Without Restart

```bash
PEER_PUBKEY="<PUBLIC_KEY_TO_REMOVE>"
sudo wg set wg0 peer "$PEER_PUBKEY" remove
# Remove the [Peer] block from wg0.conf manually to persist
```

### Check Peer Status

```bash
# Show all peers, latest handshake, transfer stats
sudo wg show wg0

# Show only handshake times (0 = never connected)
sudo wg show wg0 latest-handshakes

# Show allowed IPs
sudo wg show wg0 allowed-ips

# Check transfer per peer
sudo wg show wg0 transfer
```

---

## Site-to-Site Pattern

```
Office LAN (192.168.50.0/24)          Cloud LAN (10.10.0.0/24)
        |                                     |
  [Office GW] ──── WireGuard tunnel ──── [Cloud GW / Server]
  wg0: 10.10.0.10                       wg0: 10.10.0.1
```

**Cloud Server peer for office:**
```ini
[Peer]
PublicKey    = <OFFICE_GW_PUBLIC_KEY>
AllowedIPs   = 10.10.0.10/32, 192.168.50.0/24
Endpoint     = office-public-ip:51820
PersistentKeepalive = 25
```

**Office Gateway config:**
```ini
[Interface]
PrivateKey = <OFFICE_GW_PRIVATE_KEY>
Address    = 10.10.0.10/24
ListenPort = 51820
PostUp   = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PreDown  = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey    = <CLOUD_SERVER_PUBLIC_KEY>
AllowedIPs   = 10.10.0.0/24          # Cloud VPN subnet only (split tunnel)
Endpoint     = vpn.example.com:51820
PersistentKeepalive = 25
```

---

## Monitoring and Transfer Stats

```bash
# Live monitoring
watch -n 2 sudo wg show

# Handshake age (should be < 3 min for active clients with keepalive=25)
sudo wg show wg0 latest-handshakes | awk '{
  now = systime(); age = now - $2;
  printf "%s  last handshake: %d seconds ago\n", $1, age
}'

# Total transferred (bytes) — parse for alerting
sudo wg show wg0 transfer

# Interface stats via ip
ip -s link show wg0
```

---

## DNS for VPN Clients

### Option 1: systemd-resolved on server as DNS resolver

```bash
# Allow D

Related in Cloud & DevOps