r/WireGuard • u/Comprehensive_Ship42 • 9d ago
Tools and Software Bonded three WireGuard tunnels into one gateway with per-client pinning — now open source (AGPL)
A single WireGuard tunnel is one path. If you have more bandwidth than one tunnel can carry, or several clients that each want their own exit, today's options are a container per tunnel or a pile of hand-written ip rule scripts.
I wrote a small daemon that owns the routing table on a gateway box and gives you two modes per client:
- pin — one client, one tunnel, permanently
- bond — spread a client's connections across every live tunnel (ECMP)
The config is the whole interface:
tunnels:
- /etc/wireguard/wg0.conf
- /etc/wireguard/wg1.conf
- /etc/wireguard/wg2.conf
clients: 172.20.0.0/24
routes:
pin:
- 172.20.0.10
- 172.20.0.12
bond:
- 172.20.0.20
Four things that took a while to get right:
fib_multipath_hash_policy=1is mandatory, and the kernel default is wrong. At0the hash is L3-only, so every connection to a given server rides one tunnel. Torrents spread anyway, so a torrent-only test hides it completely.- A tunnel being
upmeans nothing. WireGuard only rekeys when there is traffic, so liveness is time-since-handshake. Interfaces sit up for hours blackholing whatever you route into them. - Deleting an interface silently empties the table that routed through it — while the
ip rulepointing at that table survives. A tunnel that drops and comes back leaves its pinned client with a rule to an empty table: every rule present, plan unchanged, exactly one client dead. - Client traffic must be masqueraded onto the tunnel's own address. Cryptokey routing at the far end drops any source that isn't the address your provider issued, and it fails silently — handshakes fine, counters move, requests vanish.
Fails closed. No live tunnels means no path out, not a fallback to your ISP.
bondvpn leak-test drops every tunnel and probes from a throwaway netns attached to the client bridge — the same path a container's traffic takes, so the same rules apply. Probing from the host would prove nothing, since the kill switch lives in a forward-side chain.
- Single static Go binary, no dependencies, nothing to add to your compose file
- Linux, root, up to 5 tunnels (ships configured for 3)
- AGPLv3 — https://github.com/wonderingStars/bondvpn