WireGuard

From Leo's Notes
Last edited on 15 September 2023, at 18:54.

WireGuard is an easy to use VPN that provides a secure connection between two parties using public key authentication.

Installation

Installing WireGuard on RHEL based systems

On CentOS / RHEL / Rocky Linux 8, you will need to install the WireGuard repos and EPEL for DKMS dependencies.

# curl -Lo /etc/yum.repos.d/wireguard.repo https://copr.fedorainfracloud.org/coprs/jdoss/wireguard/repo/epel-8/jdoss-wireguard-epel-8.repo
# yum -y install epel-release
# yum install wireguard-dkms wireguard-tools

On OracleLinux 8, the EPEL repo is named differently. Instead of epel-release, you'll need to install Oracle's version and enable the repo.

# curl -Lo /etc/yum.repos.d/wireguard.repo https://copr.fedorainfracloud.org/coprs/jdoss/wireguard/repo/epel-8/jdoss-wireguard-epel-8.repo
# yum -y install oracle-epel-release-el8
# yum-config-manager --enable ol8_developer_EPEL
# yum install wireguard-dkms wireguard-tools

Installing WireGuard on Ubuntu

On Ubuntu, WireGuard is part of the official repos and you simply need to install the package:

# apt install wireguard

Installing WireGuard on NixOS

On NixOS, use the wireguard-tools package to generate the WireGuard keys and then configure the WireGuard interface in your system configuration to enable it on the system:

# umask 077
# mkdir ~/wireguard-keys
# nix-env -iA wireguard-tools
# wg genkey > ~/wireguard-keys/private
# wg pubkey < ~/wireguard-keys/private > ~/wireguard-keys/public

