Hawser

A private network between your own machines. Each machine gets a fixed address, and every machine knows how to find the others by address and by name, wherever they are.

One machine with a public address is the coordinator. It hands out the addresses and tells each machine where the rest are. Every other machine runs an agent.

Hawser does not carry your traffic itself. WireGuard, in the Linux kernel, does that: it is fast, and it is the encryption. Hawser is the part WireGuard leaves to you: who is in the network, which address each machine has, and how they reach each other. There is no account and no third party's server.

What you need

Install from a download

The releases page has, for each release, a package and a tarball for x86-64, arm64 and armv7 (Raspberry Pi). Nothing else is needed: the binary is static.

FileForInstall with
hawser_<version>_<arch>.debDebian, Ubuntu, Raspberry Pi OSsudo apt install ./hawser_*.deb
hawser-<version>-1.<arch>.rpmFedora, RHEL, openSUSEsudo dnf install ./hawser-*.rpm
hawser_<version>_<arch>.apkAlpinesudo apk add --allow-untrusted ./hawser_*.apk
hawser-v<version>-linux-<arch>.tar.gzAny other distributionunpack, then sudo ./install.sh ./hawser

A package puts hawser in /usr/bin with its service; the tarball's install.sh puts it in /usr/local/bin. Either way nothing is set up and nothing is started: carry on at "Create a network".

Build

docker build --target binary --output dist .

writes dist/hawser, one static file with no dependencies. The same file runs on any Linux distribution. The build runs the unit tests and fails if one does.

Without Docker, on a machine with a C++20 compiler and CMake:

cmake -S . -B build && cmake --build build -j && ./build/hawser_tests

Install from a checkout

On each machine, as root, from a checkout of this repository:

scripts/install.sh dist/hawser

It puts the binary in /usr/local/bin and adds a hawser service. It sets up nothing and starts nothing.

Create a network

On the coordinator:

hawser init --network-name home --network 10.77.0.0/24 \
            --name vps --endpoint vpn.example.com:51820
systemctl enable --now hawser
OptionMeaning
--network-nameA short name for the network. Machines are also reachable as <machine>.<network-name>
--networkThe range addresses are handed out from. Pick one nothing else on your machines uses
--nameThis machine's name in the network
--endpointThe host:port other machines dial to reach this one. The UDP port must be open from outside
--addressThis machine's address. Default: the first in the range
--listen-portThe UDP port to listen on. Default: the endpoint's port
--api-portThe TCP port agents call, inside the network only. Default: 51821
--mtuThe largest packet the network carries. Default: 1420, which fits a standard 1500-byte path
--interfaceThe interface's name. Default: hawser0

Every required option must be given. Nothing is guessed.

Add a machine

On the coordinator:

hawser invite workstation

prints one line. On the new machine:

hawser join <that line>
systemctl enable --now hawser

The machine now has its address and can reach every other machine.

The invite carries a one-time key, so send it the way you would send a password. It works once, and for 24 hours unless --valid-hours says otherwise. While joining, the machine makes a key of its own and hands the public half to the coordinator; from then on the invite's key is refused. --address on invite picks the machine's address instead of taking the next free one.

Day to day

CommandWhereWhat it does
hawser statusany machineLists the machines, their addresses, and how each is reached. --json prints the same as one line of JSON, for a program
hawser invite <name>coordinatorPrints the line that lets a new machine join
hawser remove <name>coordinatorCuts a machine off at once; the others forget it within seconds
hawser upany machineRuns the network. This is what the service runs
hawser downany machineRemoves the interface and hawser's lines in the hosts file

hawser status on an agent:

Network home, 10.77.0.0/24. This machine: workstation, 10.77.0.2.

NAME         ADDRESS    PATH                  LAST HEARD
vps          10.77.0.1  direct (coordinator)  12s ago
workstation  10.77.0.2  this machine          -
laptop       10.77.0.3  direct                40s ago
build-box    10.77.0.4  through vps           -

How machines find each other

By address. The coordinator gives each machine one address for good.

By name. Each machine keeps a block in /etc/hosts listing every machine, so ssh laptop and ping laptop.home work everywhere. Only that block is ever touched. Set manages_hosts=no in /etc/hawser/hawser.conf to leave the file alone.

By the best path available. Every machine calls the coordinator, so the coordinator sees the public address and port each one is calling from, and passes those on. Then, for each pair of machines:

  1. Both sides start trying to reach each other directly, in the background. Meanwhile their traffic goes through the coordinator, so the pair can talk from the first second.
  2. Once the direct path has actually carried something, traffic moves to it.
  3. If the direct path goes quiet for 75 seconds, traffic moves back to the coordinator, and the background attempts carry on.

Traffic is never sent down a path that has not been shown to work. Two machines that cannot reach each other at all, for example each behind its own strict NAT, simply stay on the coordinator.

If the coordinator stops, machines connected directly keep talking. Machines that relied on it wait until it is back. Restarting the hawser service does not cut anything: the interface stays up.

Source and releases

Download Source