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:
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.
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
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]
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.
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:
You will need some permanent storage to have the settings surviving a reboot. In this sense you have multiple options like:
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.
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.
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.
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.
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:
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:
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.
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
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
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
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.
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
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.