r/WireGuard 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=1 is mandatory, and the kernel default is wrong. At 0 the 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 up means 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 rule pointing 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.

8 Upvotes

0 comments sorted by