Site Tools


wireguard_on_freshtomato

Wireguard

Wireguard is a revolutionary VPN technology that allows the fastest throughput and lowest latency compared to other traditional VPN technologies. Here some not-better defined bench-marking from Internet:

Intro

This exceptional performance is possible because the code is executed within kernel-space as opposite to other technologies like OpenVPN/PPTP/tinc that run in the much slower user-space. It's an asymmetric-key technology (similar to OpenVPN per se) but more basic in functionality although there's a lot of momentum on the developing side and improvements are expected. Wireguard is not a talkative protocol, opposite it is likely to send data only when needed (except if the peer is defined with a forced keepalive option). One of the revolutionary approach wireguard took was to remove all the handshaking process, now data is accepted only if the decryption key works (less chatty, simpler and faster). Wireguard communicate by default over port 51820 UDP.
Before you get started make sure you consult the official quickstart documentation and perhaps also the unofficial one.

Overview

Until a functional GUI is developed, Wireguard is only available via command line. Its configuration, once the basic principles are digested, is relatively simple although there are some caveats that needs to be considered. As per today only FT's ARM devices have the relevant code included. If unsure you can try loading the kernel module as follow:

root@router:/# modprobe wireguard

No output means it worked and you should be able to find it in the list of loaded modules

root@router:/# lsmod | grep wireguard
wireguard             131012  0

If wireguard is not supported on your system though you will get the following error:

root@router:/# modprobe wireguard
modprobe: module wireguard not found in modules.dep

Syntax

The first step is familiarize yourself with the wg command and wg help is a great starting point

root@router:/# wg help
Usage: wg <cmd> [<args>]

Available subcommands:
  show: Shows the current configuration and device information
  showconf: Shows the current configuration of a given WireGuard interface, for use with `setconf'
  set: Change the current configuration, add peers, remove peers, or change peers
  setconf: Applies a configuration file to a WireGuard interface
  addconf: Appends a configuration file to a WireGuard interface
  syncconf: Synchronizes a configuration file to a WireGuard interface
  genkey: Generates a new private key and writes it to stdout
  genpsk: Generates a new preshared key and writes it to stdout
  pubkey: Reads a private key from stdin and writes a public key to stdout
You may pass `--help' to any of these subcommands to view usage.

Essentially every parameter supports the additional help to provide additional info e.g. adblock show help

root@router:/# wg show help
Usage: wg show { <interface> | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]

Configuration

There are multiple way wireguard can be set up but in principle you'll need a configuration file (as in VPN configuration, peers, keys, IPs, etc) and a script to initiate the process. The two go hand by hand.

Point-to-point

In this section we are going to illustrate how to achieve the simplest of the connections: point-to-point. Let's have two devices with the following prerequisites:

  • FT ARM with wireguard included
  • at least one of the devices will need a public IP address
  • DDNS setup for the public IP (unless you have static IPs)
  • SSH access to both devices
  • a backdoor into the router like a LAN host accessible via Teamviewer or similar that don't depend on the VPN functionality to be accesses (optional but strongly recommended)

You will need some permanent storage to have the settings surviving a reboot. In this sense you have multiple options like:

  • USB
  • CIFS
  • JFFS
  • NVRAM (via Init/firewall script)

Please remember that if the storage becomes unavailable the VPN will not function. For the sake of this example, but also for your final setup we are going to relay on JFFS. So assuming you have your JFFS partition formatted and mounted the filesystem of reference is /jffs.

Keys

The first step is to create a a working pair of keys for each device:

root@routerA:/jffs# wg genkey > privateKey_$(hostname)
root@routerA:/jffs# wg pubkey < privateKey_$(hostname) > publicKey_$(hostname)

At this point you should have two files created:

root@routerA:/jffs# ls -l
-rw-r--r--    1 root     root            45 Feb 13 10:51 privateKey_routera
-rw-r--r--    1 root     root            45 Feb 13 10:51 publicKey_routera

The content of these files needs to be added to the configuration file which we are going to be calling here wg0.conf:

Please do not use the keys in this example they are completely bogus and will not work!

root@routerA:/jffs# cat wg0.conf
[Interface] # RouterA = local
PrivateKey = WOOgLRpUxq3XjGfuP79JHKR/f7dd+/0HkbCR1YMDakU= # This is the generated privateKeyroutera on the local router
ListenPort = 51820 # Default port this router listen to, but can be changed if needed

