Back in December 2023, I moved residences, which meant changing internet providers. My old ISP was your dream ISP, just an ONT box that you can plug your router right into and have unrestricted port forwarding rights. Unfortunately, my new ISP is still using coax and you need to use their locked down modem. I have a Synology at home that runs Active Backup for Business. I have various servers in the cloud that connect to port 5510 on my home network and backup that way. Unfortunately, with no way to open port 5510 on my home network anymore, I needed a new solution. Your first solution might be: use Tailscale, duh. And I did! While Tailscale was dead simple to setup and I had a connection between my Synology and servers in the cloud in less than ten minutes, the speeds between the two were atrociously slow. It would only transfer 2MB/s maximum. This was because it was going through their relay servers. It took nearly a week to do a full backup and was unreliable and would fail. So, I needed a new solution (which is the one I will be describing how to setup). What if we could have my home network establish a connection the cloud servers instead? As with any cloud provider that gives you dedicated servers, port forwarding any port is essentially a given. So, that is what I ended up achieving with WireGuard. The challenging thing is that there are many moving parts and you need to keep track of them all.
I had a Raspberry Pi 4B running Debian 12 that I would use to connect to the servers in the cloud, also using Debian 12. To simplify things, anything that has a green background in the code blocks will pertain to the Raspberry Pi. Code blocks with a blue background will be commands or files for the cloud server.
The first step is you need to install WireGuard:
sudo apt install wireguard
Next, you need to generate a public key and private key for the Raspberry Pi. The private key belongs only to the Raspberry Pi. The public key will be used on all of your cloud servers, so make sure you keep track of them. These commands should generate a public and private key for you.
wg genkey | tee privatekey | wg pubkey > publickey
Let’s start building our wg0.conf
file on the Raspberry Pi. Save this into /etc/wireguard/wg0.conf
:
[Interface]
Address = 10.0.0.1/24
ListenPort = 49256
PrivateKey = oO/YauPyEcQf2HXaGB1WFc89W5xOklJZU9GVBHZPumk=
PostUp = iptables -t nat -A POSTROUTING -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -j MASQUERADE
Now, we have a base for the Raspberry Pi. The ListenPort
is the port we are going to port forward on the cloud servers. This is dependent on whatever firewall you’re using, but it’s pretty simple, you just need to open port 49256
, however you do so.
Next, we’ll build our base for the cloud server now. You’ll need to make sure you have WireGuard installed on it. Run the command above to generate another public and private key. This one is for the cloud server:
sudo apt install wireguard
wg genkey | tee privatekey | wg pubkey > publickey
Here’s what our file will look like. Save it into /etc/wireguard/wg0.conf
, just like you did on the Raspberry Pi. Let’s break it down.
[Interface]
Address = 10.0.0.2/24
ListenPort = 49256
PrivateKey = +PWm1KbzVfDT9dxMM+dxpF3jJr9W2Eu6NRmfsMlWAHw=
The Address
is separate for each cloud server you add. I set it to 10.0.0.2
in this case. Note that for each separate server in the cloud you have, you’ll want to change this to something different. I could go on to 10.0.0.3
onward. The ListenPort
is 49256
, same as the Raspberry Pi. The private key is the one we just generated on the cloud server, not the one from the Raspberry Pi.
Believe it or not, the only thing left to do is add our clients on both ends. Let’s go back to the Raspberry Pi to add the client.
[Peer]
Endpoint = <public ip>:49256
PersistentKeepalive = 25
PublicKey = wNzZYikgOiYG6+t7T4TlfDMVRHBZMJEyZrLMd0jg6i0=
AllowedIPs = 10.0.0.2/32
The Endpoint
will be the cloud’s public IPv4 address and the ListenPort
of 49256
. The PublicKey will be the public key of the cloud server. Yes, the public key generated on the cloud server will go into the PublicKey field of the Raspberry Pi.
Let’s add the client on the cloud server now.
[Peer]
AllowedIPs = 10.0.0.1/32, 192.168.1.50/32
PublicKey = Kd0RnohXapDi5/HtAKZukMn+hRd7nqr77qpjAZBRtB8=
The public key is going to be the public key you generated on the Raspberry Pi. The AllowedIPs is going to have 10.0.0.1/32
as that is the address for the wg0
interface on the Pi. The 192.168.1.50/32
is the IP address of the Synology NAS. These IPs can be expanded to whatever you want. However, any IP you put in AllowedIPs
will be routed through the Raspberry Pi. You can even put public IPv4 addresses in as well. A genuine use case of this is that one of my cloud servers couldn’t connect to GitHub at all. This was breaking for me and disrupted services. I simply added all of GitHub’s public IPs to AllowedIPs
and that was a temporary fix to route all of outgoing traffic to GitHub from my cloud server through the Pi. This is how it looked with that temporary fix:
AllowedIPs = 10.0.0.1/32, 192.168.1.50/32, 140.82.112.0/20, 143.55.64.0/20, 192.30.252.0/22, 2606:50c0::/32, 2620:112:3000::/44
Detour aside, we are almost done. On the Raspberry Pi, you’ll need to change a configuration setting to allow packet forwarding. In /etc/sysctl.conf
, uncomment the line net.ipv4.ip_forward=1
. Next, run the following command on both the Raspberry Pi and cloud server to initialize the wg0.conf
file you made on both servers.
wg-quick up wg0
You can test you have a connection with ping
.
On the cloud server:
ping 10.0.0.1
On the Raspberry Pi:
ping 10.0.0.2
Next, we’ll do one additional, but optional and recommended, step: generating a preshared key. On either the cloud server or Raspberry Pi, run the following command:
wg genpsk
This will go under the PublicKey
for both configurations on the cloud server and the Raspberry Pi as follows:
PresharedKey = IOVMlzSIP4K1BlMkf8Gtxb7dcvA2lNLgZ3yaQq5RFI4=
You can run the following to reload the WireGuard configuration. Run this on both servers:
wg-quick down wg0 && wg-quick up wg0
You can check that your connection still exists with the ping
command. At this point, everything should be setup and you should now have a connection without opening any ports on your home network.
One final step is to have this configuration persist on reboot. Now that everything works, you can run this command on both servers to have it initialize WireGuard and the wg0.conf
configuration on boot.
systemctl enable wg-quick@wg0
Here’s what your full wg0.conf
file should look like on both servers.
Raspberry Pi:
[Interface]
Address = 10.0.0.1/24
ListenPort = 49256
PrivateKey = oO/YauPyEcQf2HXaGB1WFc89W5xOklJZU9GVBHZPumk=
PostUp = iptables -t nat -A POSTROUTING -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -j MASQUERADE
[Peer]
Endpoint = <public ip>:49256
PersistentKeepalive = 25
PublicKey = wNzZYikgOiYG6+t7T4TlfDMVRHBZMJEyZrLMd0jg6i0=
PresharedKey = IOVMlzSIP4K1BlMkf8Gtxb7dcvA2lNLgZ3yaQq5RFI4=
AllowedIPs = 10.0.0.2/32
Cloud server:
[Interface]
Address = 10.0.0.2/24
ListenPort = 49256
PrivateKey = +PWm1KbzVfDT9dxMM+dxpF3jJr9W2Eu6NRmfsMlWAHw=
[Peer]
AllowedIPs = 10.0.0.1/32, 192.168.1.50/32
PublicKey = Kd0RnohXapDi5/HtAKZukMn+hRd7nqr77qpjAZBRtB8=
PresharedKey = IOVMlzSIP4K1BlMkf8Gtxb7dcvA2lNLgZ3yaQq5RFI4=
And, here’s a more generic template for how the public and private keys are meant to be used:
Raspberry Pi:
[Interface]
PrivateKey = <pi private>
[Peer]
PublicKey = <server public>
Cloud server:
[Interface]
PrivateKey = <server private>
[Peer]
PublicKey = <pi public>
This configuration can scale for more than one server. When adding a new cloud server, you only need to generate a public and private key on the new cloud server. The public key is the one thing that will remain the same when adding a new cloud server. At this point, you should have a solid understanding of how to setup WireGuard from scratch to connect two servers together with the cloud server being the one port forwarded.
Credit to StevenNL2000 for the majority of the knowledge in this blog post.