{Wire}guard from your ISP

WireGuard aims to be as easy to configure and deploy as SSH. You establish a VPN connection by simply exchanging public keys, and the rest is transparently handled by WireGuard.

There are many other technologies, however wireguard is uniquley interesting for:

  • cryptokey routing: the first principles simply mapping public keys and sets of allowed addreses, making wireguard easier to grok for deployments.
  • endpoints and roaming: also initial principles that facilitate NAT traversal and utilization of dynamic addressing through keepalives.
  • focusing on simplicity and ease-of-auditability, discussed here by the author, compared to other technologies which are sprawling implementations that make auditing difficult.
  • having formal verification coverage: presented here and discussed here.
  • having recognition for eventually belonging in the kernel: mentioned here by Linus himself.

People are getting into a recent uproar about entities like facebook and cambridge analytica while willing putting their information into these ecosystems. We should instead be considerably more concerned about the unwilling surveillance we’re a part of from our ISP. Recently the guardian and some discussion pushed me to finally look into applying wireguard to something like the home network design.

Choosing a VPN Provider

For choosing a provider I was compelled to use mullvad based on seeing their presence in a bunch of places over recent years and seeing them consistently earn the respect of reviewers. They were one very early providers of wireguard end points and they’ve been supportive of the upstream development project. So, sign up for an account.

Install WireGuard on the EdgeRouter

The vyatta-wireguard package is a Vyatta module and pre-built binaries for the Ubiquiti EdgeRouter to support WireGuard. It gets built when Ubiquiti releases the GPL Archive for firmware releases, this only occurs on stable releases. Let’s assume you have an ER-4 and that you’ve got ssh already set up:

ssh router

add system image https://dl.ubnt.com/firmwares/edgemax/v1.10.x/ER-e300.v1.10.1.5067768.tar

reboot

Make sure that you’re grabbing the most recent firmware, the url above is likely dated.

The way the EdgeRouter handles upgrades is to unpack and create an entirely new filesystem. As we want some files (e.g. {public,private}keys) to persist between updates we will utilize the /config area of the filesystem as the entire /config directory is recursively copied to the new image. We’ll get the most recent release of vyatta-wireguard and install it:

ssh router

sudo su

cd /config/

mkdir packages

cd packages/

curl <link-to-wireguard-e300-0.0.20180304-1> --output 'wireguard-e300-0.0.20180304-1.deb'

dpkg -i wireguard-e300-0.0.20180304-1.deb

As the link for the download will be to some obscure aws mirror you might find it easier to use a browser plug-in like cliget.

Bring up WireGuard Tunnel

First we’ll want to generate a keypair for our WireGuard interface. Personally I think its easier to have different interfaces for difference purposes. As a convention I start with wg0 being my vpn backhaul tunnel, and create more WireGuard interfaces for other purposes. Keypair for wg0:

ssh router

sudo su

cd /config/auth

wg genkey | tee wg0 | wg pubkey > wg0.pub

Now using our account number and public key we need to obtain an address to use with our tunnel:

curl https://api.mullvad.net/wg/ -d account=ACCOUNT --data-urlencode pubkey=PUBKEY

Where:

  • ACCOUNT: your mullvad account number
  • PUBKEY: literally cat your /config/auth/wg0.pub

This should return and ipv4 and ipv6 address, we’ll just use the ipv4 for now. Let’s set up the interface:

ssh router

config

set interfaces wireguard wg0 address ADDRESS

set interfaces wireguard wg0 peer PEERKEY allowed-ips 0.0.0.0/0

set interfaces wireguard wg0 peer PEERKEY endpoint 'SERVER:PORT'

set interfaces wireguard wg0 peer PEERKEY persistent-keepalive 15

set interfaces wireguard wg0 private-key /config/auth/wg0

set interfaces wireguard wg0 route-allowed-ips false

commit;save;

Where:

  • ADDRESS: is the ipv4 address returned by our query above
  • PEERKEY: is the server “Public Key” from this list that you want to use
  • SERVER: is the “Wireguard Server” you want to use from this list
  • PORT: is the “Multihop port” from that same server this list

It is important to recognize that you’re setting route-allowed-ips false, as you’ve set allowed-ips to 0.0.0.0/0 for this peer and that can wreak havoc with the system.

We now need to set up a source NAT translation for the layer3 WireGuard interface:

set service nat rule 5010 outbound-interface wg0

set service nat rule 5010 outside-address address ADDRESS

set service nat rule 5010 type source

commit;save;

Where:

  • ADDRESS: is the ipv4 address returned by our query above without the CIDR notation

Now, this WireGuard inerface is effectively a WAN, we want to treat it with the same concern that we do to our ISP WAN and apply an appropriate firewall:

set firewall name WG_IN default-action drop

set firewall name WG_IN description 'packets from wg0 through router to LAN'

set firewall name WG_IN enable-default-log

set firewall name WG_IN rule 1 action accept

set firewall name WG_IN rule 1 description 'allow established sessions' set firewall name WG_IN rule 1 protocol all

set firewall name WG_IN rule 1 log disable

set firewall name WG_IN rule 1 state established enable

set firewall name WG_IN rule 1 state invalid disable

set firewall name WG_IN rule 1 state new disable

set firewall name WG_IN rule 1 state related enable

set firewall name WG_IN rule 2 action drop

