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
  }
}

Operation of Wireguard link for VPN

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