Wireguard configuration

From wikinotes

Overview

Wireguard is exposed as a network-interface representing each available VPN (ex: wg0, wg1, ...).
Each VPN has a prv/pub keypair, and each connection between two nodes has a pre-shared-key.

In order to make a connection, both servers must be running wireguard, and list the other as a peer.

Key Setup

# Setup Private Key for each Server
wg genkey > keyname.prv                    # generate private key
cat keyname.prv | wg pubkey > keyname.pub  # generate public key
chmod 600 keyname.*                        # set permissions
# Setup Pre-Shared-Key for each pairing of servers
wg genpsk > host1--host2.psk
wg genpsk > host1--host3.psk

Configuration

NOTE:

In order to use wireguard within a FreeBSD jail, it's networking must be setup using vnet

Persistent

Each network interface receives it's own configuration, with information about the peers it is able to connect to.

Configuration

The interface IP defines the address AND the private network range.
Here we are using a /24 subnet, which means that there are 254 addresses available.
The AllowedIPs are defined in /32 (I don't understand why).

For more background on subnets/CIDR notation, see Networking Overview and ipv4.

# /etc/wireguard/wg0.conf

[Interface]
Address = 10.0.0.1/24, fdc9:281f:04d7:9ee9::1/64
PrivateKey = PEER_A_PRIVATE_KEY
ListenPort = 55555

[Peer]
PublicKey = PEER_B_PUBLIC_KEY
PresharedKey = PEER_A-PEER_B-PRESHARED_KEY
AllowedIPs = 10.0.0.2/32, fdc9:281f:04d7:9ee9::2/128  # wireguard ip of server
Endpoint = peer-b.example:51902                       # public ip of server

[Peer]
PublicKey = PEER_C_PUBLIC_KEY
PresharedKey = PEER_A-PEER_C-PRESHARED_KEY
AllowedIPs = 10.0.0.3/32, fdc9:281f:04d7:9ee9::3/128


wg showconf > /etc/wireguard/wg0.conf  # dump dynamic connfiguration to file
wg-quick up wg0                        # activate VPN

System Service

systemd

systemctl enable wg-quick@<iface>.service  # [Linux] start VPN at boot

FreeBSD init

# /etc/rc.conf
wireguard_enable="YES"
wireguard_interfaces="wg0 wg1 wg2"  # interfaces to start at boot
service wireguard start

NAT endpoints

If one side of your connection is behind NAT, you must keep the connection alive from the side with a static ip as an endpoint.
You can do this with PersistentKeepalive = 25.

This is configured on the side behind NAT (since it is not always reachable).

# /etc/wireguard/wg-*.conf

[Peer]
Endpoint = 1.1.1.1:55555
PersistentKeepalive = 25  # adjust to suit your firewall state rules
# ...

DHCP endpoints

NOTE:
Doesn't seem necessary if I use PersistentKeepalive as outlined for NAT

If your IP address changes (DHCP, dyndns, ...), you'll need to periodically re-run a script to keep your connection alive. Docs recommend to run every 30s in a cron job.

# once per minute
* * * * * /bin/sh -c 'for i in /etc/wireguard/*.conf; do /usr/share/wireguard-tools/examples/reresolve-dns/reresolve-dns.sh "$i"; done'

Alternatively Arch Wiki has suggestion for systemd service which runs every 30s.

Multiple Interfaces

This may be unecessary - I had some issues with packet routing on hosts
with multiple interfaces, and multiple peers attached to an interface.

My problem was solved with a reboot (and possibly more importantly by rebooting the NAT'd connections.

jim salter blogged about having multiple wireguard interfaces on one host
his setup included PostUp/PostDown instructions that created routing table entries, and solidified connections with a ping.

# NOTE: linux specific

[Interface]
   Address = 10.0.0.2/24
   PrivateKey = PRIVATE_KEY_FROM_CLIENT1
   # set up routing from server/wg0 to server/wg1
   PostUp = route add -net 10.0.1.0/24 gw 10.0.0.1 ; ping -c1 10.0.0.1  # <-- (this)
   PostDown = route delete -net 10.0.1.0/24 gw 10.0.0.1                 # <-- (this to)
   SaveConfig = false

Before doing this, I'd try the DHCP setup.

Dynamic

You can also create/configure interfaces dynamically if you'd like.

Linux

# create network interface
ip link add dev wg0 type wireguard

# nodes require unique private addrs
# add ipv4 and optionally ipv6
ip addr add 10.0.0.1/24 dev wg0
ip addr add fdc9:281f:04d7:9ee9::1/64 dev wg0

# configure connection to `host2`
# (repeated for each server being connected)
wg set wg0 private-key /path/to/host.prv
wg set wg0 peer host2.pub \
  preshared-key /path/to/host1--host2.psk \
  allowed-ips 10.0.0.2/32,fdc9:281f:04d7:9ee9::2/128

# activate VPN
ip link set wg0 up

Firewall Setup

  • allow UDP traffic on configured Endpoint:ListenPort (public ip)
  • allow any traffic on wg* interface

nftables example


host with wireguard:

#!/usr/sbin/nft -f
# /etc/nftables.conf

table inet filter {
  chain input {
    type filter hook input priority 0;
    policy drop;

    # accept wg0 traffic
    # (you may want to allowlist ports)
    iif wg0 accept;

    # accept wireguard connection init traffic
    udp dport $wireguard_port ip daddr $public_ip accept;

    # ...
  }
}

pf example

# /etc/pf.conf

ext_if="jail0"
wg_if="wg0"
wg_port="55555"

block log all
pass out all

# accept wg0 traffic
# (you may want to restrict ports)
pass in on $wg_if

# accept wireguard connection init traffic
pass in on $ext_if proto udp to $public_ip port $wg_port

If this is a vnet jail, you'll also need to make sure that traffic gets passed to the jail where it's pf can take over the ruleset.

# /etc/pf.conf  (jail host)

set skip on jail_epairs  # ifconfig interface group
set skip on bridge0

block log all
pass out all

# ...rules...

Testing

NOTE:

If you're having trouble, check out wireguard debugging

Firstly, confirm that your interface is up, and has been assigned an ip addr.

sudo ip addr

# 3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
#    link/none
#    inet 10.0.0.1/24 scope global wg0
#       valid_lft forever preferred_lft forever

Also confirm the interface is displayed with info

sudo wg

# interface: wg0
#   public key: lkjAsdflkjoasiduffjweklrjdsoiaujfasljelkjouifsad
#   private key: (hidden)
#   listening port: 55555
# 
# peer: kljasdfurjjafu3rjlkjsaf9wrjjasfkljlkjaweruiouasdfj=
#   preshared key: (hidden)
#   allowed ips: 10.0.0.2/32

I had some initial issues with firewall. Try watching dropped packets.
Note this could be pf inside the jail, pf outside the jail, or the firewall on the target inbound/outbound side.

ping $target

# on $source/$target
sudo journalctl -f --dmesg | grep $wg_port       # linux
sudo tcpdump -n -e -ttt -i pflog0 port $wg_port  # freebsd

# even on failure, issuing `wg` should show 'latest handshake'
# with information about the failure.

If you're getting connection refused, but pf/nftables is not recording any dropped packets - make sure you're binding your server to 0.0.0.0, the FQDN, or the server's ip address (and not 127.0.0.1).