Access Your Home Network Remotely with WireGuard

Including a Quick How-to on DDNS

NOTE: If your network is Carrier-Grade NAT (CGNAT) or you’re in an apartment complex with shared internet, this will not work (ask me how I know). If you’re not sure, contact your ISP or property management to verify what type of internet you have. In this case, you will need to purchase a static IP address; otherwise, skip this tutorial and go with a workaround.

Now, if you’re anything like me, you already use paid VPN services, so why should you implement one at home? Let’s say you’re running a media server and you want a secure way to access your movies and shows from afar. Or you have an IP camera with a local-only feed and storage (no subscription plan or 3rd-party servers for me, thank you). Or maybe you’re running a hypervisor on a machine and someone tells you about this cool new OS or tool that you’re eager to try out. And also if you’re like me, your laptop is attached to you, so while you’re on lunch you decide to patch into your home’s network, spool up a VM, and voilà! If that sounds awesome to you (hint: it is), start by updating your repo and packages (assuming you’re on Ubuntu).

sudo apt update && sudo apt upgrade -y

Install WireGuard on Machine 1

sudo apt install wireguard

WireGuard doesn’t operate on a server-client model in the traditional sense; rather, a peer model, where each peer in the network has its own public and private keys. So next we need to generate key-pairs for the machine sitting on our home network (acting as the “server”) and our laptop (acting as the “client”). Heavy emphasis on the quotes. You can set up as many peers as you like, but we’re just going to set up these two. First generate a private key for the home machine:

wg genkey | sudo tee /etc/wireguard/private.key

And then change user permissions for the key to root using the go= command.

sudo chmod go= /etc/wireguard/private.key

Now create a public key using the private key we just generated as an input.

sudo cat /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key

We’re going to be using IPv4 for this installation, so you have three ranges for private IP addresses:

10.0.0.0 - 10.255.255.255 (10/8 prefix) 
172.16.0.0 - 172.31.255.255 (172.16/12 prefix) 
192.168.0.0 - 192.168.255.255 (192.168/16 prefix)

Because many SOHO routers use the 192.168.x.x space, I typically like to pick a 10.0-255 or 172.16-31 space for my WireGuard installation to avoid potential conflicts. NOTE: An additional, not in lieu of, way to avoid conflicts is to change your router’s default network address (typically 192.168.0.0/24 or 192.168.1.0/24) to something a little less common in that space, like 192.168.44.0/24 or 192.168.126.0/24). Without getting into the weeds, the simplest way is to use a /24 network. The /24 is a subnet mask, which is just a way to break up your network into networks and hosts. In your router’s settings, there should be a subnet mask setting, which you should leave at or enter 255.255.255.0 if blank. When picking a network address in this space, leave the first two bytes 192.168; the next byte can be any value between 0 and 255; and the last byte will be zero. 192.168.12.0 or 192.168.197.0 or 192.168.243.0 are all valid examples. Your router should autofill the gateway and broadcast address when you enter the network and the subnet mask, but if it doesn’t, your gateway will be 192.168.x.1 and your broadcast will be 192.168.x.255. The reasoning behind changing the default is that if you attempt to connect from a network with the same (default) address, you might have a conflict that won’t allow you to connect (this has happened to me). Learn from my mistakes!

Configure WireGuard on Machine 1

Ok, now that we’ve dipped a pinky toe into the networking waters, we need to set up the interface WireGuard will be using. It’s typical to name the interface wg0, so let’s open up a configuration for the interface:

sudo nano /etc/wireguard/wg0.conf

The private key will be the value that we generated inside the /etc/wireguard/private.key file. You can use the cat command to copy this value into the PrivateKey directive. For the address, select any numbers between 0 and 255 where the X’s are. You can use 10.87.191.1/32 if you like.

[Interface]
PrivateKey = private_key_value
Address = 10.x.x.1/32
ListenPort = 51820
SaveConfig = true

Port 51820 is the default, but you can change it if you like. WireGuard works over UDP, not TCP, so I will probably cover TCP tunneling WireGuard to bypass networks that block outgoing UDP traffic in a future article. For now, let’s forward IPv4 packets from our WireGuard installation so we can access other services on our network.

sudo nano /etc/sysctl.conf

And uncomment the following line and save the file:

net.ipv4.ip_forward=1

Check that we’ve properly forwarded packets

sudo sysctl -p

And we should see net.ipv4.ip_forward=1 displayed on the screen.

Now we need to configure WireGuard’s firewall rules and set up masquerading. Masquerading lets the inbound traffic on our WireGuard interface to masquerade – it’s in the name! – as the public IPv4 address of our home machine on outbound traffic. Basically, inbound traffic enters our server through the wg0 interface and out the server’s default ethernet interface (typically eth0, eth1, or eno1). That is so freaking cool!

Let’s start by checking our default interface.

ip route list default

You should see an output like the following:

default via 192.168.x.1 dev ens18 proto static

