Site Tools


wireguard_on_freshtomato

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:


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 Quick Start guide, and maybe the 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 <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.



In general, typing any subcommand followed by “help” offers additional help for the command.

For example:

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 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:


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




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




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.




    The script will display an introduction screen:





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 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/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 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?





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:




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:




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.




This should be fixed in a later version of the code.



wireguard_on_freshtomato.txt · Last modified: 2024/11/27 21:54 by hogwild