(using-the-vpn-as-the-default-gateway)= # Using the VPN as the default gateway WireGuard can be set up to route all traffic through the VPN, and not just specific remote networks. There could be many reasons to do this, but mostly they are related to privacy. Here we will assume a scenario where the local network is considered to be "untrusted", and we want to leak as little information as possible about our behaviour on the Internet. This could apply to the case of an airport, or a coffee shop, a conference, a hotel, or any other public network. ``` public untrusted ┌── wg0 10.90.90.2/24 10.90.90.1/24 network/internet │ VPN network wg0│ xxxxxx ┌──────┴─┐ ┌─┴──┐ xx xxxxx ──────┤ VPN gw │ │ ├─wlan0 xx xx eth0 └────────┘ │ │ xx x │ │ xxx xxx └────┘ xxxxxx Laptop ``` For the best results, we need a system we can reach on the internet and that we control. Most commonly this can be a simple small VM in a public cloud, but a home network also works. Here we will assume it's a brand new system that will be configured from scratch for this very specific purpose. ## Install and configure WireGuard Let's start the configuration by installing WireGuard and generating the keys. On the client, run the following commands: ```bash sudo apt install wireguard umask 077 wg genkey > wg0.key wg pubkey < wg0.key > wg0.pub sudo mv wg0.key wg0.pub /etc/wireguard ``` And on the gateway server: ```bash sudo apt install wireguard umask 077 wg genkey > gateway0.key wg pubkey < gateway0.key > gateway0.pub sudo mv gateway0.key gateway0.pub /etc/wireguard ``` On the client, we will create `/etc/wireguard/wg0.conf`: ``` [Interface] PostUp = wg set %i private-key /etc/wireguard/wg0.key ListenPort = 51000 Address = 10.90.90.1/24 [Peer] PublicKey = Endpoint = AllowedIPs = 0.0.0.0/0 ``` Key points here: - We selected the `10.90.90.1/24` IP address for the WireGuard interface. This can be any private IP address, as long as it doesn't conflict with the network you are on, so double check that. If it needs to be changed, don't forget to also change the IP for the WireGuard interface on the gateway server. - The `AllowedIPs` value is `0.0.0.0/0`, which means "all IPv4 addresses". - We are using `PostUp` to load the private key instead of specifying it directly in the configuration file, so we don't have to set the permissions on the config file to `0600`. The counterpart configuration on the gateway server is `/etc/wireguard/gateway0.conf` with these contents: ``` [Interface] PostUp = wg set %i private-key /etc/wireguard/%i.key Address = 10.90.90.2/24 ListenPort = 51000 [Peer] PublicKey = AllowedIPs = 10.90.90.1/32 ``` Since we don't know where this remote peer will be connecting from, there is no `Endpoint` setting for it, and the expectation is that the peer will be the one initiating the VPN. This finishes the WireGuard configuration on both ends, but there is one extra step we need to take on the gateway server. ## Routing and masquerading The WireGuard configuration that we did so far is enough to send the traffic from the client (in the untrusted network) to the gateway server. But what about from there onward? There are two extra configuration changes we need to make on the gateway server: - Masquerade (or apply source NAT rules) the traffic from `10.90.90.1/24`. - Enable IPv4 forwarding so our gateway server acts as a router. To enable routing, create `/etc/sysctl.d/70-wireguard-routing.conf` with this content: ``` net.ipv4.ip_forward = 1 ``` And run: ```bash sudo sysctl -p /etc/sysctl.d/70-wireguard-routing.conf -w ``` To masquerade the traffic from the VPN, one simple rule is needed: ```bash sudo iptables -t nat -A POSTROUTING -s 10.90.90.0/24 -o eth0 -j MASQUERADE ``` Replace `eth0` with the name of the network interface on the gateway server, if it's different. To have this rule persist across reboots, you can add it to `/etc/rc.local` (create the file if it doesn't exist and make it executable): ``` #!/bin/sh iptables -t nat -A POSTROUTING -s 10.90.90.0/24 -o eth0 -j MASQUERADE ``` This completes the gateway server configuration. ## Testing Let's bring up the WireGuard interfaces on both peers. On the gateway server: ```bash $ sudo wg-quick up gateway0 [#] ip link add gateway0 type wireguard [#] wg setconf gateway0 /dev/fd/63 [#] ip -4 address add 10.90.90.2/24 dev gateway0 [#] ip link set mtu 1378 up dev gateway0 [#] wg set gateway0 private-key /etc/wireguard/gateway0.key ``` And on the client: ```bash $ sudo wg-quick up wg0 [#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip -4 address add 10.90.90.1/24 dev wg0 [#] ip link set mtu 1420 up dev wg0 [#] wg set wg0 fwmark 51820 [#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820 [#] ip -4 rule add not fwmark 51820 table 51820 [#] ip -4 rule add table main suppress_prefixlength 0 [#] sysctl -q net.ipv4.conf.all.src_valid_mark=1 [#] nft -f /dev/fd/63 [#] wg set wg0 private-key /etc/wireguard/wg0.key ``` From the client you should now be able to verify that your traffic reaching out to the internet is going through the gateway server via the WireGuard VPN. For example: ```bash $ mtr -r 1.1.1.1 Start: 2022-09-01T12:42:59+0000 HOST: laptop.lan Loss% Snt Last Avg Best Wrst StDev 1.|-- 10.90.90.2 0.0% 10 184.9 185.5 184.9 186.9 0.6 2.|-- 10.48.128.1 0.0% 10 185.6 185.8 185.2 188.3 0.9 (...) 7.|-- one.one.one.one 0.0% 10 186.2 186.3 185.9 186.6 0.2 ``` Above, hop 1 is the `gateway0` interface on the gateway server, then `10.48.128.1` is the default gateway for that server, then come some in-between hops, and the final hit is the target. If you only look at the output of `ip route`, however, it's not immediately obvious that the WireGuard VPN is the default gateway: ```bash $ ip route default via 192.168.122.1 dev enp1s0 proto dhcp src 192.168.122.160 metric 100 10.90.90.0/24 dev wg0 proto kernel scope link src 10.90.90.1 192.168.122.0/24 dev enp1s0 proto kernel scope link src 192.168.122.160 metric 100 192.168.122.1 dev enp1s0 proto dhcp scope link src 192.168.122.160 metric 100 ``` That's because WireGuard is using `fwmarks` and policy routing. WireGuard cannot simply set the `wg0` interface as the default gateway: that traffic needs to reach the specified endpoint on port `51000/UDP` outside of the VPN tunnel. If you want to dive deeper into how this works, check `ip rule list`, `ip route list table 51820`, and consult the documentation on "Linux Policy Routing". ## DNS leaks The traffic is now being routed through the VPN to the gateway server that you control, and from there onwards, to the Internet at large. The local network you are in cannot see the contents of that traffic, because it's encrypted. But you are still leaking information about the sites you access via DNS. When the laptop got its IP address in the local (untrusted) network it is in, it likely also got a pair of IPs for DNS servers to use. These might be servers from that local network, or other DNS servers from the internet like `1.1.1.1` or `8.8.8.8`. When you access an internet site, a DNS query will be sent to those servers to discover their IP addresses. Sure, that traffic goes over the VPN, but at some point it exits the VPN, and then reaches those servers, which will then know what you are trying to access. There are DNS leak detectors out there, and if you want a quick check you can try out https://dnsleaktest.com. It will tell you which DNS servers your connection is using, and it's up to you if you trust them or not. You might be surprised that even if you are in a conference network, for example, using a default gateway VPN like the one described here, you are still using the DNS servers from the conference infrastructure. In a way, the DNS traffic is leaving your machine encrypted, and then coming back in clear text to the local DNS server. There are two things you can do about this: select a specific DNS server to use for your VPN connection, or install your own DNS server. ### Selecting a DNS server If you can use a DNS server that you trust, or don't mind using, this is probably the easiest solution. Many people would start with the DNS server assigned to the gateway server used for the VPN. This address can be checked by running the following command in a shell on the gateway server: ```bash $ resolvectl status Global Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported resolv.conf mode: stub Link 2 (ens2) Current Scopes: DNS Protocols: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported Current DNS Server: 10.48.0.5 DNS Servers: 10.48.0.5 DNS Domain: openstacklocal Link 5 (gateway0) Current Scopes: none Protocols: -DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported ``` Look for `Current DNS Server`. In the example above, it's `10.48.0.5`. Let's change the WireGuard `wg0` interface config to use that DNS server. Edit `/etc/wireguard/wg0.conf` and add a second `PostUp` line with the `resolvectl` command like below: ``` [Interface] PostUp = wg set %i private-key /etc/wireguard/wg0.key PostUp = resolvectl dns %i 10.48.0.5; resolvectl domain %i \~. ListenPort = 51000 Address = 10.90.90.1/24 [Peer] PublicKey = Endpoint = AllowedIPs = 0.0.0.0/0 ``` You can run that `resolvectl` command by hand if you want to avoid having to restart the WireGuard VPN: ```bash sudo resolvectl dns wg0 10.48.0.5; sudo resolvectl domain wg0 \~. ``` Or just restart the WireGuard interface: ```bash sudo wg-quick down wg0; sudo wg-quick up wg0 ``` And if you check again for DNS leaks, this time you will see that you are only using the DNS server you specified. ### Installing your own DNS server If you don't want to use even the DNS server from the hosting provider where you have your gateway server, another alternative is to install your own DNS server. There are multiple choices out there for this: `bind9` and `unbound` are quite popular, and it is easy to find quick tutorials and instructions on how to do it. Here we will proceed with `bind9`, which is in the Ubuntu *main* repository. On the gateway server, install the `bind9` package: ```bash sudo apt install bind9 ``` And that's it for the server part. On the client, add a `PostUp` line specifying this IP (or change the line we added in the previous section): ``` [Interface] PostUp = wg set %i private-key /etc/wireguard/wg0.key PostUp = resolvectl dns %i 10.90.90.2; resolvectl domain %i \~. ListenPort = 51000 Address = 10.90.90.1/24 [Peer] PublicKey = Endpoint = AllowedIPs = 0.0.0.0/0 ``` And restart the WireGuard interface. Now your VPN client will be using the gateway server as the DNS server.