[PATCH wireguard-tools] wg-quick: linux: improve and document rpfilter logic

n+wireguard at monade.li n+wireguard at monade.li
Thu Oct 13 22:28:17 UTC 2022


From: Naïm Favier <n at monade.li>

Only restore the fwmark from conntrack if it matches the wireguard
mark, so that we don't restore an empty mark. This makes reordering the
PREROUTING rules easier.

Don't check for UDP when restoring the mark. This makes it possible to
use the same mechanism (setting the conntrack mark to the wireguard
mark) for other types of connections that need to be exempt from the
tunnel; for example, forwarded packets (think Internet connection sharing).

Add an example to the man page for this use case.

Document this particularly unobvious bit of logic in the code, so
that future generations won't have to do as much digging around as I did.

Signed-off-by: Naïm Favier <n at monade.li>
---
Tested with both iptables (+ strict rpfilter) and nftables, in a NAT
scenario.

 src/man/wg-quick.8      | 12 ++++++++++--
 src/wg-quick/linux.bash | 22 ++++++++++++++++++----
 2 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/src/man/wg-quick.8 b/src/man/wg-quick.8
index b84eb64..48d96e5 100644
--- a/src/man/wg-quick.8
+++ b/src/man/wg-quick.8
@@ -21,7 +21,7 @@ wg-quick - set up a WireGuard interface simply
 
 .SH DESCRIPTION
 
-This is an extremely simple script for easily bringing up a WireGuard interface,
+This is a simple script for easily bringing up a WireGuard interface,
 suitable for a few common use cases.
 
 Use \fIup\fP to add and set up an interface, and use \fIdown\fP to tear down and remove
@@ -87,7 +87,7 @@ MTU \(em if not specified, the MTU is automatically determined from the endpoint
 or the system default route, which is usually a sane choice. However, to manually specify
 an MTU to override this automatic discovery, this value may be specified explicitly.
 .IP \(bu
-Table \(em Controls the routing table to which routes are added. There are two
+Table \(em controls the routing table to which routes are added. There are two
 special values: `off' disables the creation of routes altogether, and `auto'
 (the default) adds routes to the default table and enables special handling of
 default routes.
@@ -165,6 +165,14 @@ that this continues to allow most DHCP traffic through, since most DHCP clients
 sockets, which bypass Netfilter.) When IPv6 is in use, additional similar lines could be added using
 .BR ip6tables (8).
 
+Another possible use case (Linux only) would be to exempt forwarded traffic from going through the tunnel, so that a machine
+with a 0.0.0.0/0 peer can share its Internet connection (for example, using NAT) in a transparent manner:
+
+    \fBPostUp = iptables -t mangle -I PREROUTING -m addrtype ! --dst-type LOCAL -j CONNMARK --set-mark $(wg show %i fwmark)\fP
+.br
+    \fBPreDown = iptables -t mangle -D PREROUTING -m addrtype ! --dst-type LOCAL -j CONNMARK --set-mark $(wg show %i fwmark)\fP
+.br
+
 Or, perhaps it is desirable to store private keys in encrypted form, such as through use of
 .BR pass (1):
 
diff --git a/src/wg-quick/linux.bash b/src/wg-quick/linux.bash
index 69e5bef..f755c15 100755
--- a/src/wg-quick/linux.bash
+++ b/src/wg-quick/linux.bash
@@ -224,7 +224,7 @@ add_default() {
 	cmd ip $proto rule add table main suppress_prefixlength 0
 	cmd ip $proto route add "$1" dev "$INTERFACE" table $table
 
-	local marker="-m comment --comment \"wg-quick(8) rule for $INTERFACE\"" restore=$'*raw\n' nftable="wg-quick-$INTERFACE" nftcmd 
+	local marker="-m comment --comment \"wg-quick(8) rule for $INTERFACE\"" restore=$'*raw\n' nftable="wg-quick-$INTERFACE" nftcmd
 	printf -v nftcmd '%sadd table %s %s\n' "$nftcmd" "$pf" "$nftable"
 	printf -v nftcmd '%sadd chain %s %s preraw { type filter hook prerouting priority -300; }\n' "$nftcmd" "$pf" "$nftable"
 	printf -v nftcmd '%sadd chain %s %s premangle { type filter hook prerouting priority -150; }\n' "$nftcmd" "$pf" "$nftable"
@@ -234,10 +234,24 @@ add_default() {
 		printf -v restore '%s-I PREROUTING ! -i %s -d %s -m addrtype ! --src-type LOCAL -j DROP %s\n' "$restore" "$INTERFACE" "${BASH_REMATCH[1]}" "$marker"
 		printf -v nftcmd '%sadd rule %s %s preraw iifname != "%s" %s daddr %s fib saddr type != local drop\n' "$nftcmd" "$pf" "$nftable" "$INTERFACE" "$pf" "${BASH_REMATCH[1]}"
 	done < <(ip -o $proto addr show dev "$INTERFACE" 2>/dev/null)
-	printf -v restore '%sCOMMIT\n*mangle\n-I POSTROUTING -m mark --mark %d -p udp -j CONNMARK --save-mark %s\n-I PREROUTING -p udp -j CONNMARK --restore-mark %s\nCOMMIT\n' "$restore" $table "$marker" "$marker"
-	printf -v nftcmd '%sadd rule %s %s postmangle meta l4proto udp mark %d ct mark set mark \n' "$nftcmd" "$pf" "$nftable" $table
-	printf -v nftcmd '%sadd rule %s %s premangle meta l4proto udp meta mark set ct mark \n' "$nftcmd" "$pf" "$nftable"
+	printf -v restore '%sCOMMIT\n' "$restore"
+
+	# When strict reverse path filtering is enabled, we need to make sure that WireGuard UDP packets
+	# arriving on an external interface would be routed through that same interface with their source
+	# and destination swapped. To do this, we save the fwmark of outgoing WireGuard packets in the
+	# connection tracking module and restore it for incoming packets.
+	# As a convenience, we only check for UDP when setting the connection mark, so that other types of
+	# connections may be exempted from the tunnel using the same mechanism.
+	# Then, we enable src_valid_mark so that the restored fwmark is taken into account for the reverse path lookup.
+	# If the rpfilter netfilter module is used instead, it must be invoked with --validmark in the mangle.PREROUTING chain or later.
+	printf -v restore '%s*mangle\n' "$restore"
+	printf -v restore '%s-I POSTROUTING -m mark --mark %d -p udp -j CONNMARK --save-mark %s\n' "$restore" "$table" "$marker"
+	printf -v restore '%s-I PREROUTING -m connmark --mark %d -j CONNMARK --restore-mark %s\n' "$restore" "$table" "$marker"
+	printf -v restore '%sCOMMIT\n' "$restore"
+	printf -v nftcmd '%sadd rule %s %s postmangle meta l4proto udp mark %d ct mark set mark \n' "$nftcmd" "$pf" "$nftable" "$table"
+	printf -v nftcmd '%sadd rule %s %s premangle ct mark %d meta mark set ct mark \n' "$nftcmd" "$pf" "$nftable" "$table"
 	[[ $proto == -4 ]] && cmd sysctl -q net.ipv4.conf.all.src_valid_mark=1
+
 	if type -p nft >/dev/null; then
 		cmd nft -f <(echo -n "$nftcmd")
 	else
-- 
2.37.2



More information about the WireGuard mailing list