Wireguard - zbrewer/homelab GitHub Wiki
Wireguard is a relatively easy to configure, performant VPN that is well supported across systems. For that reason, it is the primary VPN solution that I use for remote access to internal systems and encrypting traffic on untrusted networks.
Note that the instructions below describe running an independent Wireguard server; however, it can also be run directly on an OPNsense firewall. See the instructions on the OPNsense page for more details.
In order to connect to the VPN remotely, the client config will need to include the public IP of the VPN server. While this IP could be used directly if it is a static IP or if it practically doesn't change, a DDNS setup will allow a (sub) domain to point to the public IP so that it doesn't have to be updated in the config whenever it changes and changes while you are away from the VPN server (and therefore don't know the new IP) don't break the configuration.
A DDNS server can be set up in a variety of ways including using the OPNsense plugin.
I installed Wireguard on an Ubuntu Server VM running on Proxmox. This was a normal installation with only two notable features:
- I assigned the VM's NIC to the VPN VLAN at provisioning time.
- I provided the VM with a static IP address on the VPN VLAN subnet.
I could have set up the Wireguard server on my "router" using an OPNsense plugin; however, this would have meant dedicating the entire VPN VLAN to Wireguard (I wanted to share it with Tailscale and OpenVPN) and it would have meant allowing additional connections directly to the router/firewall machine. For these reasons, I decided to host the server on a separate VM instead.
Start by installing the wireguard
package and creating public/private keys:
$ sudo apt-get update
$ sudo apt-get install wireguard
$ sudo -i
$ cd /etc/wireguard
$ umask 077; wg genkey | tee privatekey | wg pubkey > publickey
Next, create a new Wireguard configuration file at /etc/wireguard/wg0.conf
. It should contain:
[Interface]
Address = 10.0.200.13/32
ListenPort = 41194
PrivateKey = <private_key>
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens18 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o ens18 -j MASQUERADE
The Address
should be the address of the Wireguard server. <private_key>
should be replaced by the key generated earlier, and ens18
should be replaced with the ethernet interface name if it is different.
The server can then be enabled and started with:
$ sudo systemctl enable wg-quick@wg0
$ sudo systemctl start wg-quick@wg0
The server status can be shown with $ sudo systemctl status wg-quick@wg0
and the wireguard status can be seen with $ sudo wg show
. In addition, a new wg0
interface should show up on the machine when listing interfaces (such as with $ ip addr
).
The firewall must also be configured to forward traffic from the chosen Wireguard port (UDP) on the WAN interface to the Wireguard server. Without this, Wireguard connections can't be established remotely. See the Wireguard Documentation for instructions on how to do this. Importantly, Reflection NAT will also need to be setup in order to enable local clients to connect to the Wireguard server. This can be done by specifying additional local interfaces in addition to the "WAN" interface for port forwarding.
Client configurations must first be created on the client themselves and then added to the Wireguard server.
The official Wireguard app can be installed on Android or ChromeOS and configured for use. Create a new connection in the app and a new public/private key pair can be generated for you. The name
can be whatever you would like (descriptive name) and the DNS servers
should be set to the servers you would like to use. This can be a private/internal IP if you would like to use something like a Pi-Hole or it can be public servers. The Addresses
act like DHCP reservations and determine what IP address the device will effectively have on the VPN connection. This should likely be expressed as a specific address (/32) and should not conflict with any other assignments.
Next, add a peer for the Wireguard server. Fill out the server's Public key
(you can see this with the $ sudo wg
command on the server) and set the Allowed IPs
for the connection. These are the IP addresses that should be connected to over the VPN. Setting this to 0.0.0.0/0
will route all traffic over the VPN while setting it to something like 10.0.0.0/8
will route all traffic to IPs in the form 10.x.x.x. The Endpoint
should specify the public domain name or IP address of the Wireguard server as well as the port. For example: vpn.brew.foo:41194
.
The Linux client setup is extremely similar to the server:
$ sudo apt-get update
$ sudo apt-get install wireguard
$ sudo -i
$ cd /etc/wireguard
$ umask 077; wg genkey | tee privatekey | wg pubkey > publickey
$ umask 077; touch wg0.conf
Then, edit /etc/wireguard/wg0.conf
so that it contains the following:
[Interface]
# The client's private key
PrivateKey = <private_key>
# The client's IP address on the VPN
Address = 10.0.200.20/32
[Peer]
# The server's public key
PublicKey = <public_key>
# IPs to route over the VPN. 0.0.0.0/0 routes all traffic.
AllowedIPs = 10.0.0.0/16
# The server's public IPv4/IPv6 address and port
Endpoint = vpn.brew.foo:41194
The client can then be started/optionally enabled in the same way as the server:
$ sudo systemctl enable wg-quick@wg0
$ sudo systemctl start wg-quick@wg0
In order for the connection to be established and begin working, the clients must also be added to the Wireguard server. Append the following to the end of /etc/wireguard/wg0.conf
:
[Peer]
PublicKey = <public_key>
AllowedIPs = 10.0.200.20/32
In this case, the AllowedIPs
should match the address of the client. Restart the server for the new config to take effect:
$ sudo systemctl stop wg-quick@wg0
$ sudo systemctl start wg-quick@wg0
Client connections to the Wireguard server should now work.