[peer] # routerB = remote
Endpoint = rtrb.ddns.org:51820 # FDQN:port of Router B
PublicKey = iu3524WoHe0UHkY4o6kQSTe1sx9lBArrdBR9mbe+0yA= # This is the public key as generated on the remote device.
AllowedIPs = 192.168.200.1/32, 10.1.1.0/24 # 192. is the dedicated VPN IP addressing (intra-router), notice the /32! The 10. in this example is the LAN address space reachable via this endpoint. Do use a /24 for the LAN subnet.
root@routerB:/jffs# cat wg0.conf
[Interface] # RouterB = local
PrivateKey = WOOgLRpUxq3XjGfuP79JHKR/f7dd+/0HkbCR1YMDakU= # This is the generated privateKeyrouterb on the local router
ListenPort = 51820 # Default port this router listen to, but can be changed if needed

[peer] # RouterA = remote
Endpoint = rtra.ddns.org:51820 # FDQN:port of Router A
PublicKey = Pr1EV/OukTXsj0eeEM96mOCW4Jy00iUMIFp24Z93owo= # This is the public key as generated on the remote device.
AllowedIPs = 192.168.200.2/32, 10.1.2.0/24 # 192. is the dedicated VPN IP addressing (intra-router), notice the /32! The 10. in this example is the LAN address space reachable via this endpoint. Do use a /24 for the LAN subnet.

Endpoint behind NAT implications


On router with private IP addressing (behind a NAT) that can't be reached from the Internet inbound (port-forwarding/DMZ towards not possible), the connection will be initiated from the NATed device but you will need to force a keepalive activity towards the unNATed device. Remember wireguard doesn't relay on keepalive by default. Let's assume RouterB is behind an unmanaged NAT device (so your WAN has a private IP) your RouterA [peer] definition within wg0.conf will need to have the PersistentKeepalive defined. This allows the main router mapping table to stay updated and make the defined wireguard port reachable:

[peer] # RouterA = remote
Endpoint = rtra.ddns.org:51820
PublicKey = Pr1EV/OukTXsj0eeEM96mOCW4Jy00iUMIFp24Z93owo=
AllowedIPs = 192.168.200.2/32, 10.1.2.0/24
PersistentKeepalive = 25

25sec is a best practice considering the a “normal” nat-mapping can age as quickly as 30sec (of inactivity). You can however try to increase/decrease this as needed, just monitor the connection stability.

Two endpoints behind NAT implications


As discussed above, on a point-to-point we need at least 1 public IP or at least properly mapped port. It is however possible to have two endpoints both behind a NAT if these also have a termination towards a non-NATed endpoint. The PersistentKeepalive directive topic as per above still applies on every NATed endpoint.

Automated Script with full-mesh support

v1.22

wget https://tinyurl.com/28b5rckn -O- | tr -d "\r" > /jffs/wg.sh ; chmod 777 /jffs/wg.sh 

This is an all-in-one solution to create config via a wizard and automate all the other steps including key creation, loading, unloading, etc.

Assumptions:

  • On each device both wg.conf and wg0.conf both live in the same folder. You needn't/shouldn't define the path anywhere, the script will know where it is called from.
  • wg.sh and wg0.conf are ready to go and require no modification. All the config is auto-generated and stored exclusively within the wg0.conf





Particularly the makeconf parameter will guide you through some questions to fully automate the config. The result of this is placed in /tmp/wireguard where a subfolder called after the FQDN of each site is created and each of these subfolders contains two files:

  • wg.sh
  • wg0.conf

There is no need to modify anything within the files. You just need to copy them both onto the relevant device (jffs being a great place but any would so). This implicitly means you must run the makeconf on one (any) device only.

The

wg.sh

has been written in a way that can be run multiple times even consecutively (e.g. iptables/router rules will not be added if already present). The script also supports an optional parameter stop to safely unload the wireguard module and remove any relevant configuration it might have added to the system. Running the script will add a cron reference to selectively check the reachability of each defined VPN endpoint and remove them from the routing table is they are not to respond. This allows safe fail-back onto e.g. tinc.

Bringing up the VPN

Now we have the two files wg.sh and wg0.conf on each site we can bring up the VPN simply running wg.sh. Please note if you are to do this remotely you might get disconnected while the script is running so you should consider running something like { /jffs/wg.sh stop ; /jffs/wg.sh ; } & to make sure the script is fully executed (restarted in this case). Once the script completes (within 5-10 seconds), your next step is to verify the last handshake command between the two hosts via:

