Route all traffic through Wireguard interface

I want to route all my traffic through a Wireguard VPN. So far, following the Wireguard page on the wiki, I’ve set up a wg0 interface successully. Running wg confirms the I have a connection, but how do I route all my traffic (except LAN) through it?

2 Likes

Hi, the wiki already mentions that right ? allowedIPs = [ "0.0.0.0/0" ]; in the client.

With allowedIPs = [ "0.0.0.0/0" ]; I’m unable to connect to anything.

Oh right, I think you’ll need to fiddle around with ip-route to replace the default route.
See Routing & Network Namespaces - WireGuard.

I’ll have to look into that. But also, using wg-quick with a suitable config in /etc/wireguard yields the same problem. Is wg-quick not supported on NixOS?

You can also use wg-quick from wireguard-tools. Redirecting the gateway is not supported in our nixos module.

OpenVPN has a funny way of rerouting all traffic:

$ ip route
0.0.0.0/1 via 10.8.0.5 dev tun1 
128.0.0.0/1 via 10.8.0.5 dev tun1
[...]

Using /1 instead of /0 ensure that it takes precedence over the default /0 route.
Be careful to add add a reachable dns in the VPN and that it is the one configured on your machine.

For completeness, here is my full ip route (configured by networkmanager and openvpn)

0.0.0.0/1 via 10.8.0.5 dev tun1 
default via 192.168.1.1 dev wlp2s0 proto dhcp metric 600 
10.8.0.1 via 10.8.0.5 dev tun1 
10.8.0.5 dev tun1 proto kernel scope link src 10.8.0.6 
10.8.1.1 via 10.8.1.9 dev tun0 
10.8.1.9 dev tun0 proto kernel scope link src 10.8.1.10 
10.66.0.0/24 via 10.8.1.9 dev tun0 
my.own.vpn.ip via 192.168.1.1 dev wlp2s0 
128.0.0.0/1 via 10.8.0.5 dev tun1 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
192.168.1.0/24 dev wlp2s0 proto kernel scope link src 192.168.1.2 metric 600 

Wireguard uses routing policies with ip rule, which is less hacky then the openvpn approach as it does not require a route for the VPN traffic via the local gateway.

I’m wondering, is it currently possible to implement the namespace solution? with the current networking modules?
It boils down to having the wg0 interface in the default network namespace, the physical ifs like wlan0 in another network namespace and then the utilities that need access to the real network must be executed like ip netns exec physical dhcpcd eth0.
I never tried but I intend to try soon :slight_smile:

1 Like

That would be awesome. I knew we could do it with containers but I didn’t realize we could do it without them.

I have exactly the same problem as you, guys, it seems that I cannot properly route all traffic.

Some notes:

  • If I try to route only the related IPs, then I can ping the gateway, e.g.
% cat /etc/wireguard/temp.conf
[Interface]
Address = 10.0.0.6/24
PrivateKey = xxx

