Using WireGuard with OSPF and Bird

I’ve long used OpenVPN’s PtP tunnels to set up star-style network topologies across the WAN, with dynamic routing set up using OSPF/Quagga.

WireGuard is new, allows simpler configuration, and is measurably faster than OpenVPN, so naturally I wanted to switch to it.

However, WireGuard seems to be aimed at smaller, simpler use cases, with its AllowedIPs configuration option being used to set up both static routes, as well as a form of allowlist regarding what traffic is allowed to flow through the tunnel. With this, I would have needed to hard code all subnets across the network in each end’s AllowedIPs, which would have prevented taking advantage of routing protocols to dynamically set up routes.

After a long time of wanting to be able to set up PtP WireGuard tunnels, with traffic allowed to flow freely within the tunnel a la OpenVPN, I finally found the answer.

This answer is not well known, to the point of people on mailing lists describing it as impossible. See this.

Wireguard

For this to work, using wg-quick, we need to utilize both the (uncommon) Table option as well as AllowedIPs.

The following is a simple working example.

Router A (acting as server):

Tunnel IP: 10.8.0.1/24
Also has access to: 10.30.0.0/24

Wireguard Conf (/etc/wireguard/wg2.conf):

[Interface]
Address = 10.8.0.1/24
PrivateKey = RouterA'sPrivKey
ListenPort = 8999
Table = off # This is the crucial part

[Peer]
PublicKey = RouterBsPubKey
AllowedIPs =  0.0.0.0/0

Router B (acting as client).

Tunnel IP: 10.8.0.2/24
Also has access to: 192.168.122.0/24 and 10.0.0.0/24

Wireguard Conf (/etc/wireguard/wg2.conf):

[Interface]
PrivateKey = RouterB'sPrivKey
Address = 10.8.0.2/24
Table = off # This is the crucial part

[Peer]
PublicKey = RouterAsPubKey
AllowedIPs = 0.0.0.0/0
Endpoint = RouterA'sWanIP:8999
PersistentKeepalive = 30 # Needed if B is behind a NAT

To explain: ordinarily when you set a peer’s AllowedIps to 0.0.0.0/0, this causes WireGuard to force all outgoing traffic to go through the tunnel by altering the host’s routing table to replace the default route.

The magic is when you set ‘Table = off’ the host’s routing table is untouched, and any traffic will now be allowed to float between the tunnel, with the potential for the user to set up any custom routes they want outside of WireGuard’s configuration.

After configuring the conf files, immediately enable the tunnels using, on either end (as we chose identical tunnel names) the following. Tested on Ubuntu and Alma/Fedora.

systemctl enable --now wg-quick@wg2

OSPF

Now that we have a tunnel that allows any traffic to flow, just like with OpenVPN, we need custom routes so all nodes connected to these subnets can easily find each other.

OSPF works well; I used Quagga in the past, but wanted to give Bird a try.

The bird conf for each Router is very simple, with only the wireguard interface name and stubnets being changed.

The following is for Router A.


## Boilerplate from distro
log syslog all;
protocol device {
}
protocol direct {
	disabled;		# Disable by default
	ipv4;			# Connect to default IPv4 table
	ipv6;			# ... and to default IPv6 table
}
protocol kernel {
	ipv4 {			# Connect protocol to IPv4 table by channel
	      export all;	# Export to protocol. default is export none
	};
	persist;
}
protocol kernel {
	ipv6 { export all; };
}
protocol static {
	ipv4;			# Again, IPv4 channel with default options
}

## Sauce
protocol ospf MyOSPF {
	## Boilerplate taken from Bird's example docs https://bird.network.cz/?get_doc&v=20&f=bird-6.html#ss6.8
        ipv4 {
                export filter {
                        if source = RTS_BGP then {
                                ospf_metric1 = 100;
                                accept;
                        }
                        reject;
                };
        };
        area 0.0.0.0 {
        	## What matters
		stubnet 10.30.0.0/24;
                interface "wg2" {
                        type ptp; # VPN tunnels should be point-to-point 
                };
        };
}

We define the WireGuard interface as type “ptp” as it is a tunnel.

Any accessed subnets or routes to be advertised, that are not attached to other interfaces defined, need to be configured as stubnets.

I prefer to only configure the WireGuard interface as this way unnecessary OSPF traffic is not sent to the other interfaces.

The Bird conf for Router B is the same as A except for the stubnet params, as both ends have their WireGuard interface named “wg2” for simplicity.

After both wireguard and bird are running, you can see the routes from ospf being created on the client host above:

[root@machina ~]# ip -4 ro 
*snip*
10.30.0.0/24 via 10.8.0.1 dev wg2 proto bird metric 32 
*snip*

[root@machina ~]# birdc show ro
BIRD 2.0.8 ready.
Table master4:
10.30.0.0/24         unicast [MyOSPF 2021-11-21] * I (150/20) [10.8.0.1]
	via 10.8.0.1 on wg2