Notice: Undefined offset: 1 in /usr/local/src/wordpress/wp-content/themes/montezuma/includes/parse_php.php on line 79

WireGuard – A Next-Gen VPN

~~   Forward   ~~

Why would we need another VPN when we already have IPSEC, PPTP, L2TP, OpenVPN, and an array of proprietary SSL VPNs?  After all these are tried and true and exhaustively tested.  But are they really, exhaustively tested?

WireGuard has around 4,000 lines of code  —  compare this with 600,000 lines of code for OpenVPN plus OpenSSL, or 400,000 lines of code for XFRM plus StrongSwan for an IPSEC VPN.  How can such huge code have all aspects fully tested, honestly?  WireGuard’s two orders of magnitude fewer lines of code means a lot smaller attack surface to have flaws in.  Reducing attack surface is the same principle used by micro-kernels, and is a cardinal principle of information security.

A much smaller codebase also means that it is more likely to work the way it is supposed to, in contrast with OpenVPN where tunnels can either crash or hang.  This really matters in a network with hundreds of clients, all of which should automatically manage their own always-on tunnels, and especially when the VPN is not meshed, as most.  Plus a small code footprint means instant on (no more 10 to 30 second handshakes), and high throughput.

Further, WireGuard deliberately avoids “industry standard” algorithms which have long-standing vulnerabilities, instead using only the latest, toughest primitives:

  • ChaCha20 for symmetric encryption, authenticated with Poly1305, using RFC7539’s AEAD construction
  • Curve25519 for ECDH
  • BLAKE2s for hashing and keyed hashing, described in RFC7693
  • SipHash24 for hashtable keys
  • HKDF for key derivation, as described in RFC5869

Those of us converting to WireGuard are in good company:
Can I just once again state my love for [WireGuard] and hope it gets merged soon?  Maybe the code isn’t perfect, but I’ve skimmed it, and compared to the horrors that are OpenVPN and IPSec, it’s a work of art.
— Linus Torvalds, on the Linux Kernel Mailing List
If this sounds like ‘damning with faint praise’, please familiarize yourself with Linus’ normal writing style.  This is full endorsement, and he will actually be including WireGuard in the Linux kernel around release 4.22.

~~   That Key Question   ~~

Let’s be honest, VPNs are not simple. (when not implemented in an appliance)  It took me weeks of part-time work to learn and implement IPSec to the point of distilling it enough to be able to clearly explain it.  And I’ve never bothered to learn OpenVPN because well, it’s lame.  Devs started WireGuard with the intent that it be as simple as SSH to implement, not to mention their principled rejection of cryptographic agility.  And it is nearly so once you understand the rules.

But the keys are so short, and there are no configuration options.  I’ve always stayed on the side of caution with a big honking key…

Consider that with WireGuard’s 256-bit key, there are 2256 total permutations to brute-force through.  Presuming that on average you’ll get the solution halfway through the set, it still leaves you with 2255 failures before success.  So say we have an extremely powerful system, able to test 38 million keys/second, and have the public Wireguard key for a VPN —   It will take a little less than 1.52 × 1070 seconds on average to come up with the private key.  As the estimated age of the universe is 4.32 × 1017 seconds, eh, a 256 bit key is probably good.

Why then, do IPSec and OpenVPN need such large keys?  Because you do not have to brute-force them.  There are vulns in the algos, so attacks used on traditional VPNs (or SSL for websites) behave more like a dictionary attack, than a brute-force attack;  you can eliminate great breadths of the testing space without actually trying them.  Convenient!

So long as the algos used by WireGuard are unbreached, a 256bit key is strong enough that the laws of physics make brute-forcing practically impossible. (For now, but we’ll have new algos then, already do actually)

~~   Implementation   ~~

But enough talk and objections.  Let’s do this.  We are going to start with the simplest case of an incoming VPN, then move on to a meshed VPN LAN, then to an outgoing connexion to a third-party WireGuard VPN provider.

We have a LAN of say, 50 machines, whether iron, VMs, or a mix, and a router connecting to the outside.  Start by designating a machine that has nothing else to do as the ‘WireGuard server’.  There’s no effective distinction in WG between server/client, they’re all peers, but it helps conceptually.  It’s a good idea to define the WG server to be other than the router or a machine with other work, in case the WG server is ever breached, our goal is to confine them in it.  We’ll assume all machines are RHEL Linux.


~~   The Incoming Channel   ~~

On the WG Server:

Install WG.  On the WG server:

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

