SuperAPI AI Directory

wireguard

Deployment & operations

View repository

Updated 10-16 09:39

About this skill

Identity

  • Kernel module: wireguard (built into kernel 5.6+; backported to 5.4 via DKMS)
  • Config: /etc/wireguard/wg0.conf (interface name matches filename: wg0)
  • Manage via: wg-quick up wg0 / wg-quick down wg0 / wg (status and stats)
  • Unit: wg-quick@wg0.service (systemd unit for persistent operation)
  • Install: apt install wireguard / dnf install wireguard-tools (kernel module usually pre-included on modern distros)

Key Generation

# Generate server keys
wg genkey | tee server_private.key | wg pubkey > server_public.key

# Generate client keys
wg genkey | tee client_private.key | wg pubkey > client_public.key

# Optional: pre-shared key (adds post-quantum resistance layer)
wg genpsk > preshared.key

chmod 600 server_private.key client_private.key preshared.key


Private keys never leave the host they were generated on. Public keys are shared freely. Pre-shared keys (PSKs) are symmetric secrets shared between exactly two peers — one PSK per peer pair, stored in both configs.

## Key Operations

| Goal | Command |
|------|---------|
| Bring interface up | `sudo wg-quick up wg0` |
| Bring interface down | `sudo wg-quick down wg0` |
| Enable on boot | `sudo systemctl enable wg-quick@wg0` |
| Start now + on boot | `sudo systemctl enable --now wg-quick@wg0` |
| Show all interface status | `sudo wg show` |
| Show specific interface | `sudo wg show wg0` |
| Show current running config | `sudo wg showconf wg0` |
| Check handshake timestamps | `sudo wg show wg0 latest-handshakes` |
| Show transfer stats | `sudo wg show wg0 transfer` |
| Add peer at runtime | `sudo wg set wg0 peer <pubkey> allowed-ips 10.0.0.3/32` |
| Save runtime changes to file | `sudo wg-quick save wg0` |
| Reload config without down/up | `sudo wg syncconf wg0 <(wg-quick strip wg0)` |

## Expected Ports
- **51820/udp** — default; set via `ListenPort` in `[Interface]`
- WireGuard is UDP-only. TCP is not supported.
- Verify listening: `ss -ulnp | grep wireguard`
- Allow through firewall: `ufw allow 51820/udp` or `firewall-cmd --add-port=51820/udp --permanent`

## Health Checks

1. `sudo wg show` — lists all interfaces and peers; confirms the interface is up
2. `sudo wg show wg0 latest-handshakes` — epoch timestamps; any value > 0 means the peer has connected; values more than ~3 minutes in the past mean the connection is idle or broken
3. Ping across the tunnel (e.g., `ping 10.0.0.2` from the server to the first client's tunnel IP)
4. `journalctl -u wg-quick@wg0 -n 50` — startup errors, PostUp script failures
5. `sudo wg show wg0 transfer` — bytes sent/received; zero RX means no traffic is returning

## Common Failures

| Symptom | Likely cause | Fix |
|---------|-------------|-----|
| `wg show` shows peer but no handshake | Firewall blocking UDP 51820 on server, or wrong public key pasted | Open UDP port on server; verify keys match exactly |
| Handshake occurs but no traffic flows | `ip_forward` disabled on server; PostUp NAT rule missing | `sysctl -w net.ipv4.ip_forward=1`; verify PostUp iptables rule |
| Handshake times out repeatedly | Client endpoint wrong (wrong IP or port) | Confirm server's public IP and ListenPort; check `Endpoint` line |
| DNS leaks outside the tunnel | DNS server not set in client `[Interface]`, or DNS not reachable via tunnel | Set `DNS = <server-tunnel-ip>` in client config; run DNS server on server |
| Mobile client drops after idle | NAT table on router expires UDP mapping | Add `PersistentKeepalive = 25` to client's `[Peer]` (server entry) |
| Split tunnel routes wrong traffic | AllowedIPs too broad or overlapping between peers | AllowedIPs is a routing table — each IP range can appear in only one peer |
| `wg-quick: Permission denied` on startup | `wg0.conf` permissions are not 600 | `chmod 600 /etc/wireguard/wg0.conf` |
| `RTNETLINK answers: Operation not supported` | WireGuard kernel module not loaded | `modprobe wireguard`; on older kernels install `wireguard-dkms` |

## Pain Points

- **AllowedIPs is a routing table**: Each peer's `AllowedIPs` determines which destination IPs get routed through that peer's tunnel. `0.0.0.0/0, ::/0` = all traffic (full VPN). Specific subnets = split tunnel. Common mistake: overlapping `AllowedIPs` between two peers — WireGuard will accept the config but routing becomes undefined.

- **No auto-discovery**: Every peer must be explicitly added to every other peer's config. There is no central auth server or certificate authority. Adding a new client means editing the server config and reloading it.

- **UDP only**: WireGuard strictly uses UDP. If a network blocks all UDP (restrictive hotel/corporate firewall), WireGuard will not work. Unlike OpenVPN, there is no TCP fallback.

- **Handshake timeout = dead connection**: `wg show` shows `latest handshake` as a Unix timestamp. If it is more than 3 minutes ago, the tunnel is likely broken. `PersistentKeepalive = 25` sends a keepalive every 25 seconds to maintain NAT mappings and detect dead connections faster.

- **ip_forward required on server**: For the server to route traffic between peers (or to the internet), `net.ipv4.ip_forward = 1` must be active. Set persistently in `/etc/sysctl.d/99-wireguard.conf` with `net.ipv4.ip_forward = 1`; activate with `sysctl -p`.

- **File permissions**: `/etc/wireguard/wg0.conf` must be mode 600 (owner-read-only). `wg-quick` warns and may refuse to start if permissions are too open. The file contains the private key in plaintext.

- **Runtime changes vs. file changes**: `wg set` modifies the running interface immediately but does not persist to `wg0.conf`. Use `wg-quick save wg0` to write runtime state back to the file, or use `wg syncconf wg0 <(wg-quick strip wg0)` to apply file changes without taking the interface down.

## References
See `references/` for:
- `wg0.conf.annotated` — fully annotated server and client config examples
- `setup-patterns.md` — step-by-step guides for common deployment scenarios
- `docs.md` — official documentation and community resources