I previously hinted at the desire for layered security in a small network of computers. WireGuard turns out to be a delightfully easy way to do just that. Here I outline a simplistic configuration that meets all of my own needs for networking two servers.
WireGuard is a virtual private network that is integrated into the
Linux kernel. It is exceedingly simple and highly opinionated. For
someone like me, who isn't exactly a cryptography expert, this is a
good thing. I don't need to spend hours digging through byzantine
configuration options and wade around vulnerable configurations —
they are not available. Interestingly it is also very small,
compared to other VPNs. You could conceivably read all of the source
code for fun (check drivers/net/wireguard/
in your
local Linux source tree).
It seems the jargon for what I intend is a "site to site virtual private network". Basically I do not need to forward traffic from clients to the wider internet and the only real "client" is another server (a physically separate machine, so far as I know — both are virtual private servers). Perhaps a picture would help:
If you look at that diagram and think something like "but that is so simple!", you are not wrong. It is actually a little curious how complex other systems make such a configuration. Happily WireGuard is nearly as simple as possible. Here is I how I set up a system like the one pictured above:
Machine A has an /etc/wireguard/wg0.conf
entry like
this:
[Interface]
PrivateKey = iOP7M0LDI0Avj/jb4Mr1YgmQ0wuDjAhiqbxqjAK7kHA=
ListenPort = 51820
Address = 10.0.0.1/24
[Peer]
PublicKey = OgPy99pGiJvY4t6n69KCRNNyD1hn66R5SOvrQOVBvTk=
AllowedIPs = 10.0.0.2/32
Endpoint = 109.170.24.9:51820
Machine B has an /etc/wireguard/wg0.conf
entry like
this:
[Interface]
PrivateKey = yJbXt8NsPfQjG3GhkGuTSpRBgNn25wkpd3gV3xIpo14=
ListenPort = 51820
Address = 10.0.0.2/24
[Peer]
PublicKey = NGQRUWJQPsr4mADIJlYA+e44j2jFCEEvR3/DmlONEik=
AllowedIPs = 10.0.0.1/32
Endpoint = 193.10.218.90:51820
The wireguard-tools
package includes a utility for
generating keys so creating them ends up looking like wg
genkey | tee privatekey | wg pubkey > publickey
With that much configuration and the wireguard-tools
package installed it is possible to, on each machine,
invoke wg-quick up wg0
. And that is
it. The Endpoint
is used to initiate the tunnel (it
refers to the remote machine's IP address) and
the AllowedIPs
designates what the machine will appear
as in the tunnel. As an example from Machine A I might try to ping
Machine B with:
$ ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=1.17 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.934 ms
The reverse, pinging machine A from B, works the same way. Any
workings of the private network are transparent. It is possible to
see what the operating system "sees" from this configuration using
the standard ip
tool since WireGuard is integrated with
Linux seamlessly:
$ 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.2/24 scope global wg0
valid_lft forever preferred_lft forever
link/none
With a VPN in place it is a simple matter of ramping the overall
security of the system up to match. In my case I want to disallow
any traffic from outside the VPN to "machine B". My latest
experiments have me using Fedora 35 on the server which ships
with firewalld
rather than my usual ufw
or iptables
. It turns out this makes things easier
still. I only need to add a zone within the firewall and designate
how traffic should (or should not) flow within zones.
Creating a new zone requires writing (or copying) an XML file that looks something like this:
# cat /etc/firewalld/zones/vpn.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>VPN</short>
<description>A zone for VPN traffic</description>
</zone>
Then I can add my VPN network interface to the zone
with: firewall-cmd --add-interface=wg0 --zone=vpn
. With
that done it is possible to route traffic like: firewall-cmd
--add-port=22/tcp --zone=vpn
Easy, right? With maybe an hour's worth of work it is possible to
setup an encrypted tunnel to isolate machines. Defense-in-depth
suggests we shouldn't rely only on the tunnel for security, which is
why yesterday I configured
an encrypted,
isolated SFTP server for database backups. Just
like ping
, pointing a database backup tool to this
VPN-connected machine requires only pointing to the IP address
assigned within the tunnel.