wireguard

v2026.09.24

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)

GitHub
安装命令
npx skhub add claude-dev-suite/wireguard
Markdown
SKILL.md

WireGuard Core Knowledge

Concepts

TermMeaning
InterfaceVirtual network adapter (wg0). One per VPN tunnel.
Private keyGenerated per peer; never shared.
Public keyDerived from private key; exchanged with remote peers.
PeerAny other WireGuard node (server or client).
AllowedIPsIP ranges allowed through the tunnel to/from this peer. Acts as routing table + firewall.
EndpointIP:port where this peer is reachable (optional on server for dynamic clients).
PersistentKeepaliveSends keepalive every N seconds — required for clients behind NAT.
PreSharedKeyOptional symmetric key for additional post-quantum protection.

Server Setup

Install WireGuard

# 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

# 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

[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

# /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

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

# 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)

# /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)

[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

# 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

# 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

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

# 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:

[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:

[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

# 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

# Allow DNS queries from VPN subnet
sudo apt install -y systemd-resolved
# Edit /etc/systemd/resolved.conf
[Resolve]
DNS=1.1.1.1 8.8.8.8
DNSStubListener=yes

# Allow port 53 from VPN interface
sudo iptables -A INPUT -i wg0 -p udp --dport 53 -j ACCEPT
sudo iptables -A INPUT -i wg0 -p tcp --dport 53 -j ACCEPT

Clients set DNS = 10.10.0.1 in their [Interface] block.

Option 2: dnsmasq for internal hostname resolution

sudo apt install -y dnsmasq
# /etc/dnsmasq.conf
interface=wg0
bind-interfaces
domain=vpn.internal
expand-hosts
local=/vpn.internal/
# Forward everything else to upstream
server=1.1.1.1
server=8.8.8.8

Anti-Patterns

Anti-PatternProblemSolution
Sharing private keys between peersIf one device is compromised, all peers with that key are compromisedGenerate a unique key pair per device; never copy private keys
Not setting PersistentKeepalive for NAT clientsNAT mapping expires → client appears disconnected after idle periodSet PersistentKeepalive = 25 on all clients behind NAT/firewall
Forgetting net.ipv4.ip_forward = 1Traffic enters VPN but is not forwarded → clients can reach server but nothing elseSet via sysctl.d file and apply PostUp in wg0.conf
Using SaveConfig = true in productionwg-quick save overwrites conf on shutdown, blowing away comments and formattingUse SaveConfig = false; manage conf file manually or via config management
Assigning overlapping AllowedIPs to two peersWireGuard silently uses the last-defined peer for a given IP rangeAssign unique, non-overlapping /32 addresses per client
Opening port 51820 to 0.0.0.0/0 in all directionsExposure is unavoidable for UDP listen port, but ensure firewall blocks other portsFirewall all ports except 22 (SSH), 51820 (WireGuard), and your services
Full tunnel (0.0.0.0/0) without DNS setDNS leaks — queries go to ISP DNS outside the tunnelAlways set DNS in client [Interface] when using full tunnel
Distributing client configs over unencrypted channelsPrivate key exposed in transitTransfer via encrypted channel (SFTP, gpg-encrypted file, Signal, QR code over physical display)
Leaving unused peers in configRevoked clients remain technically validRemove both the [Peer] block from wg0.conf and run wg set wg0 peer <key> remove
Not logging handshake timestampsNo visibility into active vs stale clientsSet up periodic cron to log wg show wg0 latest-handshakes to a monitoring system

Troubleshooting

SymptomLikely CauseFix
No handshake ever establishedEndpoint unreachable or firewall blocking port 51820/UDPnc -u vpn.example.com 51820 from client; check server firewall: sudo ufw allow 51820/udp
Handshake established but traffic not routingip_forward not enabled, iptables FORWARD chain dropping packetssysctl net.ipv4.ip_forward must return 1; check iptables -L FORWARD
DNS not working inside VPNClient DNS set to VPN server IP but DNS not running on serverInstall dnsmasq/systemd-resolved; allow port 53 from wg0 interface
Client connects but internet not reachable (full tunnel)NAT/MASQUERADE rule missingVerify iptables -t nat -L POSTROUTING shows MASQUERADE on correct interface
Connection drops every few minutesNo keepalive and NAT mapping expiresAdd PersistentKeepalive = 25 to client config
"RTNETLINK answers: Operation not supported" on modprobeKernel too old (< 5.6) or WireGuard not installedInstall wireguard-dkms package or upgrade kernel
Peers listed in wg show but can't ping VPN serverAllowedIPs mismatch — client AllowedIPs doesn't include server VPN IPEnsure server VPN IP (10.10.0.1) is within client's AllowedIPs range
wg-quick: line X: invalid optionwg0.conf syntax errorRun wg-quick strip wg0 to validate conf; check for tabs vs spaces
IP address conflict with LANClient VPN subnet overlaps with local networkChange VPN subnet to unused range (e.g., 10.88.0.0/24 instead of 10.0.0.0/24)
Can't SSH to server after bringing up wg0iptables FORWARD chain blocking return trafficAdd iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

Production Checklist

  • Unique key pair per device (never share private keys)
  • /etc/wireguard/ permissions: directory 700, private key files 600
  • net.ipv4.ip_forward = 1 in /etc/sysctl.d/
  • iptables MASQUERADE rule set in PostUp / cleaned in PreDown
  • PersistentKeepalive = 25 on all NAT clients
  • SaveConfig = false in production (manage config via code)
  • systemctl enable wg-quick@wg0 for auto-start on reboot
  • Firewall: only port 51820/UDP open externally
  • Client configs distributed via encrypted channel
  • Unused peers removed promptly from config and live interface
  • DNS set for full-tunnel clients to prevent DNS leaks
  • Monitoring: cron/alert on peers with no handshake in > 5 minutes
发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

2026年9月24日

分类

未分类

许可证

MIT

源路径

skills/infrastructure/wireguard

默认分支

main

最新提交

9496306

Tree SHA

fe4e2f1