Wireguard finegrain routing for remote NAT with iproute2

You’ll learn more about networking the wireguard, iproute2, and iptables linux packages through my routing set up in this post. Click here if you just want to skip to the full set up.

What I want to setup

The WireGuard Server is helping connecting my macbook (Clients) to the remote servers in LAN1 (192.168.0.0/24) through its own NAT Server. The setup should not interfere with the Wireguard Server’s ability to reach the server within its own local network LAN2 (192.168.0.0/24), which has the same IP subnet as the remote LAN1.

Expected routing behavior

1. A packet from Client wanting to reach a computer in LAN1 should:

2. On the other hand, a packet from WireGuard server wanting to reach the computer in LAN2 should still be routed through its own LAN2 interface wlp92 (192.168.0.1).

Why this is not working out-of-the-box

The AllowedIPs parameter of the wireguard configuration permit the packets of destination IP in the allowed list can be packaged and forwarded to the corresponding peer’s wireguard interface.

Problem: Routing conflict at the WireGuard Server

At the WireGuard server’s config, we have:

/etc/wireguard/wg0.conf
[Interface]
Address = 192.168.2.1/24
PrivateKey = ---

# Macbook Client
[Peer]
PublicKey = ---
AllowedIPs = 192.168.2.4/32

# NAT Server (LAN1)
[Peer]
PublicKey = ---
AllowedIPs = 192.168.2.7/32, 192.168.0.0/24

Notice the additional AllowedIPs item 192.168.0.0/24, which permits packets with source and destination IP in such subnet to be packaged and forwarded from and to the NAT Server peer through the wireguard tunnel. And by default, wireguard populate the main route table, which overriding the routing to wlp92 with wg0 instead:

$ ip route
default via 10.0.0.1 dev eno1 proto dhcp metric 20600
                        ...
192.168.0.0/24 dev wg0 scope link
192.168.0.0/24 dev wlp92 proto kernel scope link src 192.168.0.108 metric 600
192.168.2.0/24 dev wg0 proto kernel scope link src 192.168.2.1

More specifically, we have two conflicting routing entries:

192.168.0.0/24 dev wg0 scope link

which routes all packets, regardless of originated from Macbook Client or WireGuard Server, to LAN1, as in Figure 2, and:

192.168.0.0/24 dev wlp92s0 proto kernel scope link src 192.168.0.108 metric 600

which in turns routes all packets to LAN2, as in Figure 3.

Fixing the problem with iproute2

Solution: Finegrain routing policy

The iproute2 package provides feature to distinguish between these two types of packets, and route them differently, it’s called policy routing. In simple term, this functionality will allow the WireGuard Server to recognize traffic specifically coming from the Macbook Client through the wireguard tunnel, and route it differently from the default traffic.

Roughly speaking, here are the steps to achieve this:

  1. Disable wireguard’s default route handling.
  2. (Optional) Add a route table entry rt_wg0 in /etc/iproute2/rt_tables to route the Client’s packets.
  3. Add a policy to bind packets with Client’s source IP with the rt_wg0 table using ip rule.
  4. Add the routing entries to rt_wg0 that routes packets to 192.168.0.0/24 through wg0.

Step 1: Disable wireguard default route handling

This option is provided by parameter Table in the configuration file:

/etc/wireguard/wg0.conf
[Interface]
Address = 192.168.2.1/24
ListenPort = ---
PrivateKey = ---
Table = off

Step 2: Add routable entry

Add 100 rt_wg0 to the file as follows:

/etc/iproute2/rt_tables
#
# reserved values
#
255	local
254	main
253	default
0	unspec
#
# local
#
#1	inr.ruhep
100 rt_wg0

As said, this step is optional in that it only assigns a name (rt_wg0) to the table number (100), however, it’s still recommended as it would make your configurations much easier to read.

Step 3: Add a routing policy with ip rule

The configuration goes as follows:

ip rule add from 192.168.2.4 table rt_wg0

Which adds an entry to the policy database:

$ ip rule show
0:	from all lookup local
32765:	from 192.168.2.4 lookup rt_wg0
32766:	from all lookup main
32767:	from all lookup default

This basically says to the WireGuard Server that, if a packet comes from 192.168.2.4 (Macbook Client’s address), then check the rt_wg0 table for routing instruction before main.

Step 4: Add routing instructions to table rt_wg0

As the packets sent from the Macbook Client matched with the policy defined in Step 3. We create a routing entry in table rt_wg0 to forward these packets through the wg0 tunnel.

ip route add 192.168.0.0/24 dev wg0 table rt_wg0

The routing entry can be observed with the show command as follows:

$ ip route show table rt_wg0
            ...
192.168.0.0/24 dev wg0 scope link

Full step-by-step configuration guide

To be continued ..