## Add the following to your configuration.nix
# cat <<EOF >> /etc/nixos/configuration.nix
networking.wireguard.interfaces = {
  wg0 = {
    ips = [ "10.100.0.1/24" ];
    listenPort = 51820;
    privateKeyFile = "path to private key file";
      peers = [
        {
          publicKey = "public key";
          # List of IPs assigned to this peer within the tunnel subnet. Used to configure routing.
          allowedIPs = [ "10.100.0.2/32" ];
        }
      ];
...
}
EOF

## Then apply the changes
# nixos-rebuild switch

Installing WireGuard on OpenWRT

On OpenWRT, install the luci-proto-wireguard and luci-app-wireguard packages. The WireGuard config including settings its IP address, listen ports, and peers are done through the Luci web interface only.

See: https://openwrt.org/docs/guide-user/services/vpn/wireguard/basics

Quick Start

Configuration

WireGuard sets up a private network amongst one or more remote endpoints using UDP traffic. All traffic sent and received by WireGuard is encrypted using the endpoint's public key and decrypted using this instance's private key. Therefore, each WireGuard node must have its own set of public/private keys.

Each WireGuard instance will create a WireGuard interface (eg. wg0). The configuration file should be named after each WireGuard interface. To get started, create a new wg0.conf file with a new private key:

## Create the directory if it doesn't already exist
# mkdir /etc/wireguard
# cd /etc/wireguard

## Generate a new private / public key
# (umask 077 && printf "[Interface]\nPrivateKey = " | sudo tee /etc/wireguard/wg0.conf > /dev/null)
# wg genkey | sudo tee -a /etc/wireguard/wg0.conf | wg pubkey | sudo tee /etc/wireguard/publickey

Continue editing the wg0.conf and specify this endpoint's address. The VPN's subnet is determined based on the address and netmask specified here. What you should end up with should be something similar to this:

# cat /etc/wireguard/wg0.conf
[Interface]
PrivateKey = **secret**
ListenPort = 5555
Address = 10.44.118.100/24

Repeat the steps above on another remote endpoint.

Connecting two (or more) endpoints

After installing WireGuard and configuring wg0 on two or more endpoints, you should have something similar to this:

Site A Site B
/etc/wireguard/wg0.conf:
[Interface]
PrivateKey = SITE-A-KEY
ListenPort = 5555
SaveConfig = true
Address = 192.168.1.1/24
/etc/wireguard/wg0.conf:
[Interface]
PrivateKey = SITE-B-KEY
ListenPort = 5555
SaveConfig = true
Address = 192.168.1.2/24

Start WireGuard on both sites. The wg0 network device should have the address that is specified in the configuration file.

# systemctl start wg-quick@wg0
# systemctl enable wg-quick@wg0
# ip a show wg0
6: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 192.168.1.2/24 scope global wg0
       valid_lft forever preferred_lft forever

Next, connect Site A to Site B and vice versa using the each other's public key.

Site A Site B
Determine Site A's public key by reading /etc/wireguard/publickey or by running echo SITE-A-KEY | wg pubkey. Add the following lines to /etc/wireguard/wg0.conf and restart WireGuard.
[Peer]
PublicKey = SITE-B-PUBLIC-KEY
AllowedIPs = 192.168.1.2/32
Endpoint = SITE-B-EXTERNAL-IP:5555
or run:
# wg set wg0 peer SITE-B-PUBLIC-KEY \
  endpoint SITE-B-IP:5555 \
  allowed-ips 192.168.1.2/32
Determine Site B's public key by reading /etc/wireguard/publickey or by running echo SITE-B-KEY | wg pubkey. Add the following lines to /etc/wireguard/wg0.conf and restart WireGuard.
[Peer]
PublicKey = SITE-A-PUBLIC-KEY
AllowedIPs = 192.168.1.1/32
Endpoint = SITE-A-EXTERNAL-IP:5555
or run:
# wg set wg0 peer SITE-A-PUBLIC-KEY \
  endpoint SITE-A-IP:5555 \
  allowed-ips 192.168.1.1/32

If everything worked, you should be able to ping each other's private IP address. You should also be able to see each peer's status by running wg show.

# wg show
interface: wg0
  public key: (public-key)
  private key: (hidden)
  listening port: 5555

peer: (peer-public-key)
  endpoint: 10.1.1.1:5555
  allowed ips: 192.168.1.1/32
  latest handshake: 55 seconds ago
  transfer: 68.72 KiB received, 218.09 KiB sent
  persistent keepalive: every 30 seconds

Additional peers can be added by repeating the steps above.

Allowed IPs list

Each [Peer] entry has a AllowedIPs list which tells WireGuard what traffic can be received from and sent to this endpoint. Any traffic received from this peer that doesn't originate from an address listed in AllowedIPs is dropped. Furthermore, when WireGuard brings up the connection, it will also create routing table entries for each CIDRs listed in the AllowedIPs list. Therefore, as written in the WireGuard documentation: "when sending packets, the list of allowed IPs behaves as a sort of routing table, and when receiving packets, the list of allowed IPs behaves as a sort of access control list."

As a rule of thumb, you will typically see single /32 CIDRs in the AllowedIPs list on servers and one or more CIDR subnets on clients.

For example, you may have a peer defined on a WireGuard server like this:

[Peer]
PublicKey = xxxx
Endpoint = x.x.x.x:xxxx
AllowedIPs = 192.168.1.1/32

This tells WireGuard that this peer will only send traffic that originates from 192.168.1.1 and that all traffic destined for 192.168.1.1 should go to this peer. Any traffic from this peer that doesn't originate from 192.168.1.1 will get dropped. As a result, if you intend to use WireGuard for some sort of NAT application, you'd need to enable masquerading.

Another example with multiple ranges would look like this:

[Peer]
PublicKey = xxxx
Endpoint = x.x.x.x:xxxx
AllowedIPs = 192.168.1.0/24, 192.168.10.0/24

Similar to the first example, this tells WireGuard that traffic destined to 192.168.1.0/24 and 192.168.10.0/24 should go through to this peer and that traffic from 192.168.1.0/24 and 192.168.10.0/24 can be received from this peer. Because the AllowedIPs also add a routing table entry, you should not have a WireGuard instance where you have multiple peers with overlapping AllowedIPs since traffic would get routed to the first entry in the routing table.

Accessing other internal subnets

In order to access a private/internal subnet using WireGuard, you will need to:

  • On the client-side, specify the internal networks you wish to access under the AllowedIPs list. Subnets listed under the AllowedIPs list will be added as additional routing table entries by WireGuard when the connection is brought up.
  • Enabling masquerading on the server-side so that LAN traffic gets routed back to the client via WireGuard. This can be accomplished by specifying a PostUp/PostDown directive in WireGuard.

Client-side setup

On the client side (that's trying to access the remote internal subnets), edit the peer's AllowedIPs list to include the remote subnets.

For example, if the you wish to access two networks 192.168.0.0/24 and 192.168.100.0/24 that only the server-side has access to, you would define:

AllowedIPs = 192.168.0.0/24, 192.168.100.0/24

Server-side setup

On the server side (that has access to the internal subnets the client is trying to access), edit the [Interface] section to include the following PostUp and PostDown commands, where eth0 is the interface to your 192.168.0.0/24 and 192.168.100.0/24 LANs

PostUp   = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

Alternatively, if you don't want to deal with interface names, you may use something like: iptables -t nat -A POSTROUTING -s 10.1.1.0/24 -d 192.168.1.0/24 -j MASQUERADE

Check your work

Bring WireGuard up on both endpoints. The client side should have a routing table for each of the internal subnets via the WireGuard interface.

Troubleshooting

Missing Kernel Module

If the kernel module isn't loading, ensure that you have the proper kernel development package installed for the running kernel. Reinstall the wireguard-dkms package to trigger a kernel module rebuild. Alternatively, trigger DKMS to rebuild the module with:

# dkms status

## Build and install the version that's available on your system.
# dkms build -m wireguard/1.0.20211208
# dkms install wireguard/1.0.20211208

Connection Timeout

If your WireGuard connections are dropping or becomes unresponsive, add a PersistentKeepalive under the Peer section. Eg:

[Peer]
PublicKey = private-key
AllowedIPs = 192.168.1.1/32
Endpoint = endpoint:5555
PersistentKeepalive = 30

See Also