[Peer]
PublicKey = xxx
AllowedIPs = 10.0.0.0/24
Endpoint = xxx
% sudo wg-quick up temp
[#] ip link add temp type wireguard
[#] wg setconf temp /dev/fd/63
[#] ip address add 10.0.0.6/24 dev temp
[#] ip link set mtu 1420 dev temp
[#] ip link set temp up
% ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=1.06 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.635 ms
% ip route
default via 192.168.1.1 dev enp0s25 proto dhcp src 192.168.1.127 metric 512 
default dev enp0s25 proto static scope link metric 2048 
10.0.0.0/24 dev temp proto kernel scope link src 10.0.0.6
<omit the rest>
  • If I try to route everything:
% cat /etc/wireguard/temp.conf
[Interface]
Address = 10.0.0.6/24
PrivateKey = xxx

[Peer]
PublicKey = xxx
AllowedIPs = 0.0.0.0/0
Endpoint = xxx
% wg-quick up temp
[#] ip link add temp type wireguard
[#] wg setconf temp /dev/fd/63
[#] ip address add 10.0.0.6/24 dev temp
[#] ip link set mtu 1420 dev temp
[#] ip link set temp up
[#] wg set temp fwmark 51820
[#] ip -4 route add 0.0.0.0/0 dev temp table 51820
[#] ip -4 rule add not fwmark 51820 table 51820
[#] ip -4 rule add table main suppress_prefixlength 0
% ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
^C
--- 10.0.0.1 ping statistics ---
7 packets transmitted, 0 received, 100% packet loss, time 181ms
% ip route
default via 192.168.1.1 dev enp0s25 proto dhcp src 192.168.1.127 metric 512 
default dev enp0s25 proto static scope link metric 2048 
10.0.0.0/24 dev temp proto kernel scope link src 10.0.0.6
<omit the rest>
% ip -4 route list table 51820
default dev temp scope link

Not sure if I type the right command, since manually adding the route, it complains as existing:

% sudo ip -4 route add 0.0.0.0/0 dev temp table 51820
RTNETLINK answers: File exists

I’m a bit lost with how I would debug this on my system. A first step would be to have wg-quick to actually work, which isn’t the case here. Does it work for anyone with AllowedIPs = 0.0.0.0/0? Is this a NixOS/nixpkgs bug?

1 Like

It looks like @anderspapitto is working on this :heart_eyes: : Support network-namespace based wireguard vpn setup [feature request] · Issue #52411 · NixOS/nixpkgs · GitHub

1 Like

yeah I have a working setup based on the namespace approach, described here Reflexive Reflection - Wireguard vpn with network namespace on NixOS

5 Likes

I think I am hitting the same problem today, when trying to connect from my NixOS laptop to GitHub - trailofbits/algo: Set up a personal VPN in the cloud VPN on the server.

If I start the wg connection, I can’t connect to anything (pinging 8.8.8.8 does not work) and I don’t see peer connection from the server either. When disconnect wg interface (via network manager), I actually see a handshake on the server (presumably, because something in the ip route configuration changes temporary during shutdown), but then the connection goes down.

Is https://reflexivereflection.com/posts/2018-12-18-wireguard-vpn-with-network-namespace-on-nixos.html still the best way to fix this? I am not an expert in Linux networking, so that post looks rather intimidating to me :slight_smile:

I have the same question.

Does anyone have a solution of both server and client setup that allows the VPN server to route all the network traffic of the client?

You may want to take a look at this POC Graham built a while ago: 🐉🔥💀 (draft) (WIP) wireguard: allow whole-internet VPN configuration (do not merge) 💀🔥🐉 by grahamc · Pull Request #66300 · NixOS/nixpkgs · GitHub

Apart from that, I know of two additional approaches (I experimented with):

  • You could explicitly define a route to your VPN-host in a dhcpcd exit-hook (see dhcpcd-run-hooks(8) and NixOS Search). Doesn’t work with networkd though.
  • As pointed out in @fpletz’s talk about networkd on NixCon 2018 (https://www.youtube.com/watch?v=us7V2NvsQRA) you could put all of your physical interfaces (enp0s*, wlp0s* etc) into a VRF and then you could simply pass 0.0.0.0/0/etc through your VPN interface and <VPN-host IP> through the VRF interface. I’m using this approach for several months now and I’m fairly happy with it.
1 Like

I also had issues with allowedIPs = [ "0.0.0.0/0" ], but was able to solve it with an ip route.

The Problem: including 0.0.0.0/0 as an allowed IP routes all traffic through the Wireguard interface (good!), including the wireguard traffic itself (not good!). As far as I can tell, all network traffic ends up in a loop and never actually leaves the machine.

The Solution: Add a more specific ip route allowing traffic to the VPN via the default gateway.

I controlled the route with the following added to my nix config:

networking.wireguard.interfaces.wg0 = {
  postSetup = "${pkgs.iproute}/bin/ip route add <vpn-public-ip> via <default-gateway>";
  postShutdown = "${pkgs.iproute}/bin/ip route del <vpn-public-ip> via <default-gateway>";
};

where in my case <default-gateway> happened to be 192.168.1.1 and <vpn-public-ip> is the public IP (i.e. not 10.x.x.x) of the Wireguard server. Adding this to the config now properly routes all traffic through the Wireguard connection.

Currently, the wiki page for Wireguard demonstrates a client config with allowedIPs = [ "0.0.0.0/0" ], but as far as I can tell includes nothing to add a route for the VPN itself, at least in the section for the basic Wireguard client. Perhaps I missed something, or maybe I’m misunderstanding the ip routes, but if not, should the wiki be updated to include the client ip route rules?

3 Likes

I’m not smart enough to figure out all of this, can someone help me out?

Here is the configuration for Wireguard on my NixOS server (VPS is Linode if that matters)

{
  ...
  networking.wg-quick.interfaces = {
    # "wg0" is the network interface name. You can name the interface arbitrarily.
    wg0 = {
      # Determines the IP/IPv6 address and subnet of the client's end of the tunnel interface
      address = [ "10.0.0.1/24" "fdc9:281f:04d7:9ee9::1/64" ];
      # The port that Wireguard listens to - recommended that this be changed from default
      listenPort = 51820;
      # Path to the server's private key
      privateKeyFile = "...";

      # This allows the wireguard server to route your traffic to the internet and hence be like a VPN
      postUp = ''
        ${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT
        ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.0.0.1/24 -o eth0 -j MASQUERADE
        ${pkgs.iptables}/bin/ip6tables -A FORWARD -i wg0 -j ACCEPT
        ${pkgs.iptables}/bin/ip6tables -t nat -A POSTROUTING -s fdc9:281f:04d7:9ee9::1/64 -o eth0 -j MASQUERADE
      '';

      # Undo the above
      preDown = ''
        ${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -j ACCEPT
        ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.0.0.1/24 -o eth0 -j MASQUERADE
        ${pkgs.iptables}/bin/ip6tables -D FORWARD -i wg0 -j ACCEPT
        ${pkgs.iptables}/bin/ip6tables -t nat -D POSTROUTING -s fdc9:281f:04d7:9ee9::1/64 -o eth0 -j MASQUERADE
      '';

      peers = [
      ...
      ];
    };
  };
}

…which is basically the one for wg-quick on the wiki, after installing Wireguard on my phone with allowed IPs 0.0.0.0/0 for peers, I started the tunnel, executing # wg show, I can see that my phone is connected, however, after doing that I cannot ping anything, neither the server, nor the gateway of my home router.

I don’t understand what I’m doing wrong, is it possible to make this work like a regular VPN?