Em's Site

Access your home network using WireGuard

Setting up a secure WireGuard VPN to access your home network

About two years ago, I published this blog about using Algo to access a local network. It remains my most visited post (not that it's wildly popular), but I actually stopped using it very shortly after and switched to this new method.

There were a couple of reasons. First, Algo is meant for a server that is only used as a VPN. It configures iptables rules and changes other settings that can conflict with additional services on the same server. Second, having the WireGuard server on your home network was problematic for people who didn't have a public IP address or had a router that they couldn't configure.

So I ditched Algo and rethought the best way to get to my home network from wherever I am. I've been using this for the past year, and it's not perfect, but it is easier to set up and doesn't rely on port forwarding or having a public IP address at your house.

Overview

As a general overview, two servers make up the routing and forwarding part of the network. The public server is a VPS. I use the $5/month server from Linode (here's an affiliate link to use); other server providers are available. The gateway client is a Raspberry Pi, but you can use any Linux server that supports WireGuard.

Setting up the server

We'll configure the server first. Note that I'm assuming you're using systemd (sorry Gentoo and Alpine users). If you're using Ubuntu, install wireguard and wireguard-tools. Other distros might have different names; make sure you install the package that has wg-quick in it.

Once you install it, find or create a folder to put the configuration files. I use /etc/wireguard/ since that's where the systemd target looks for the main conf file. In that folder, generate the private and public keys for the server and each client you'll connect by doing wg genkey | tee client.key | wg pubkey > client.pub, replacing client with the computer's name you're creating the key for. As an example, I created these keys:

wg genkey | tee server.key | wg pubkey > server.pub
wg genkey | tee gateway.key | wg pubkey > gateway.pub
wg genkey | tee iphone.key | wg pubkey > iphone.pub
wg genkey | tee macbook.key | wg pubkey > macbook.pub

I'll be referencing the names of these files throughout, so I would suggest naming the server and gateway like I did. Once those are done, you'll need to create a configuration file. The files are different for the server and client; we'll start with the server configuration file. Conventionally, this is called wg0.conf. Once we start it with systemd, wg0 will be the name of the network interface. Here's my (redacted) wg0.conf with comments:

# Server
[Interface]
# Put the contents of server.key here
PrivateKey = [redacted]
# Create the IP address in a subnet different from your home network subnet
# this should be a good value for most people. If you want a larger or smaller subnet, change /24 accordingly.
Address = 10.200.0.1/24
# Port that the server listens on. You can change it; make sure to change it in each client config once we make them
ListenPort = 51820

# Gateway
[Peer]
# Put the contents of gateway.pub here
PublicKey = [redacted]
# AllowedIPs in the server config lists the IPs that the server routes to the client.
# The first address is the address that the client will have.
# since this is our gateway to our home network, we want to route the home network subnet to it.
# Replace 192.168.0.0/24 with your actual home network subnet
AllowedIPs = 10.200.0.2/32,192.168.0.0/24
PersistentKeepalive = 25

# mac
[Peer]
# Put the contents of mac.pub here
PublicKey = [redacted]
# Since this is a normal client, we only need to add the IP address that it will use.
AllowedIPs = 10.200.0.3/32
PersistentKeepalive = 25

## Copy/paste the mac section for each client, replacing the PublicKey and incrementing the value of AllowedIPs each time.

Once you have all the clients' [Peer] sections added, save the wg0.conf file, and run systemctl start wg-quick@wg0.service (you'll need sudo if you aren't root). Then run ip a or the equivalent for your distro, and you should see a wg0 interface with the IP address you put in the config. Here's my output:

5: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 10.200.0.1/24 scope global wg0
       valid_lft forever preferred_lft forever

Next, make sure that the server allows IP forwarding. Open /etc/sysctl.conf and find the line that has net.ipv4.ip_forward. Uncomment it and set it to 1. Then run sysctl -w net.ipv4.ip_forward=1 to make it take effect without rebooting.

Finally, add an iptables rule to forward the traffic to the gateway: iptables -t nat -A POSTROUTING -s 10.200.0.0/24 -o eth0 -j MASQUERADE. eth0 is the name of the main interface. Replace it if yours is different, and replace the subnet if you changed it from my example conf.

There's not a way to test it until we set up the gateway client. So let's do that.

Setting up the gateway client

Install wireguard and wireguard-tools like on the server. Then create the configuration file at /etc/wireguard/wg0.conf. Here's my file:

[Interface]
# Put the first address that is in the AllowedIPs value on the gateway section of the server conf file
Address = 10.200.0.2/32
# Put the contents of gateway.key here
PrivateKey = [redacted]

[Peer]
# Replace 1.1.1.1:51820 with the IP and port of your server
Endpoint = 1.1.1.1:51820
# Put the contents of server.pub here
PublicKey = [redacted]
# This should be the same subnet as the Address of the server configuration file
AllowedIPs = 10.200.0.0/24
PersistentKeepalive = 25

Save it, and then run systemctl start wg-quick@wg0.service. Then run ip a to check, you should see a wg0 interface with the correct address, for example:

8: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 10.200.0.2/32 scope global wg0
       valid_lft forever preferred_lft forever

Change the net.ipv4.ip_forward value the same way you did on the server, and then add the iptables rule, which is slightly different: iptables -t nat -A POSTROUTING -s 10.200.0.0/24 -j MASQUERADE.

We can now test it. You should be able to ping the server IP from the client and vice versa, and get a response. If you don't get a response, check /var/log/syslog and /var/log/kern.log for errors, and make sure that you copy/pasted the private and public keys correctly.

Now on the server, ping an IP address on your local network (not the gateway client IP). You should get a response from that same IP address. If you didn't, make sure that the subnet and network interface name of the iptables rules are correct, and that you ran sysctl -w net.ipv4.ip_forward=1 on both the server and the client.

Once you've successfully got those two set up, you can make the configuration files for the other clients you'll be connecting with. I save each file in the same directory that I created the public/private keys for each. Here's a sample file for my Macbook:

[Interface]
# Name = mac
# The IP from the AllowedIP of the mac section in the server configuration file
Address = 10.200.0.3/32
# Put the contents of mac.key here
PrivateKey = [redacted]

[Peer]
# IP and port of the server
Endpoint = 1.1.1.1:51820
# Put the contents of server.pub here
PublicKey = [redacted]
# The first value is the same as the AllowedIPs value of the mac section of the server config
# The second value is the subnet of your home network.
AllowedIPs = 10.200.0.0/24,192.168.0.0/24
PersistentKeepalive = 25

Now, this configuration will only send traffic to IP addresses in those subnets through WireGuard. This is useful if, for instance, you're at work and need to access internal servers. If you want all your traffic going through WireGuard, like when you're on public wifi, change AllowedIPs to 0.0.0.0/0. You can import multiple configuration files, so I have one with only the home subnets, and the other with all traffic, and switch between them depending on where I am.

Connecting the other clients

If you're using a laptop/desktop, just copy/paste the configuration file to that computer and import it into the WireGuard client. For phones, you can import via QR code. Install the qrencode package (other distros might have a different name), and then run qrencode -t ansiutf8 < /etc/wireguard/iphone.conf, changing iphone.conf to the name of the config for that client. Then you can scan it with your phone, and it will import it.

Drawbacks

As I said in the intro, this isn't perfect. There are two downsides to this setup. The first is that you need to remember to not have the WireGuard client running on your phone/tablet/laptop when you're at home; otherwise all the traffic will be going through the server and back and will be slower than normal. Second, if you're at work and your home and work subnets overlap, then you won't be able to access both of them. The easiest fix is to change your home network's subnet (and update the configuration files accordingly).

Extra step: DNS

Because you're able to access your home network from wherever, if you have a network-level adblocking setup, like AdGuard Home or Pihole, you can force WireGuard to use that as the DNS server. In each client config, underneath the Address line, add this line:

DNS = 192.168.1.1

Change the IP address to the IP of your DNS server. Now you have the benefits of network-level adblocking even when you're not at home!