Jump to content

User:Cmsigler/Wireguard Configuration Guide

From ArchWiki

My Personal Step-by-step Guide to Wireguard Setup, Configuration and Operation

CMS, 2022/03/14

Note These procedures have been developed and deployed on an Arch Linux installation. Other distributions and environments will require modifications to the steps below. YMMV
Note For information on WireGuard under Arch, see the Arch Linux WireGuard page.

Nomenclature

  • Gateway peer: Wireguard "server" peer connected to public Internet
  • VPN peer: Wireguard "client" peer; may be located behind, e.g., a NAT router

Initial Setup

Requirements

  • Install and use kernel with CONFIG_WIREGUARD
  • Install wireguard-tools

Pre-configuration

  • Generate keys for each peer [gateway = Gateway peer; vpn = VPN peer]
$ cd ~/wireguard_config
$ (umask 0077; wg genkey > gateway.key)
$ wg pubkey < gateway.key > gateway.pub
$ (umask 0077; wg genkey > vpn.key)
$ wg pubkey < vpn.key > vpn.pub
  • Optional: Generate pre-shared keys for each peer-to-peer link pair
$ (umask 0077; wg genpsk > gateway-vpn.psk)
  • Optional: On gateway peer, set up DNS server for wireguard peers using dnsmasq as server
    • Install dnsmasq
    • Edit /etc/dnsmasq.conf
      • Uncomment domain-needed, bogus-priv, bind-interfaces
      • Set "interface=wg0"
      • Set "listen-address=::1,127.0.0.1,10.0.0.1,2001:db8:1234:5678::1,fd89:abc1:def2:1::1"
      • Optional: Set "cache-size=1000"

Configuration for operation via wg-quick

Example -- Wireguard VPN gateway:

Wireguard configuration

On gateway peer:

/etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.1/24, 2001:db8:1234:5678::1/64, fd89:abc1:def2:1::1/64
ListenPort = 51871
PrivateKey = # GATEWAY_PEER_PRIVATE_KEY

[Peer]
PublicKey = # VPN_PEER_PUBLIC_KEY
PresharedKey = # GATEWAY_PEER-VPN_PEER-PRESHARED_KEY
AllowedIPs = 10.0.0.2/32, 2001:db8:1234:5678::2/128, fd89:abc1:def2:1::2/128

On VPN peer:

/etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.2/32, 2001:db8:1234:5678::2/128, fd89:abc1:def2:1::2/128
ListenPort = 51902
PrivateKey = # GATEWAY_PEER_PRIVATE_KEY
DNS = 2001:db8:1234:5678::1, 10.0.0.1

[Peer]
PublicKey = # VPN_PEER_PUBLIC_KEY
PresharedKey = # GATEWAY_PEER-VPN_PEER-PRESHARED_KEY
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 198.51.100.49:51871

Firewall/filtering configuration (using nftables)

On Gateway peer:

  • Input filter
define upstream-if = ens0
define vpn-if = wg0
define mgmt-host = 203.0.113.51
define ssh-port = 22
define vpn-port = 51871
#
table inet inet-local-table {
  chain inet-local-input {
    type filter hook input priority filter
    policy drop
# Accept localhost traffic
    iif lo accept
# Bad TCP --> reject network scanning
    iif $upstream-if tcp flags & (fin|syn) == (fin|syn) counter drop
    iif $upstream-if tcp flags & (syn|rst) == (syn|rst) counter drop
    iif $upstream-if tcp flags & (fin|syn|rst|psh|ack|urg) == 0 counter drop
    iif $upstream-if tcp flags & (fin|syn|rst|psh|ack|urg) == (fin|psh|urg) counter drop
# Accept Wireguard inbound UDP traffic from peer to VPN port
    iif $upstream-if udp dport $vpn-port accept
# Accept ICMP from Wireguard peers
    iifname $vpn-if ip protocol icmp limit rate 5/second accept
    iifname $vpn-if meta l4proto ipv6-icmp limit rate 5/second accept
# Allow DNS from Wireguard peers
    iifname $vpn-if udp dport 53 accept
    iifname $vpn-if tcp dport 53 accept
# Remaining input from VPN interface to VPN server (local) prohibited
#   -- default policy drop
    iifname $vpn-if counter drop
# Drop invalid (untracked?) packets
    iif $upstream-if ct state invalid counter drop
# Accept established and related tracked connections
    iif $upstream-if ct state {established, related} accept
# Allow connection from given mgmt host on given ssh port
    iif $upstream-if ip saddr $mgmt-host tcp dport $ssh-port accept
# Limit ICMP packets accepted
    iif $upstream-if ip protocol icmp limit rate 5/second accept
    iif $upstream-if meta l4proto ipv6-icmp limit rate 5/second accept
# Count traffic dropped by default policy
    counter drop
  }
}
  • Forward filter
