WireGuard
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
# 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
# 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
- https://www.digitalocean.com/community/tutorials/how-to-create-a-point-to-point-vpn-with-wireguard-on-ubuntu-16-04
- https://blog.stigok.com/2018/10/08/wireguard-vpn-server-on-centos-7.html
- https://mikkel.hoegh.org/2019/11/01/home-vpn-server-wireguard/