====== Wireguard ======
Wireguard is a revolutionary VPN technology that allows for very fast throughput with low latency compared to traditional VPN technologies.
Here are some rough benchmarks that illustrate the performance differences:
\\
{{:pasted:20230221-073749.png}}
===== Web interface or command-line configuration =====
Release 2024.1 and later allow Wireguard configuration in the web interface. Configuration is also still available via command line. Instructions on this wiki page detail how to configure Wireguard via the command-line interface. Instructions for the graphical web interface will follow later. The main principles apply regardless of the interface used.
===== Introduction =====
Wireguard's fast performance is possible because code is executed inside kernel-space. Other technologies like OpenVPN, PPTP, or tinc, run in the much slower user-space. Wireguard still uses asymmetric-key technology (like OpenVPN) that is more basic in functionality. However, development is ongoing, and encryption key improvements are expected soon.
Wireguard is not a "talkative" protocol. It tends to send data only when needed (unless there's a peer with a forced keepalive option). A new approach with Wireguard was to completely remove handshaking. Now, data is accepted only if the decryption key works. This makes Wireguard less "chatty", simpler, and faster. By default, Wireguard communicates over UDP port 51820.
Before configuring Wireguard, consult the official documentation's [[https://www.wireguard.com/quickstart/|Quick Start]] guide, and maybe the [[https://github.com/pirate/wireguard-docs|unofficial]] version too.
===== Overview =====
Wireguard is now available in FreshTomato's web interface since release 2024.1. It is also still available via command line.
Once you understand some basic principles, it is fairly simple to configure. Currently, only ARM-based devices include the code needed to run Wireguard.
\\
=== Checking if Modules are Available/Running ===
\\ If you're unsure, try loading the kernel module as follows:
root@router:/# modprobe wireguard
If you see no output, it means Wireguard executed.
\\ \\ \\ You should now be able to find it in the list of loaded modules:
root@router:/# lsmod | grep wireguard
wireguard 131012 0
\\
\\
If Wireguard isn't supported on your system, you'll see the following error:
root@router:/# modprobe wireguard
modprobe: module wireguard not found in modules.dep
===== Syntax =====
The first step is to familiarize yourself with the ''wg'' command. For this, typing: ''wg help'' is a great place to start.
\\
root@router:/# wg help
Usage: wg []
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.
\\
\\
In general, typing any subcommand followed by "help" offers additional help for the command.
For example:
root@router:/# wg show help
Usage: wg show { | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]
\\
===== Configuration =====
There are many ways Wireguard can be set up. However, in principle, you'll need a configuration file (containing VPN configuration, peers, keys, IPs, and so on) and a script to initiate the process. The two work together.
==== Point-to-Point Connection ====
This will illustrate how to achieve a point-to-point connection, the simplest kind.
Let's assume there are two devices with these prerequisites:
* An ARM-based device with Wireguard in the firmware build.
* At least one device with a public IP address.
* DDNS configured for the device's public IP address \\ (Unless you have static addresses).
* SSH access available on both devices.
* An alternate method of accessing the router, or a host on the LAN \\ accessible via remote access software that doesn't need VPN access. \\ The latter is optional but strongly recommended.
\\
You will need some type of permanent storage so settings will survive a reboot.
The options include:
* [[nas-usb|USB]]
* [[admin-cifs|CIFS]]
* [[JFFS|JFFS]]
* NVRAM (via an init or firewall script)
\\
If the storage becomes unavailable, the VPN won't function.
For this example, and the final setup, we'll use JFFS. It's assumed you have a JFFS partition mounted at: "/jffs".
\\
=== Keys ===
The first step is to create a working pair of keys for each device:
root@routerA:/jffs# wg genkey > privateKey_$(hostname)
root@routerA:/jffs# wg pubkey < privateKey_$(hostname) > publicKey_$(hostname)
\\
\\
The above two key generation programs should create two files:
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 must be added to the configuration file. In this case, we will call that file: "wg0.conf".
**Do not** use the keys from this example. They are hypothetical and only an example.
\\ \\ The contents of the wg0.conf file on routerA are as follows:
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.
\\ \\ \\
The contents of the wg0.conf file on routerB look like this:
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.
\\
\\
\\
=== The Consequences of an Endpoint Sitting behind a NAT Device ===
\\
{{:pasted:20230213-145253.png}}\\
\\
On a network with private addressing (behind NAT), unreachable from the Internet, the connection is initiated from the NAT'd device. However, you must force keepalive activity towards the unNAT'd device to maintain the connection. By default, Wireguard doesn't use keepalive packets.
\\
Let's assume routerB is behind an unmanaged NAT device (your WAN has a private IP). Your routerA [peer] definition in wg0.conf will need to have ''PersistentKeepalive'' defined. Doing this keeps the main router mapping table updated, making the defined Wireguard port reachable.
\\ \\ The necessary changes to wg0.conf for this are: \\
[peer] # routerA = remote
Endpoint = rtra.ddns.org:51820
PublicKey = Pr1EV/OukTXsj0eeEM96mOCW4Jy00iUMIFp24Z93owo=
AllowedIPs = 192.168.200.2/32, 10.1.2.0/24
PersistentKeepalive = 25
\\
A //PersistentKeepalive// value of 25 seconds is best practice, since a typical NAT mapping can expire in just 30 seconds of inactivity. However, you can adjust this, as needed. Just make sure to to monitor the connection stability when making adjustments.
\\
\\
=== The Consequences of Having Two Endpoints Sit behind a NAT Device ===
\\
{{:pasted:20230213-144712.png}}\\ \\
On a point-to-point connection, you need at least one public IP address or mapped port. However, it's possible to have two endpoints behind a NAT, if they also terminate towards a non-NAT'd endpoint. The PersistentKeepalive guidance above still applies to every NAT'd endpoint.
===== Automated Script with Full Mesh Support =====
A wizard creates the configuration in this all-in-one solution. It will automate all other steps including key creation, loading, unloading and more. The current version of this script is: 1.22\\ \\ \\
wget https://tinyurl.com/28b5rckn -O- | tr -d "\r" > /jffs/wg.sh ; chmod 777 /jffs/wg.sh
\\
==== Assumptions: ====
\\
* On each device, both the "wg.conf" and "wg0.conf" files are in the same folder. You needn't/shouldn't \\ have to define the path anywhere. The script will know from where it is called.
* The "wg.sh" and "wg0.conf" files are complete and require no modification. The entire configuration is \\ automatically generated and stored in the "wg0.conf" file. \\ \\ {{:pasted:20230324-065819.png?580}}\\ \\ \\ The script will display an introduction screen:
\\ {{:pasted:20230326-205524.png?852}}\\ \\ \\
Running "wg.sh" with the //makeconf// parameter will guide you through questions to fully automate the configuration. The result is placed in the: "/tmp/wireguard" folder. Subfolder(s) will be created there, named after the FQDNs of each site created. Each of these folders contains two files:
* wg.sh
* wg0.conf
\\
You do not need to make changes to those files. Simply copy them both to the relevant device (preferably jffs). This means you must run the makeconf on any one, and only one, device.
The wg.sh script has been written so that it can be run multiple times, even consecutively. Router and iptables/router rules that are already present in the configuration it creates will not be added again.
The script also supports an ''stop'' parameter option, to unload the Wireguard module and remove any relevant settings it added to the system. Running the script adds a cron reference to selectively check the connectivity of every defined VPN endpoint, and remove it from the routing table. This way, it will not respond. This allows safe fail-back, for example, onto tinc or other VPN protocols/modules.
==== Starting the VPN ====
Now that we have the "wg.sh" and "wg0.conf" files on each site, we can bring up the VPN simply running ''wg.sh''. If you do this remotely, you might get disconnected while the script is running. For this reason, you should consider running something like '' { /jffs/wg.sh stop ; /jffs/wg.sh ; } &'' to ensure the script has fully completed (or in this case, restarted).
Once the script completes (in about 5-10 seconds), the next step is to verify the last handshake command between the two hosts using the //show// parameter, like this:
wg show
\\
This can be shortened to just ''wg'' , since 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 next step is to ping the remote intra-VPN IP address.
For example, from RouterA: ''ping 192.168.200.2''
Then ping the remote LAN IP address for example, from RouterA: ''ping 10.1.2.1''\\
==== DNS Resolution ====
As with other VPN protocols, if you want the local DNS server to respond to DNS queries from other VPN sites, you must tell dnsmasq to listen on the "wg0" interface. This is done by adding the following directive to dnsmasq's custom configuration:
\\
interface=wg0
\\
\\
==== Running Wireguard at Boot ====
As hinted previously, you must rely on permanent storage to make this work. Regardless of what type of storage you choose, it might become unavailable. For this reason, [[jffs|JFFS]] has been used throughout the examples, as it arguably the most reliable form. To run Wireguard automatically, place this line in the Firewall field in the //Administration///[[admin-scripts|Scripts]] menu:
\\
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 as is in the //Execute after mounted// field on the [[jffs|JFFS]] page, but be advised that it has not yet been tested.
\\
==== Running multiple VPN technologies at once ====
It is generally unwise to mix and match different technologies. There are resource utilization considerations relating to this. However, there can also be secondary //positive// effects to running tinc in the background while Wireguard is running. You could run tinc between all the hosts with a wider network mask, (such as 255.255.0.0) first and then add Wireguard only on specific links (ideally, the high performance ones).
Having a Wireguard client connect from a mobile phone or a site that uses slow DSL could create unwanted overhead. So why use two VPNs at once?
\\ \\ {{:pasted:20230213-091756.png}}\\ \\
Let's assume we have five sites connected via tinc (green). Three of these sites have high speed connectivity where we decide to add Wireguard (blue). We now now find ourselves in the situation illustrated above.
\\ \\ If something were to cause a Wireguard failure (say, storage of the script) and Site A were to be disconnected, it would now be unreachable. This is illustrated in the diagram below, in which red stars represent the broken links:
\\ {{:pasted:20230213-092742.png}}\\ \\
However, what if addressing was done such that:
* A "/16" netmask was used for global tinc. This would result in a single /16 netmask \\ for your whole tinc network in the routing table) and;
* A "/24" netmask was used for each individual Wireguard link.
\\ Under these conditions, the tinc-only devices could greatly increase resilience and redundancy.
If the Wireguard script is up ("wg0" device exists), that could allow you to connect via SSH into a tinc-only device (say, Site E). From there, you could use SSH to connect to Site A again 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 use the built-in ''ip route'' command.
In the same example, with two VPN technologies running at once, you can verify the routing table's elected path, like this:\\
''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
===== Wireguard Notes and Troubleshooting =====
If any problems arise while running the script, you should consider changing the first line from:
#!/bin/sh
\\
to the following:
\\
#!/bin/sh -x
Using the script with the "-x" option causes it to run in trace (debug) mode.
\\
\\
Remember that you can get help with any subcommand by typing:\\
root@routera:/# wg help
\\
\\
Remember that ''wg'' is a shortcut to using ''wg show'' and displays helpful information, as seen below:
\\ {{:pasted:20230213-133121.png}} \\ \\
You can also display more advanced information by using:
root@routera:/# wg show all dump
\\
\\ If there's no issue with the VPN, there might be a routing problem which can be verified using the route subcommand:
root@routera:/# route
\\
Pinging the intra-VPN IP address first will help to identify if Wireguard is the issue or if it is routing-related.\\ \\ Finally, pay attention to the //AllowedIPs// field. There, each intra-vpn router's IP address is defined with a /32 subnet. However, in the wg.sh script, a /24 subnet is used. This is meant.
==== Known Bugs ====
\\
With the current version of the code (v1.0.20200827), if you use the ''wg'' ("wg show") command, it appears as if sent traffic is not counted as it should be.
\\
{{:pasted:20230213-152337.png}}\\ \\
This should be fixed in a later version of the code.
\\
\\