define upstream-if = ens0
define vpn-if = wg0
#
table inet inet-local-table {
  chain inet-local-forward {
    type filter hook forward priority filter
    policy drop
# Drop IP forward for upstream invalid packets
    iif $upstream-if ct state invalid counter drop
# Accept IP forward for upstream established and related tracked connections
    iif $upstream-if ct state {established, related} accept
# Accept all VPN traffic to be forwarded upstream
    iifname $vpn-if oif $upstream-if accept
# Count traffic dropped by default policy
    counter drop
  }
}
  • Address translation (NAT) filter
define upstream-if = ens0
define vpn-if = wg0
#
table inet inet-local-table {
  chain inet-local-nat {
    type nat hook postrouting priority srcnat
    policy accept
# NAT/masquerade all traffic coming from VPN interface, and count
    iifname $vpn-if oif $upstream-if meta protocol ip counter masquerade
  }
}

Packet forwarding configuration

On Gateway peer:

  • sysctl configuration
/etc/sysctl.d/30-ipv4_forward.conf
net.ipv4.ip_forward=1
net.ipv4.conf.default.forwarding=1
net.ipv4.conf.all.forwarding=1
net.ipv4.conf.ens3.forwarding=1
net.ipv4.conf.wg0.forwarding=1
/etc/sysctl.d/30-ipv6_forward.conf
net.ipv6.conf.default.accept_ra = 2
net.ipv6.conf.all.accept_ra = 2
net.ipv6.conf.ens3.accept_ra = 2
net.ipv6.conf.wg0.accept_ra = 2
net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.ens3.forwarding=1
net.ipv6.conf.wg0.forwarding=1

Configuration for operation via systemd-networkd

Example -- Wireguard VPN gateway:

/etc/systemd/network configuration

On gateway peer:

/etc/systemd/network/99-wg0.netdev
[NetDev]
Name=wg0
Kind=wireguard
Description=WireGuard tunnel wg0

[WireGuard]
ListenPort=51871
PrivateKey=#GATEWAY_PEER_PRIVATE_KEY

[WireGuardPeer]
PublicKey=#VPN_PEER_PUBLIC_KEY
PresharedKey=#GATEWAY_PEER-VPN_PEER-PRESHARED_KEY
AllowedIPs=10.0.0.2/32
AllowedIPs=2001:db8:1234:5678::2/128, fd89:abc1:def2:1::2/128

On gateway peer:

/etc/systemd/network/99-wg0.network
[Match]
Name=wg0

[Network]
Address=10.0.0.1/24
Address=2001:db8:1234:5678::1/64
Address=fd89:abc1:def2:1::1/64
IPForward=yes
IPMasquerade=ipv4
# or
#IPMasquerade=both

On VPN peer:

/etc/systemd/network/99-wg0.netdev
[NetDev]
Name=wg0
Kind=wireguard
Description=WireGuard tunnel wg0

[WireGuard]
ListenPort=51902
PrivateKey=#VPN_PEER_PRIVATE_KEY
FirewallMark=0x89ab

[WireGuardPeer]
PublicKey=#GATEWAY_PEER_PUBLIC_KEY
PresharedKey=#GATEWAY_PEER-VPN_PEER-PRESHARED_KEY
AllowedIPs=0.0.0.0/0
AllowedIPs=::/0
Endpoint=198.51.100.49:51871

On VPN peer:

/etc/systemd/network/50-wg0.network
[Match]
Name=wg0

[Network]
Address=10.0.0.2/32
Address=2001:db8:1234:5678::2/128
Address=fd89:abc1:def2:1::2/128
DNS=2001:db8:1234:5678::1
DNS=10.0.0.1
DNSDefaultRoute=yes
Domains=~.

[RoutingPolicyRule]
FirewallMark=0x89ab
InvertRule=yes
Table=1000
Priority=10

