More Advanced Wireguard Configuration

In my previous blog post I demonstrated how to get a basic Wireguard server up and running that would essentially allow you to route your traffic through your LAN which implies the ability to access services on your LAN. Since then, I have managed to get more complex setups working with wireguard and I figured I could write another blog post on the matter. Specifically, there are two different things being demonstrated below. The first is how to selectively route your traffic through two different servers allowing you the speed of an exit-point outside your network while still granting you access to services inside your LAN. The second is how one can use a wireguard VPN to make services available to the outside world securely and without port-forwarding.

Please be adviced that the contents below assume some familiarily with the wireguard configuration, so please make sure to read my previous post and check out the linked articles. Also, a friend of mine has published her own blog post about running the Pihole and Wireguard together. Its really cool, check it out.

Routing Traffic Through Multiple Wireguard Servers

This is a particularly cool one that my friend figured out, and if he ever writes about it, I will link it here (hint hint, Mitch).

Basically, imagine the following scenario: You have some very useful services running on your local network, like, for example, DNS adBlocking with a Pihole (strongly recommended), so you set up a wireguard server like in my previous post, and you get all the advantages of the pihole. But, at the same time, you don't have the best internet connection at home. Comcast service sucks and your upload speed is terrible, which effectively limits your download speed when you are connected remotely. So you want to be able to access LAN services, but you want the speed of available somewhere else in the outside world.

In my case, I have a server on Linode where I run several services that are available everywhere. Its very nice, and from here on it I will be referring to this as my "remote" server. The key thing is that we can install Wireguard on this server as well, configure it pretty much the same way that we did our original wireguard server, and use that server to route all traffic to the outside world from the client. The result is that all traffic destined for my LAN will go through the LAN wireguard server (which includes DNS queries), while all other traffic flows through the remote wireguard server, such as the actual external HTTP calls, etc.

In order to accomplish this, we need to set up the remote server the same way as we did before, except we give it a different IP address. In my situation I decided to use IP addresses between 10.0.0.1 and 10.0.0.9 as server IPs, and IP addresses from 10.0.0.10 and up as client IP addresses. Hopefully I will not have any more than nine wireguard servers, but I suppose we will see. The new remote wireguard server gets the IP address 10.0.0.2 and the rest of the server configuration looks effectively the same:

[Interface]
Address = 10.0.0.2/32
PostUp   = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
ListenPort = 51820
PrivateKey = <<<SERVER PRIVATE KEY>>>

...
# Clients go here

This assumes that the LAN server configuration is the same except that the IP address is 10.0.0.1 and a different private key (of course).

Now the interesting part is on the client-side configuration. We need to configure two servers as peers and set the right allowed IPs for each one:

[Interface]
Address = 10.0.0.10/24
PrivateKey = <<<CLIENT PRIVATE KEY>>>
DNS = 192.168.86.20

[Peer]
PublicKey = <<<LAN SERVER PUBLIC KEY>>>
AllowedIPs = 192.168.86.0/24 # should be customized for your LAN IP Address space
Endpoint = <<<LAN SERVER PUBLIC IP>>>:51820
PersistentKeepalive = 25

[Peer]
PublicKey = <<<REMOTE SERVER PUBLIC KEY>>>
AllowedIPs = 0.0.0.0/0
Endpoint = <<<REMOTE SERVER PUBLIC IP>>>:51820
PersistentKeepalive = 25

...and that's it. Now when you're connected to wireguard all traffic with the IP address of your local network (in my case 192.168.86.*) will be routed through the LAN wireguard server, which includes DNS, and all other traffic will be routed through the remote server. It's so cool!

Making LAN Services Available Externally

Now consider a different scenario. Let's say that you are running a service, such as Seafile which is at its most useful when many people are using it to share files. Furthermore, you may want to keep using it even when you're not connected to Wireguard or on your LAN.

Consider the following (very likely) constraints:

  • Your ISP will not let you serve content on normal ports like 80 and 443
  • You need HTTPS, non-negotiable, but you do not want to constantly hand out your CA to everyone, explain how to set it up, etc - in short you need to use Letsencrypt, but Letsencrypt requires access to ports 80/443 (yes I know there is a way around it, but its a non-trivial amount of work).
  • You want to just give people a normal, easy-to-remember URL, none of this https://eugene.kovalev.systems:1337/stuff garbage.

There are many ways of solving this problem, for one you can proxy from your remote server to a non-standard port and go from there, but I opted to use wireguard, which meant that I didn't actually need to open any ports to my LAN at all. The high-level idea here is that we have a service running on a server within my LAN, we set up wireguard on that server (let's call it the application server from here on in) in the client configuration and have it connect to the remote wireguard server that we configured above. The only allowed IP in the client configuration should be that of the remote wireguard server so that all other traffic flows normally to/from the application server. At the same time on the remote server we run a webserver like Caddy configured with LetsEncrypt (Caddy has that set up by default actually) with a domain name and have it proxy to the client's virtual address and configured port for the application to be served on. By the time we're done, we essentially have a dedicated encrypted tunnel for just this one service. Its accessible to both the outside world and to the LAN, and HTTPS is available to anyone who accepts the Let's Encrypt CA (most modern browsers and operating systems).

So, how do we do this, well we configure wireguard on the application server, like so:

[Interface]
Address = 10.0.0.14/32 # Note that in my case, this was the IP address of my server, it might be different for you
PrivateKey = <<<APPLICATION SERVER PRIVATE KEY>>>
DNS = 192.168.86.20 # LAN Pihole for adBlocking DNS

[Peer]
PublicKey = <<<REMOTE SERVER PUBLIC KEY>>>
AllowedIPs = 10.0.0.2/32 # Only route traffic for the matrix server through wireguard. The rest should be normal
Endpoint = <<<REMOTE SERVER PUBLIC IP>>>:51820
PersistentKeepalive = 25

No special wireguard configuration is required on the remote server other than adding the client, and we already know how to do that.

Now, assuming that we have the desired service running on the application server's port 8000 (it can be any port really), we can alter our webserver configuration like so (I am using Caddy/Seafile as an example, but the same thing should be possible with any combination of webserver/application):

seafile.kovalev.systems {
    log stdout
    errors
    proxy / 10.0.0.14:8000 {
        transparent
    }
    gzip
}

...and that's it! Now your service should be available at your desired domain name, with HTTPS, and all that other fun stuff.