This is an old revision of the document!
Wireguard is a revolutionary VPN technology that allows the fastest throughput and lowest latency compared to other traditional VPN technologies.
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.
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. Please make sure to also consult the official quickstart documentation:
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@router:/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 publicKey_routera -rw-r--r-- 1 root root 45 Feb 13 10:51 publicKey_routera
This information now needs to be added to the configuration file which we are going to be calling here wg0.conf:
root@routera:/jffs# cat wg0.conf [Interface] # RouterA = local PrivateKey = WOOgLRpUxq3XjGfuP79JHKR/f7dd+/0HkbCR1YMDakU= # This is the generated private Key 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 PublicKey = iu3524WoHe0UHkY4o6kQSTe1sx9lBArrdBR9mbe+0yA= 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.
root@routerb:/jffs# cat wg0.conf [Interface] # RouterB = local PrivateKey = WOOgLRpUxq3XjGfuP79JHKR/f7dd+/0HkbCR1YMDakU= # This is the generated private Key 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 PublicKey = Pr1EV/OukTXsj0eeEM96mOCW4Jy00iUMIFp24Z93owo= AllowedIPs = 192.168.200.2/32, 10.1.2.0/24
NOTE: If either of the router is behind a NAT and can't be reached from the Internet (e.g. port-forwarding/DMZ towards it is not possible), you need to force a keepalive from the NATed device towards the unNATed device. Let's assume Router B is behind a NAT your RouterA [peer] will look have the PersistentKeepalive defined:
[peer] # RouterA = remote Endpoint = rtra.ddns.org:51820 PublicKey = Pr1EV/OukTXsj0eeEM96mOCW4Jy00iUMIFp24Z93owo= AllowedIPs = 192.168.200.2/32, 10.1.2.0/24 PersistentKeepalive = 25
After the configuration part is completed and you have a wg0.conf on each site, we now need to tackle the scripting part. The are numerous ways to perform this but for simplicity sake we are cover this in a specific way: a single script on each site that we're going to call wg.sh
root@routera:/jffs# cat ./wg.sh
#!/bin/sh
int=wg0
port=51820
lnet="192.168.200.1/24"
rnet="10.1.2.0/24"
ifconfig $int down 2>/dev/null
ip link del dev $int 2>/dev/null
rmmod wireguard 2>/dev/null
modprobe wireguard
ip link add dev $int type wireguard
ip address add dev $int $lnet
wg setconf $int /jffs/$int.conf
sleep 2
ifconfig $int up
sleep 1
# bypass CTF for wireguard id CFF is enabled at all
[ $(nvram get ctf_disable) -eq 0 ] && {
iptables -t mangle -nvL PREROUTING | grep -q '.*MARK.*all.*wg0.*0x1/0x7' || iptables -t mangle -I PREROUTING -i $int -j MARK --set-mark 0x01/0x7
}
# Open port used by Wireguard
iptables -nvL INPUT | grep -q ".*ACCEPT.*udp.dpt.${port}$" || iptables -A INPUT -p udp --dport $port -j ACCEPT
# Accept packets from WireGuard internal subnet
iptables -nvL INPUT | grep -q ".*ACCEPT.*all.*$int" || iptables -A INPUT -i $int -j ACCEPT
# Set up forwarding
iptables -nvL FORWARD | grep -q ".*ACCEPT.*all.*$int" || iptables -A FORWARD -i $int -j ACCEPT
# Add route to opposite LAN
ip route | grep -q "^${rnet}.dev.$int.*" || ip route add $rnet dev $int
root@routera:/jffs# cat ./wg.sh
#!/bin/sh
int=wg0
port=51820
lnet="192.168.200.2/24"
rnet="10.1.1.0/24"
ifconfig $int down 2>/dev/null
ip link del dev $int 2>/dev/null
rmmod wireguard 2>/dev/null
modprobe wireguard
ip link add dev $int type wireguard
ip address add dev $int $lnet
wg setconf $int /jffs/$int.conf
sleep 2
ifconfig $int up
sleep 1
# bypass CTF for wireguard id CFF is enabled at all
[ $(nvram get ctf_disable) -eq 0 ] && {
iptables -t mangle -nvL PREROUTING | grep -q '.*MARK.*all.*wg0.*0x1/0x7' || iptables -t mangle -I PREROUTING -i $int -j MARK --set-mark 0x01/0x7
}
# Open port used by Wireguard
iptables -nvL INPUT | grep -q ".*ACCEPT.*udp.dpt.${port}$" || iptables -A INPUT -p udp --dport $port -j ACCEPT
# Accept packets from WireGuard internal subnet
iptables -nvL INPUT | grep -q ".*ACCEPT.*all.*$int" || iptables -A INPUT -i $int -j ACCEPT
# Set up forwarding
iptables -nvL FORWARD | grep -q ".*ACCEPT.*all.*$int" || iptables -A FORWARD -i $int -j ACCEPT
# Add route to opposite LAN
ip route | grep -q "^${rnet}.dev.$int.*" || ip route add $rnet dev $int
Please notice the only part that differs are the two variables (in purple in the diagram):
lnet= rnet=
No 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 wg.sh & in this case instead. Once the script completes (will take 5-10 seconds) your next step is to verify the last handshake command etween the two hosts via:
wg show
which btw can be shortened to
wg
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.1.2
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.
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 you can run advanced output like
root@routera:/# wg show all domp
If there's no issue with the VPN you might have a routing problem which can be verified via:
root@routera:/# route
TODO:
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