WireGuard client router on Arch Linux


Return to index

This guide assumes that you already set up a WireGuard server, or that you otherwise have access to one, for internet service.

A guide was written prior to this one, showing you how to set up your own WireGuard server, on any VPS provider. See: WireGuard server on Debian Linux.

I happened to have an Arch Linux machine running, that I already used for a few things, so I decided to re-purpose it. It is now my WireGuard client router, on my network. Let’s get started.

Regarding firewall configuration

READ THIS CAREFULLY:

Because we will be using iptables and ip6tables commands, we must configure the firewall only when the tunnel is down on your client router, because of how wg-quick manipulates iptables at runtime.

This is because we will then, in this guide, save the iptables rules using iptables-save and ip6tables-save, and we want to ensure that only those rules not created by wg-quick are saved!

Dependencies

Install these dependencies:

pacman -S resolvconf wireguard-tools

If pacman asks which resolvconf implementation to use, just pick the systemd one. It will do nicely. (if you’re using a non-systemd Arch variant, then please adapt accordingly)

systemd-networkd

We will assume that you have systemd-networkd. If you have something else, such as NetworkManager, then you should adapt accordingly. All of the network management daemons are pretty much the same thing anyway, and they give you roughly the same features.

Firstly, make sure you have two network interfaces. I have two ethernet cards in my machine, with names eno0 and enp2s0.

I used eno0 for LAN, and enp2s0 for my WAN (wireguard client).

Here is the setup, on my machine:

/etc/systemd/network/10-eno0.network

Again, this is my LAN setup:

[Match]
Name=eno0

[Network]
Address=10.69.0.1/24
Address=fd69:69:69::1/64

Note the use of 69 instead of 66 as before; the latter is on the tunnel side.

Now for WAN:

/etc/systemd/network/10-enp2s0.network

[Match]
Name=enp2s0

[Network]
Address=10.42.0.81/24

[Route]
Gateway=10.42.0.1
Destination=IPv4-address-of-your-VPS-goes-here/32

Here, 10.42.0.81 is a LAN address too, but it is to the router that I use for normal internet (no tunnel). You might set it up differently.

Note how we are only specifying a subnet size of /24, but we have not specified a default route.

We have, in this example, specific a static route under the [Route] section. This static route goes through the gateway address 10.42.0.1, giving us routing for our WireGuard server’s IPv4 address, but only that will be routed.

No other IP addresses will be routed. There is no default route.

When the wireGuard tunnel is online, you will then have a default route, giving you internet. This is the assumption behind our setup. Continue reading!

WHY!? - simple. Doing it this way prevents DNS leaks. All traffic is routed through the tunnel by default.

Packet forwarding

I create a file, /etc/sysctl.d/10-pf.conf - with these contents:

net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

You can apply these straight away without reboot, by doing:

sysctl --system

This enables packets to traverse between interfaces. Without it, your router will not work at all.

NOTE:

On non-systemd systems, you might just use the classic /etc/sysctl.conf instead, and run sysctl -p if you specifically want to load sysctl.conf from /etc - some variants of Arch do exist that use other system software e.g. s6, openrc.

This enables packets to traverse interfaces.

You are advised to use .d directories instead, as in the above example. This is the most common paradigm nowadays, with all sorts of Linux software.

Firewall

Make sure that no firewall is currently configured.

Disable/remove other firewall managers (ufw/firewalld) so that they will not conflict with your setup.

We will be using iptables manually.

Make sure that you have iptables and ip6tables commands available when logged in as root.

Note that modern iptables implementations typically just use nftables behind the scenes. I prefer the old iptables syntax, so I will just use that.

Wireguard client config

On your Arch Linux machine, that you will use as a router, the WAN side of it will be a client to a WireGuard server, hosted elsewhere, probably a VPS provider where you set up your own WireGuard server, or perhaps some VPN provider.

We will assume that you set up your own VPN. See: Wireguard router on Debian Linux.

Wireguard client setup is pretty much the same as in that guide. Make sure to add the client public key to your server. Read the other guide, linked above (Wireguard router on Debian Linux), to know how to set up a WireGuard client.

I did that on my Arch Linux WireGuard Client Router and ended up with something like this:

[Interface]
PrivateKey = CLIENT PRIVATE KEY GOES HERE!!!!!!!!!!!!!!!!!!
Address = 10.66.66.4/8,fd66:66:66::4/64
DNS = COMMA SEPARATED LIST OF DNS RESOLVERS HERE!!!!!!!!!!!!!!!!!!!!

[Peer]
PublicKey = SERVER PUBLIC KEY GOES HERE!!!!!!!!!!!
Endpoint = 85.17.146.213:8999
AllowedIPs = 0.0.0.0/0,::/0
PersistentKeepalive = 25

Afterward, you would have a file /etc/wireguard/wg0.conf like in the above example.

Enable WireGuard client

First, ping your WireGuard server IP to make sure that it works.

Ensure that there is no other route. If you didn’t reboot already, or didn’t reset the network, do this:

systemctl restart systemd-networkd

After that, check:

ip route show table all

If you see any default routes at this point, you should fix that. There should only be a static route to your VPS IP address. Ping that to make sure it works.