[Route]
Gateway=2001:db8:1234:5678::1
GatewayOnLink=yes
Table=1000

[Route]
Gateway=10.0.0.1
GatewayOnLink=yes
Table=1000

Firewall/filtering configuration (using nftables)

On gateway peer:

  • Input filter
define upstream-if = ens0
define vpn-if = wg0
define mgmt-host = 203.0.113.51
define ssh-port = 22
define vpn-port = 51871
#
table inet inet-local-table {
  chain inet-local-input {
    type filter hook input priority filter
    policy drop
# Accept localhost traffic
    iif lo accept
# Bad TCP --> reject network scanning
    iif $upstream-if tcp flags & (fin|syn) == (fin|syn) counter drop
    iif $upstream-if tcp flags & (syn|rst) == (syn|rst) counter drop
    iif $upstream-if tcp flags & (fin|syn|rst|psh|ack|urg) == 0 counter drop
    iif $upstream-if tcp flags & (fin|syn|rst|psh|ack|urg) == (fin|psh|urg) counter drop
# Accept Wireguard inbound UDP traffic from peer to VPN port
    iif $upstream-if udp dport $vpn-port accept
# Accept ICMP from Wireguard peers
    iifname $vpn-if ip protocol icmp limit rate 5/second accept
    iifname $vpn-if meta l4proto ipv6-icmp limit rate 5/second accept
# Allow DNS from Wireguard peers
    iifname $vpn-if udp dport 53 accept
    iifname $vpn-if tcp dport 53 accept
# Remaining input from VPN interface to VPN server (local) prohibited
#   -- default policy drop
    iifname $vpn-if counter drop
# Drop invalid (untracked?) packets
    iif $upstream-if ct state invalid counter drop
# Accept established and related tracked connections
    iif $upstream-if ct state {established, related} accept
# Allow connection from given mgmt host on given ssh port
    iif $upstream-if ip saddr $mgmt-host tcp dport $ssh-port accept
# Limit ICMP packets accepted
    iif $upstream-if ip protocol icmp limit rate 5/second accept
    iif $upstream-if meta l4proto ipv6-icmp limit rate 5/second accept
# Count traffic dropped by default policy
    counter drop
  }
}
  • Forward filter
define upstream-if = ens0
define vpn-if = wg0
#
table inet inet-local-table {
  chain inet-local-forward {
    type filter hook forward priority filter
    policy drop
# Drop IP forward for upstream invalid packets
    iif $upstream-if ct state invalid counter drop
# Accept IP forward for upstream established and related tracked connections
    iif $upstream-if ct state {established, related} accept
# Accept all VPN traffic to be forwarded upstream
    iifname $vpn-if oif $upstream-if accept
# Count traffic dropped by default policy
    counter drop
  }
}

Manual operation via wg-quick

Bring up wg0 interface

$ sudo wg-quick up wg0

systemd operation via wg-quick

Start wg-quick@wg0 service; enable for operation upon reboot

$ sudo systemctl start wg-quick\@wg0
$ sudo systemctl enable wg-quick\@wg0

systemd-networkd operation

  • Enable and start systemd-resolved on VPN peer (required by "DNS=" lines under [Network] section)
  • Restart systemd-networkd

On gateway peer:

$ sudo systemctl restart systemd-networkd

On VPN peer:

$ sudo systemctl start systemd-resolved
$ sudo systemctl enable systemd-resolved
$ sudo systemctl restart systemd-networkd

Testing of VPN connection and operation

Read wireguard comm status on gateway and VPN peer(s)

$ sudo wg

Ping peer(s)

On VPN peer:

$ ping -4 -n -c 5 10.0.0.1

On gateway peer:

$ ping -4 -n -c 5 10.0.0.2

Optional: Persistent keepalive

If ping on gateway peer to VPN peer fails, configure devices located behind, e.g., a NAT router for persistent keepalive:

  • wg-quick: Add, e.g., "PersistentKeepalive = 15" to [Peer] section of /etc/wireguard/wg0.conf
  • systemd-networkd: Add, e.g., "PersistentKeepalive=15" to [WireGuardPeer] section of /etc/systemd/network/99-wg0.netdev

Read packet filter counters

$ sudo nft list ruleset | grep counter

Read packet filter logging

$ journalctl