L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Développement · SKILL.md

wireguard

wireguard

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", …

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: wireguard description: | 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) allowed-tools: Read, Grep, Glob, Write, Edit, Bash

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
Skills enregistrées