wg show

which btw can be shortened to wg only as the show parameter is implicit.

root@routera:/jffs# wg
interface: wg0
  public key: Pr1EV/OukTXsj0eeEM96mOCW4Jy00iUMIFp24Z93owo=
  private key: (hidden)
  listening port: 51820

peer: iu3524WoHe0UHkY4o6kQSTe1sx9lBArrdBR9mbe+0yA=
  endpoint: 60.61.62.63:51820
  allowed ips: 192.168.200.2/32, 10.1.2.0/24
  latest handshake: 16 seconds ago
  transfer: 0 B received, 1.44 MiB sent

The final part is to ping first the remote intra-VPN IP e.g. from routera ping 192.168.200.2, then the remote LAN IP e.g. from routera: ping 10.1.2.1

DNS Resolution

As per other VPN technologies if you want the local DNS server to respond to DNS queries from the other VPN sites you need to tell dnsmasq to enable listening on the wg0 interface. This is achieve adding the following directive to the dnsmasq's custom config:

interface=wg0

Running the wireguard VPN at boot

As hinted few times in this document you are relaying on permanent storage to make this working. No matter what this storage is, anything can happen and the storage where this is stored might become unavailable. This is the reason why JFFS has been used throughout the examples as it is perhaps (debatable) the most reliable. To run wireguard automatically place this line in the Administration/Scripts/Firewall:

until [ $(ping -c 1 -A -W 5 -q google.com &>/dev/null && echo 1 || echo 0) -eq 1 ]; do sleep 5; done; /jffs/wg.sh

It should work (exactly like this) in the Execute after mounted on the JFFS page itself but this hasn't been tested yet

Multiple VPN technologies at once

Usually it is not a great idea to mix and match different technologies and there are some resources utilization considerations to this too, however while writing this I discover a secondary positive effect in having tinc running in background while running wireguard in parallel. You could run tinc between all the hosts with a wider network mask e.g. 255.255.0.0 first and only then add wireguard on specific links only (the high performance one ideally). Having wireguard from your mobile phone or a site connected with a slow adsl is probably an unwanted overhead. So why using two VPNs at once?

Say I have 5 sites all connected over tinc (green) and 3 of those have high speed Internet connectivity where I decide to add wireguard (blue). I now find myself in the following here above.
Now let's assume something happens to wireguard and Site A (storage failure where the script lived or something) and it gets disconnected, that site is now unreachable.

However if the addressing has been done properly say “/16” for global tinc (so you get a single /16 for all your tinc network in the routing table) and a “/24” for each individual wireguard link, the tinc only devices are playing a very important resilience role here. To be clear, If the wireguard script is up (wg0 device exists) tinc will not be able to save you from (automatically at least) but it should allow you to at least ssh into a tinc only box (say Site E in the example) and from there you could ssh into a Site A again to try to fix the issue.

Routing verification

When your routing table has multiple options to reach a destination a good way to verify this is to take benefit of the build-in ip route command. In the above example with multiple VPN technologies at once you can verify the routing table's elected path via e.g.

ip route get x.x.x.x

root@router:/# ip route get 10.10.10.1
10.10.10.1 dev wg0  src 192.168.192.6
    cache  mtu 1420 advmss 1380
root@router:# ip route get 10.10.8.1
10.10.8.1 dev tinc  src 10.10.6.1
    cache  mtu 1500 advmss 1460

Troubleshooting

If any issue is experienced while running the script, consider changing the first line from

#!/bin/sh

to

#!/bin/sh -x

this will run the script in trace (debug) mode.

root@routera:/# wg help

is your friend and remember wg is a shortcut to wg show and would produce some helpful information e.g.

but you can run advanced output like

root@routera:/# wg show all dump

If there's no issue with the VPN you might have a routing problem which can be verified via:

root@routera:/# route

Pinging the intra-VPN Ip address first will help understanding is the issue is wireguard or rather routing related.
Finally please pay attention to the AllowedIPs field in the configuration, each intra-vpn router's IP is defined with a /32 in there where on the wg.sh it uses a /24. This is meant.

Known bugs

On the current version of the code:

root@router:/# wg --version
wireguard-tools v1.0.20200827 - https://git.zx2c4.com/wireguard-tools/


Using the wg (a.k.a. wg show) command it appears like the sent traffic is not counted as it should.

wireguard_on_freshtomato.txt · Last modified: 2023/03/26 20:55 by rs232