The 192.168.x.1 address assumes you’re using this address space, but if you’re not, don’t worry, the important part is what comes after dev: that is the primary interface the computer is using for internet. As stated previously, it’s typically eth0, eth1 or eno1, but could be a number of things. If you’re unsure, you can check the interfaces with

ip addr show

And you’ll get an output that looks like:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether a7:4c:d6:22:6g:12 brd ff:ff:ff:ff:ff:ff
    altname enp0s18
    inet 192.168.10.12/24 metric 100 brd 192.168.10.255 scope global dynamic ens18
       valid_lft 3066sec preferred_lft 3066sec
    inet6 fe70::a43c:f6ef:ae28:5d53/64 scope link
       valid_lft forever preferred_lft forever

The “lo” interface is for the localhost, or loopback address (what the machine uses to talk to itself); but what we’re interested in is the second one. In my case, the interface’s name is ens18. So that’s the interface we’ll route traffic through. Open back up your wg0.conf file and add the routing and firewall rules:

sudo nano /etc/wireguard/wg0.conf

Add the firewall and routing rules, and be sure to match the interface to your machine.

[Interface]
PrivateKey = server_private.key_value
Address = 10.x.x.1/32
ListenPort = 51820
SaveConfig = true
PostUp = ufw route allow in on wg0 out on ens18
PostUp = iptables -t nat -I POSTROUTING -o ens18 -j MASQUERADE
PreDown = ufw route delete allow in on wg0 out on ens18
PreDown = iptables -t nat -D POSTROUTING -o ens18 -j MASQUERADE

Now let’s open up the firewall to allow traffic through the WireGuard port, as well as for SSH access.

sudo ufw allow 51820/udp
sudo ufw allow OpenSSH

Then enable and reload the firewall.

sudo ufw enable && sudo ufw reload

You can check the status with

sudo ufw status

Configure WireGuard on Machine 2

If your other computer is also running Linux, you’ll need to follow these steps again to install WireGuard and generate a public-private key pair on the 2nd machine. From there you can follow the rest of the guide over at DigitalOcean, but you’ll need to return for configuring DDNS and port forwarding since DigitalOcean doesn’t cover that.

Otherwise, if your daily driver is a MacOS or Windows machine, or if you want to set up a peer on iOS or Android, keep reading. Simply visit the app store and download your operating system’s client application. We’ll simply generate the keys and the configuration on the same machine as our WireGuard server. You can call the peer anything you’d like, but for simplicity we’ll name it peer.

wg genkey | sudo tee /etc/wireguard/peer_priv.key

Change the permissions as before.

sudo chmod go= /etc/wireguard/peer_priv.key

And generate a public key using the peer_priv.key.

sudo cat /etc/wireguard/peer_priv.key | wg pubkey | sudo tee /etc/wireguard/peer_pub.key

Now create a configuration file. Again, you can call it anything, but we’ll name it peer (how original!). Open the file.

sudo nano /etc/wireguard/peer.conf

And enter the following:

[Interface]
Address = 10.x.x.2/32
PrivateKey = peer_priv.key_value
DNS = 1.1.1.1

[Peer]
PublicKey = the_home_machines_public.key_value
Endpoint = your_public_ip_address_or_domain_name:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 21

The address is the peer machine’s VPN address in the 10.x.x.x space we picked. And since we’ve designated the machine at home as .1/32, we’ll be using .2/32 for the peer. Subsequent peers don’t have to be sequential, but it’s easier that way, so next would be .3/32, .4/32, and so on. You don’t have to set a DNS server, but I like to use Cloudflare. You can check the main server’s public key with the cat command; that’s what you’ll enter for the public key under [Peer], not the peer_pub.key. For the endpoint, you can put in your home’s public IP address for now; however, since residential IP addresses change, we’ll use a domain name and a DDNS server so that whenever our IP address changes we can still connect to our home (you can skip the DDNS step if you have a static IP address). Setting AllowedIPs = 0.0.0.0/0 will route all of our outbound traffic on our remote machine through the VPN whenever we’re connected. Send this file to your peer device and import it directly into your WireGuard client application.

Now let’s finish up configuring our WireGuard interface.

sudo nano /etc/wireguard/wg0.conf

Under the routing rules we’ll add the peer we just created.

[Interface]
PrivateKey = server_private.key_value
Address = 10.x.x.1/32
ListenPort = 51820
SaveConfig = true
PostUp = ufw route allow in on wg0 out on ens18
PostUp = iptables -t nat -I POSTROUTING -o ens18 -j MASQUERADE
PreDown = ufw route delete allow in on wg0 out on eth0
PreDown = iptables -t nat -D POSTROUTING -o ens18 -j MASQUERADE

[Peer]
PublicKey = peer_pub.key_value
AllowedIPs = 10.x.x.2/32
Endpoint = 0.0.0.0:0

Lastly, we need to bring up the wg0 interface, then enable, and start the WireGuard service.

sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0.service
sudo systemctl start wg-quick@wg0.service

Check the WireGuard status with

sudo systemctl status wg-quick@wg0.service

And you should see something like the following:

● wg-quick@wg0.service - WireGuard via wg-quick(8) for wg0
     Loaded: loaded (/lib/systemd/system/wg-quick@.service; enabled; vendor preset: enabled)
     Active: active (exited) since Wed 2023-09-20 06:33:41 PDT; 6h ago
       Docs: man:wg-quick(8)
             man:wg(8)
             https://www.wireguard.com/
             https://www.wireguard.com/quickstart/
             https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8
             https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8
    Process: 720 ExecStart=/usr/bin/wg-quick up wg0 (code=exited, status=0/SUCCESS)
   Main PID: 720 (code=exited, status=0/SUCCESS)
        CPU: 58ms

Sep 20 06:33:41 guardian wg-quick[720]: [#] ip -4 route add 10.87.191.2/32 dev wg0
Sep 20 06:33:41 guardian wg-quick[720]: [#] iptables -A FORWARD -i wg0 -j ACCEPT
Sep 20 06:33:41 guardian wg-quick[720]: [#] iptables -t nat -A POSTROUTING -o eno1 -j MASQUERADE
Sep 20 06:33:41 guardian systemd[1]: Finished WireGuard via wg-quick(8) for wg0.

Setting Up Port Forwarding

And you’ve successfully installed and configured WireGuard! But we’re not finished. We have two peers in our virtual private network: the machine sitting at home connected to our router, and our laptop. But there’s no way for traffic to pass through our router from the outside because the default firewall settings for ports on our home router are typically closed…for very good reason. So we need to forward ports from our router to the machine where we have WireGuard installed. But don’t worry. It’s safe. Because we’ve configured firewall rules and routing to pass unsolicited traffic through our WireGuard interface, only devices with the encryption keys will be granted access.

Here are the general steps to set up port forwarding (varies by router vendor):

  1. Access your router’s web user interface
  2. Locate port forwarding (typically in Advanced, WAN, or Security settings)
  3. Select the device for which to forward ports. This will be the local IP address of the machine where WireGuard is installed.
  4. For protocol, select UDP. If you do not select UDP, port forwarding will not work with WireGuard.
  5. If you have a starting and ending port, enter 51820 for both; otherwise, enter 51820.

And that’s it! You should have already imported your peer configuration to your 2nd machine’s WireGuard application, but if you haven’t, go ahead and do so. Simply select “Activate” and you should start to see inbound and outbound traffic. Congratulations! If you want to double-check that you truly are connected to your home’s network, try connecting through your smartphone’s hotspot first, then connect and check your IP address. It should be the same as your home’s! If you have any other locally available services/devices on your home network, like a NAS, Samba share, network printer, or other machines configured for SSH or S/FTP, you can also connect to those as if you were on the network. Awesome.

Setting UP DDNS (Optional)

The pesky thing about residential IP addresses is that they change often, so if we’re trying to connect to our home’s public IP address and it changes, there’s really no way for us to determine our public IP and connect. So we’re going to anchor it to a domain name and set up a dynamic domain name system server that will update our domain’s IP address whenever it changes. This way, we only ever have to connect to the domain name. Pretty cool!

First you’ll need a domain name. This can be a .com, .net, .org, etc. I’ve purchased mine from NameCheap, but you can purchase them from any reputable registrar. Next you’ll need to set up an A (Address) record. This record will update the domain name system servers with the domain you purchased and the IP address you associated it with. The A record should have or similar: Type, Host, Value, and TTL parameters. A typical A record looks like the following:

 [Type]               [Host]                  [Value]                   [TTL]
A Record                @                   52.33.130.69              Automatic

Setting up DDNS will be specific to your registrar. Since I’m using Namecheap, that’s what we’ll use in this tutorial, but the process should generally look the same. In my case, Namecheap requires a formatted query with a password token in order to update the A record address.

https://dynamicdns.park-your-domain.com/update?host=[host]&domain=[domain_name]&password=[ddns_password]&ip=[your_ip]

But how will we make those queries? With a DDNS server, of course! Many SOHO routers provide this functionality built in, so jump into the your router’s web user interface and look for the DDNS settings. You should see input boxes for the host, domain name, password, and IP address, or some combination of those. If instead you’re presented with an empty text box, enter the query format required by your registrar, and you’re ready to go! In your 2nd machine’s peer configuration, instead of your home’s (current) public IP address, simply change it to:

Endpoint = your_new_domain_name.com:51820

Now connect!

Additional Configuration (Optional)

“I don’t have a DDNS server setting on my router. What do I do?” This will take some additional configuration on your part, which is outside the scope of this article, but I’d recommend starting with lightweight DNS software packages such as DnsMasq or MaraDNS. Otherwise, you can check your router’s compatibility with DD-WRT in order to update its firmware. Good luck!