Original request (via Reddit)
To use EBGP between locations, we need to allow access via 0.0.0.0/0. Is it possible to add interfaces like wg0, 1, and 2 from the GUI?
Interpretation
The requester runs multiple sites and wants to connect them with WireGuard tunnels, with an eBGP daemon (FRR, bird, …) exchanging routes between sites over those tunnels. WireGUI would manage the tunnels; BGP would own the routing. That decomposes into two capabilities we don't have:
- Default-route cryptokey routing on selected peers. For a BGP-learned route to forward through a tunnel, the remote peer's
AllowedIPs must cover any prefix BGP might learn — in practice 0.0.0.0/0 + ::/0. WireGuard then accepts/sends whatever the kernel routes at it, and the BGP daemon decides what actually goes where.
- Multiple managed interfaces (wg0, wg1, wg2 …) from the GUI. WireGuard permits
0.0.0.0/0 in at most one peer per interface (allowed-ips are exclusive), so each site-to-site link needs its own interface. Separating them from the road-warrior interface is also just good hygiene: different listen ports, different keys, different firewall policy.
What blocks this today
Single interface is baked in everywhere. One wg_interface / tunnel-network pair / listen port in Settings, one server keypair in the single configurations row, and ensure_interface / configure_interface / reconcile / stats / firewall all assume it.
0.0.0.0/0 as a routed subnet is accepted but dangerous. A device's "Routed Subnets" (Device.allowed_subnets) would pass parse_subnet_list validation and land in the peer's allowed-ips (services/events.py:_device_allowed_ips) — that part is fine. But events.on_device_created then calls wireguard.add_routes, which would run ip route add 0.0.0.0/0 dev wg0 — installing a default route on the VPN server itself and hijacking the host's upstream traffic. With BGP in the picture, kernel routes must come from the BGP daemon, never from WireGUI.
Proposed design
Phase 1 — BGP-friendly peers (small, unblocks the use case on a second manual interface)
- Per-device flag (e.g.
install_routes, default true), or narrower: never install kernel routes/firewall entries for 0.0.0.0/0 / ::/0 even when present in allowed_subnets. The subnet still goes into the peer's AllowedIPs.
sync_routes must treat such subnets as expected-absent so it doesn't re-add or "orphan-remove" them.
- UI guard rails on the Routed Subnets field: warn when a default route is entered; reject a second
0.0.0.0/0 device on the same interface.
Phase 2 — multi-interface support
- New
interfaces table: name, IPv4/IPv6 tunnel networks, listen port, private key (encrypted), MTU, enabled, role (clients | site-to-site). Alembic migration seeds one row from the current settings/configurations values so existing deployments upgrade untouched.
Device gets an interface_id FK (default: the migrated wg0 row).
ensure_interface / configure_interface / reconcile / stats / masquerade iterate over enabled interfaces; the periodic convergence added in v1.1.0 applies per interface.
- Admin GUI: an Interfaces page with CRUD + status (up/down, addresses, peer count).
Out of scope
- Installing or managing the BGP daemon itself. FRR/bird config stays the operator's job; we only need to not fight it.
Acceptance criteria
- Admin can create
wg1/wg2 from the GUI with their own listen port and tunnel networks; they converge on startup and periodically like wg0 does since v1.1.0.
- A site-to-site device with AllowedIPs
0.0.0.0/0, ::/0 can be created on a dedicated interface; WireGUI does not install a default kernel route; an FRR session over the tunnel exchanges routes and traffic forwards between sites.
- Existing single-interface deployments upgrade with zero config changes.
Open questions
- One listen port per interface means N exposed UDP ports — acceptable, or do we want to document a single-port relay alternative? (Vanilla WG can't share a port across interfaces.)
- Should per-user firewall rule chains apply to
site-to-site role interfaces at all, or is forwarding policy there entirely the operator's?
- Address allocation for point-to-point links: reuse the tunnel-network allocator, or support tiny /30–/31 style transfer networks?
Original request (via Reddit)
Interpretation
The requester runs multiple sites and wants to connect them with WireGuard tunnels, with an eBGP daemon (FRR, bird, …) exchanging routes between sites over those tunnels. WireGUI would manage the tunnels; BGP would own the routing. That decomposes into two capabilities we don't have:
AllowedIPsmust cover any prefix BGP might learn — in practice0.0.0.0/0+::/0. WireGuard then accepts/sends whatever the kernel routes at it, and the BGP daemon decides what actually goes where.0.0.0.0/0in at most one peer per interface (allowed-ips are exclusive), so each site-to-site link needs its own interface. Separating them from the road-warrior interface is also just good hygiene: different listen ports, different keys, different firewall policy.What blocks this today
Single interface is baked in everywhere. One
wg_interface/ tunnel-network pair / listen port inSettings, one server keypair in the singleconfigurationsrow, andensure_interface/configure_interface/reconcile/ stats / firewall all assume it.0.0.0.0/0as a routed subnet is accepted but dangerous. A device's "Routed Subnets" (Device.allowed_subnets) would passparse_subnet_listvalidation and land in the peer's allowed-ips (services/events.py:_device_allowed_ips) — that part is fine. Butevents.on_device_createdthen callswireguard.add_routes, which would runip route add 0.0.0.0/0 dev wg0— installing a default route on the VPN server itself and hijacking the host's upstream traffic. With BGP in the picture, kernel routes must come from the BGP daemon, never from WireGUI.Proposed design
Phase 1 — BGP-friendly peers (small, unblocks the use case on a second manual interface)
install_routes, defaulttrue), or narrower: never install kernel routes/firewall entries for0.0.0.0/0/::/0even when present inallowed_subnets. The subnet still goes into the peer's AllowedIPs.sync_routesmust treat such subnets as expected-absent so it doesn't re-add or "orphan-remove" them.0.0.0.0/0device on the same interface.Phase 2 — multi-interface support
interfacestable: name, IPv4/IPv6 tunnel networks, listen port, private key (encrypted), MTU, enabled, role (clients|site-to-site). Alembic migration seeds one row from the current settings/configurationsvalues so existing deployments upgrade untouched.Devicegets aninterface_idFK (default: the migrated wg0 row).ensure_interface/configure_interface/reconcile/ stats / masquerade iterate over enabled interfaces; the periodic convergence added in v1.1.0 applies per interface.Out of scope
Acceptance criteria
wg1/wg2from the GUI with their own listen port and tunnel networks; they converge on startup and periodically likewg0does since v1.1.0.0.0.0.0/0, ::/0can be created on a dedicated interface; WireGUI does not install a default kernel route; an FRR session over the tunnel exchanges routes and traffic forwards between sites.Open questions
site-to-siterole interfaces at all, or is forwarding policy there entirely the operator's?