(r)engarde
is a network utility specifically designed to create a point-to-point tunnel over multiple network
(typically Internet) connections, ensuring that the tunnel stays up and healthy without a single delay or package loss,
as long as at least one of the connections is working.
rengarde
is a largely a port of engarde to Rust.
Implemented Features:
- Configuration via YAML (
engarde.yml
) - Client (
rengarde-client
) - Server (
rengarde-server
) - Built-In Web Server
- Built-In Web Interface
(r)engarde
relies on the encryption and the de-duplication technology of the underlying WireGuard connection. It takes
every UDP packet that is emitted by WireGuard and sends it through every available connection. So, the first package
that reaches its destination wins, and the others are silently discarded by WireGuard itself. In the same way, every
response packet is sent to all the connected sockets, reaching the origin through all the connections.
It does, it's awesome, and it's one of the things (r)engarde
relies on. WireGuard, however, sends its UDP packets over
the
default system interface for a specific route, usually the one used to access the Internet. If this interface goes down
or loses access to the network, it's up to the operating system to detect it and change the routing table accordingly -
and it doesn't always do it right.
In some way, (r)engarde
is similar to a failover mechanism, but it doesn't switch the connection when a problem
occurs:
this would inevitably lead to a delay in the transmission. Instead, (r)engarde
constantly sends every single packet
through
all the available connections: if one of the links has problems, the packet will still fastly reach its destination
through the other ones, and the user won't even notice it. It's what some commercial solutions call "Redundant Mode"
bonding. Moreover, failover technologies often rely on expensive hardware and hard configurations: (r)engarde
, on the
other
side, is totally open source and really simple to configure.
Absolutely yes. The used bandwidth is the one you would normally use multiplied by the number of the connections you have. But hey, imagine you are transmitting real time audio to a national radio station: would you really prefer that a connection failure causes some moments of silence to the listeners, or would you happily waste your bandwidth to avoid it?
This is the most typical scenario: you have a reliable server on one end, and a client with multiple unstable connections (maybe in mobility).
graph LR
;
Laptop --- Cable;
Laptop --- Starlink;
Laptop --- 5G;
Cable ---|unreliable| Internet
Starlink ---|unreliable| Internet
5G ---|unreliable| Internet
Internet ---|reliable| Server["Server / VPS"]
So, we need to establish a reliable connection between our laptop and the server, but none of the connections we have on the laptop is reliable enough. We can, however, assume that at least one of them will work in every moment.
-
First of all, set up a WireGuard tunnel between the two peers, following the official quick start. You need to specify a static listen port on the server (59301 is assumed in this procedure) and set the correct peer endpoint on the client. The opposite is not necessary: WireGuard will use a random listen port on the client, and the server will memorize it along with the source address when it first receives a packet. At the end, both the laptop and the server should have a WireGuard IP: in this procedure we'll assume that the server has IP 192.168.5.1, and the laptop has 192.168.5.2, but it can be whatever you want. Test the two systems can ping each other on the WireGuard IP before proceeding to step 2.
-
Prepare the
(r)engarde
configuration file: the engarde.yml.sample is well commented and will guide you. For this procedure, we will use the configuration:
client:
listenAddr: "127.0.0.1:59401"
dstAddr: "198.51.100.32:59402"
excludedInterfaces:
- "eth3"
- "wg0"
server:
listenAddr: "0.0.0.0:59402"
dstAddr: "127.0.0.1:59301"
Have a particular look at excludedInterfaces, its usage is well documented in the sample file comments. Don't forget to exclude the WireGuard interface itself, or it can cause a weird loop.
Take the file and copy it to the client and to the server. You can omit the client portion on the server and vice-versa, or leave both: the unuseful portion will just be ignored.
-
Download rengarde-server (see the How do I get it? section). Launch it passing the config file path as the first and only parameter: if nothing is passed, rengarde will look for an
engarde.yml
file in the current directory. -
Follow the same procedure of step 3 for the client, using rengarde-client instead of rengarde-server.
-
Change the WireGuard configuration on the client: set the peer address to the rengarde-client listen address (in this procedure, it will be 127.0.0.1:59401). You don't need to change anything on the server.
Done! rengarde is now overlying your WireGuard tunnel: try to ping the server from the client (on the WireGuard IP address 192.168.5.1) and, while doing so, disconnect an interface or even physically detach it: the traffic will continue to flow normally, without any delay, until at least one interface is working. If there are no interfaces available at a given moment, the traffic will stop, but will be immediately resumed whenever one of them will be available again.
Here we have a totally different scenario: we are in a data center, and two servers are connected with two Ethernet cables that follow different paths for redundancy.
graph LR
;
Server1["Server 1"];
Server2["Server 2"];
Server1_Eth0["eth0\n192.168.1.1"];
Server1_Eth1["eth1\n192.168.2.1"];
Server2_Eth0["eth0\n192.168.1.2"];
Server2_Eth1["eth1\n192.168.2.2"];
Server1 --- Server1_Eth0;
Server1 --- Server1_Eth1;
Server1_Eth0 --- Server2_Eth0;
Server1_Eth1 --- Server2_Eth1;
Server2_Eth0 --- Server2;
Server2_Eth1 --- Server2;
The procedure to follow is identical to the first scenario, but with a small change in the configuration file:
client:
listenAddr: "127.0.0.1:59401"
dstAddr: "192.168.1.2:59402"
dstOverrides:
- ifName: "eth1"
dstAddr: "192.168.2.2:59402"
server:
listenAddr: "0.0.0.0:59402"
dstAddr: "127.0.0.1:59301"
That's it: (r)engarde
client will use 192.168.1.2 as destination, except for eth1 where 192.168.2.2 is used. You will
just
need to use the WireGuard IPs to communicate between the two servers: the packets will always find their way to the
destination.
There are a lot of other situations where (r)engarde
can make the difference: the connection doesn't have to be a
point-to-point one, an entire network can be routed through a tunnel. Virtually, (r)engarde
can work in every
situation
where WireGuard can. For now, if you need to connect different clients to a single server you need to run a different
instance of rengarde-server, with the same dstAddr and different listen ports, and configure each client to talk with
a
different port.
We're looking into the possibility of making (r)engarde
inspect WireGuard handshakes to differentiate the clients, but
it's
still a work in progress.
There is an Angular web interface embedded in both the client and the server. Please have a look to the comments in the example config file for more information about how to enable it.
In the client, it shows the interfaces that are currently sending data and, for each of them, the last time a packet was
received from the server on it. If an interface isn't receiving data from the server, while the other are, it's probably
faulty. If all of them are not receiving data, it's probably because there's no traffic on the tunnel.
You can also exclude an interface on-the-go, but keep in mind that those changes are temporary and they're lost when the
client is restarted. To make them permanent, you need to edit the configuration file.
The server interface is pretty much the same, but instead of the interfaces it shows the addresses it's currently receiving (and sending) data on.
Honestly, we are quite lazy people, and before coding something we always look for an existing solution that would suit our needings. This time, we really couldn't find one. If you know something similar, please, PLEASE open an issue with title "(r)engarde is a bad copy of ...", we'd love to know that!
(r)engarde
can be run without any administrative privileges, but many users reported that rengarde-client doesn't
create any socket on Linux if executed as a non-root user.
To solve that, you need to move the executable file in a folder that is not mounted with the nosuid
option (so, don't
keep it in your home but move it, for example, to /usr/bin). Then, run as
root setcap cap_net_raw+ep /path/to/rengarde-client
.
After doing this, you'll be able to run rengarde-client as a normal user.
Of course! Feel free to open an issue for any necessity ;)
Wow, thanks! I appreciate it, you can drop the original author some Bitcoin at:
39fBEZvKvxf2aZUBWWV1PuoKwCUvk6VWLg
The whole source code is released under the terms of the GNU General Public License (GPL) v2. You can find a copy of the license in the LICENSE.txt file.