It is recommended that you use IPv4 for connecting to the VPN server, from your client router, because IPv4 will have a lower MTU overhead than IPv6; you can use IPv6 through the tunnel just fine.

Now enable the wireGuard client:

systemctl enable wg-quick@wg0.service

Don’t start it yet!

Enable packet forwarding and NAT/NAT66

MAKE SURE wireguard isn’t running yet, on the WireGuard client machine.

This will enable NAT and NAT66:

iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
ip6tables -t nat -A POSTROUTING -o wg0 -j MASQUERADE

This will clamp TCP MSS, to mitigate packet fragmentation on TCP connections:

iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1380
ip6tables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360

Wireguard typically has an MTU overhead of 80 bytes on IPv4, leaving you with an MTU of 1420 on Ethernet MTU 1500. This is a rule of thumb, but you can check the MTU size on your tunnel specifically, by running ip a to check the reported MTU size.

MSS size is set without including the TCP header (20 bytes) and IP header (20 bytes for IPv4 and 40 bytes on IPv6).

We combine the TCP header and the IP header, subtracting from MTU, to decide MTU size. In so doing, other hosts will more reliably know to send smaller packets; the alternative is that you would have to deal with packet fragmentation, which can harm performance a lot more. Note that this change only pertains to TCP.

Many applications are designed to accomodate smaller MTUs, because lots of people use tunnel connections; it was even more common back in the day, and lots of people used much smaller MTUs, so applications tend to be quite resilient to this. Otherwise, just let Linux handle fragmentation. It’s what Linux was born to do!

If you want to, you can figure out precisely what your MTU is, by doing ping tests; consult the manual for your ping implementation, which will tell you how to set the packet size, when sending a ping. You test it on higher settings first until you find the lowest MTU that works.

Stateful firewall

Optionally, you may disable incoming unsolicited traffic, by setting up a stateful packet filter. This is pretty basic, and standard for most networks.

I didn’t see the point in writing this section, since there is already an excellent guide on the Arch Linux wiki:

https://wiki.archlinux.org/title/Simple_stateful_firewall

You can adapt this for your needs. Note that some of this was already configured, if you followed this WireGuard guide, e.g. MASQUERADE rule for NAT.

Save firewall setup

When you’re done configuring your firewall, you can check it like so:

iptables-save
ip6tables-save

If the output satisfies you, you should then save it to a file. You can use these commands.

For IPv4:

iptables-save > /etc/iptables/iptables.rules

For IPv6:

ip6tables-save > /etc/iptables/ip6tables.rules

You should make the rules persistent across reboots. You can do this quite simply:

systemctl enable iptables
systemctl enable ip6tables

You might open/forward ports as well, if you wish. Note that you would also have to open and forward them on your WireGuard server, and the previous WireGuard server guide provides some guidance for that.

This does something similar to iptables-persistent like on Debian. It just applies the iptables config at boot time.

Start wireguard!

Like so:

systemctl start wg-quick@wg0.service

This basically just runs:

wg-quick up wg0

If all went well, you should have internet on your client router.

Now connect your clients to the client router.

Your client router has these LAN IP ranges:

Your gateway address for clients, on WAN side, shall be that of the LAN IPs of your WireGuard router, specifically:

So a client behind the router might use e.g. 10.69.0.2 and fd69:69:69::2.

IPv6 ULA GetAddressInfo

As before, in the previous guide, you are using NAT66 which means you have only a ULA, no GUA, assigned on your client machines. This means that DNS functions may resolve IPv4 first, even if v6 is available; it is better to resolve IPv6 first, when available.

To mitigate this, you can use a custom priority for ULA ranges, with a modified /etc/gai.conf like so:

## RFC 6724 Default
label ::1/128        0
label ::/0           1
label ::ffff:0:0/96  4
label 2002::/16      2
label 2001::/32      5
# label fc00::/7      13
# Changed the above line for IPv6-ULA-first workaround.
label fc00::/7       1
label ::/96          3
label fec0::/10     11
label 3ffe::/16     12

precedence ::1/128       50
precedence ::/0          40
precedence ::ffff:0:0/96 35
precedence 2002::/16     30
precedence 2001::/32      5
precedence fc00::/7       3
precedence ::/96          1
precedence fec0::/10      1
precedence 3ffe::/16      1

A bit hacky, but it does work. The above example assumes Linux; adapt according to whatever operating system you use.

DHCP

Pretty pointless if it’s just you on it. You might set up something like isc-dhcp-server or similar.

The ISC one is the old (read: good) one. Simple for most needs. ISC has a new DHCP server now but I never bothered with it, the old one suits me just fine when I need it.

In my case, I don’t bother. At this point, I’m happy and I just use my router. I plug an OpenWRT router into this, for WiFi and such. A bit over-engineered at that point, but consider that most OpenWRT routers are quite under-powered and might not get good speed on WireGuard, which is precisely why I made my own dedicated WireGuard box. I want to reliably have 1Gbps WireGuard tunnels, so I’m using a faster x86_64 machine to do the job (I use Libreboot btw).

Yes. If this sounds easy, it’s because it is. Enjoy!

Markdown file for this page: https://fedfree.org/docs/router/wireguard-client.md

Site map

This HTML page was generated by the Libreboot Static Site Generator.