set firewall name WG_IN rule 2 description 'drop invalid state'

set firewall name WG_IN rule 2 log enable

set firewall name WG_IN rule 2 protocol all

set firewall name WG_IN rule 2 state invalid enable

set firewall name WG_LOCAL default-action drop

set firewall name WG_LOCAL description 'packets from wg0 to router'

set firewall name WG_LOCAL enable-default-log

set firewall name WG_LOCAL rule 1 action accept

set firewall name WG_LOCAL rule 1 description 'allow established sessions'

set firewall name WG_LOCAL rule 1 log disable

set firewall name WG_LOCAL rule 1 protocol all

set firewall name WG_LOCAL rule 1 state established enable

set firewall name WG_LOCAL rule 1 state invalid disable

set firewall name WG_LOCAL rule 1 state new disable

set firewall name WG_LOCAL rule 1 state related enable

set firewall name WG_LOCAL rule 2 action drop

set firewall name WG_LOCAL rule 2 description 'drop invalid state'

set firewall name WG_LOCAL rule 2 log enable

set firewall name WG_LOCAL rule 2 protocol all

set firewall name WG_LOCAL rule 2 state invalid enable

set interfaces wireguard wg0 firewall in name WG_IN

set interfaces wireguard wg0 firewall local name WG_LOCAL

commit;save;

Now if you’re wg0 interface was up before applying this firewall you will see packet loss. You can just reboot the router and wireguard will make that connection again at boot time. We didn’t specify a listen port on the wg0 interface, so the traffic between the upstream vpn and the wg0 interface has to be connected via your router making the outbound request and the firewall allowing packet flow based on the WG_IN rule 1 above for established and related traffic.

Send DNS requests over the Tunnel

In the case that you’re using the Mullvad DNS you can simply set the system domain-name to the associated DNS server on the Mullvad side, which is 10.x.0.1. However if you’re using another recursor like 8.8.8.8 or the new 1.1.1.1 you can force this by adding routes in the main table for the resolver addresses (assuming 1.1.1.1, 1.0.0.1):

ssh router

configure

set protocols static interface-route 1.1.1.1/32 next-hop-interface wg0

set protocols static interface-route 1.0.0.1/32 next-hop-interface wg0

commit;save;

Now from any host on the network you can do an mtr 1.1.1.1 and see the traced path that the traffic takes that is destined for that address.

I find that this is currently an easier way to hide your DNS requests from your ISP than trying to adopt any of the clients required to perform DNS-over-TLS or DNS-over-HTTPS.

Send Selective hosts over the Tunnel

In the case of the home network design we have a “home” and “guest” network. We’ll send some hosts from the “home” network over the tunnel and all hosts from the “guest” network over the tunnel. We accomplish this through something called Policy Based Routing. First we make a routing table for the wireguard tunnel:

set protocols static table 1 description 'table to force wg0:mullvad'

set protocols static table 1 interface-route 0.0.0.0/0 next-hop-interface wg0

set protocols static table 1 route 0.0.0.0/0 blackhole distance 255

As we’re creating multiple routing tables we need to be cognizant that traffic can “fall through” to the main routing table, which means if the wg0 interface goes down traffic would immedietly be traversing your ISP line raw. So we add a blackhole route with a higher distance than the next-hop-interface route (255) to stick traffic to this table.

Now we’ll create a an address group that we will then use in a modify ruleset:

set firewall group address-group HOME_MULLVAD description 'hosts in HOME that route out via Mullvad' set firewall group address-group HOME_MULLVAD address 172.16.0.10

set firewall group address-group HOME_MULLVAD address 172.16.0.11

set firewall group address-group HOME_MULLVAD address 172.16.0.12

In this case we’re going to have the hosts in the “home” network at 10,11,12 respectively backhaul over the tunnel. We’ll write the modify rule:

set firewall modify PBR_MODIFY description 'set routing tables selectively based on source address'

set firewall modify PBR_MODIFY rule 10 action accept

set firewall modify PBR_MODIFY rule 10 description 'exclude LAN to LAN traffic from PBR'

set firewall modify PBR_MODIFY rule 10 destination address 172.16.0.0/12

set firewall modify PBR_MODIFY rule 100 action modify

set firewall modify PBR_MODIFY rule 100 description 'modify all traffic coming from interloper'

set firewall modify PBR_MODIFY rule 100 modify table 1

set firewall modify PBR_MODIFY rule 100 source address 172.16.1.0/24

set firewall modify PBR_MODIFY rule 200 action modify

set firewall modify PBR_MODIFY rule 200 description 'modify selective hosts within haven'

set firewall modify PBR_MODIFY rule 200 modify table 1

set firewall modify PBR_MODIFY rule 200 source group address-group HOME_MULLVAD

set interfaces ethernet eth2 vif 7 firewall in modify PBR_MODIFY

set interfaces ethernet eth2 vif 14 firewall in modify PBR_MODIFY

Now any hosts enumerated in the HOME_MULLVAD address group will be being modified to use the wireguard tunnel. You can test by checking mtr to any arbitrary site.

I’ll say on my network I noticed only a 15-20ms latency increase with little to no bandwidth drop. WireGuard is incredibly impressive.

Special thanks to Lochnair for learning how to package wireguard for vyatta and for patiently explaining how to do everything that is described above.