# mkdir /etc/wireguard

Make the WG channel’s config file:

# (umask 077 && printf "[Interface]\nPrivateKey = " | sudo tee /etc/wireguard/inWG.conf > /dev/null)

Make the private key, and the public key from the private key:

# wg genkey | sudo tee -a /etc/wireguard/inWG.conf | wg pubkey | sudo tee /etc/wireguard/in-publickey

# chown -R 700 wireguard

Now edit the simple /etc/wireguard/inWG.conf.  It already has:

[Interface]
PrivateKey = fd4M~FDNJGK4ndskjvfk76s4n4%ddfFCB9nj*T%*dfn=

Your WG VPN will have its own RFC1918 private IP address space, which can be Class A 10.*.*.*/8, Class B 172.16.*.*/12, or Class C 192.168.*.*/16, your choice, makes no difference.  But this must not overlap with any other IP ranges in your systems.  I like 10.*.*.*/8 because it is less to type, a broader address space, and easier to remember.  So add a line to the config file with the IP address you want to assign to the WG server, and choose any UDP port from 1025-65535.  Here’s my example:

[Interface]
PrivateKey = fd4M~FDNJGK4ndskjvfk76s4n4%ddfFCB9nj*T%*dfn=
Address = 10.10.10.1/32
ListenPort = 45923

Now the laptops, phones, cars, etc out in the wild will be connecting to this WG server, so we need their public keys to complete this file (yes that would make this complete), but we haven’t set clients up yet so don’t start the server.

So we now have a Wireguard server set up, but it has noplace to go.  We must set up forwarding of packets in the LAN router to send everything coming in on the public IP port 45923/udp, to the WG server.  How you do this depends on your firewall  I run Shorewall.  Go in to /etc/shorewall and edit shorewall.conf.  Make sure that:

IP_FORWARDING=Yes

Edit rules and add:

DNAT            net     local:{WG server LAN IP}    udp    45923     -
# systemctl restart shorewall

Ok, ready for the client.

On the WG Client:

Install WG.  On the WG client:

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

# mkdir /etc/wireguard

Make the WG channel’s config file:

# (umask 077 && printf "[Interface]\nPrivateKey = " | sudo tee /etc/wireguard/officeWG.conf > /dev/null)

Make the private key, and the public key from the private key:

# wg genkey | sudo tee -a /etc/wireguard/officeWG.conf | wg pubkey | sudo tee /etc/wireguard/office-publickey

# chown -R 700 wireguard

Now edit the /etc/wireguard/officeWG.conf.  It already has:

[Interface]
PrivateKey = FV7kDoKm+DOTSovSD32lI0S92kVdWHnkJl5jqMYR3uf=

So add a line to the config file with an IP address in the range you’d assigned to the WG server, and use the same UDP port:

[Interface]
PrivateKey = FV7kDoKm+DOTSovSD32lI0S92kVdWHnkJl5jqMYR3uf=
Address = 10.10.10.2/32
ListenPort = 45923

Now we’ll add the [Peer] section to the client’s config file so the client will know how to reach the server.  We also need to specify which IP addresses you allow to be conveyed over the tunnel.  If you want the client to route all the client’s network packets over the WG tunnel, make it 0.0.0.0/0.

Get the server’s public key — on the server:

# cat /etc/wireguard/in-publickey

… and put it in the client config’s [Peer] section file thus:

[Interface]
PrivateKey = FV7kDoKm+DOTSovSD32lI0S92kVdWHnkJl5jqMYR3uf=
Address = 10.10.10.2/32
ListenPort = 45923

# Office
[Peer]
PublicKey = lI0S92kVdWHnkJl5jqMYR3ufFV7kDoKm+DOTSovSD32=
AllowedIPs = 10.10.10.2/32, 192.168.1.0/24
Endpoint = 47.103.172.50:45923

The Endpoint must be of course the WG server’s public IP on the router.

Not time to start the daemon yet.

On the WG Server:

# ss -ulp

users:((“chronyd”,pid=2980,fd=1))
UNCONN 0 0 *:45923 *:*

# wg

interface: inWG
public key: VdWHnkJl5jqMYR3ufFV7kDoKm+DOTSovSD32lI0S92k=
private key: (hidden)
listening port: 57962

peer: +8XL3QF4pDcBuaxrezBsk6onPxDlJUEJkWsUMnbIz2Y=
allowed ips: 10.1.3.2/32
# wg showconf

… In progress.


,'after' => '

') )