Dec 15 2020
Lately I’ve been getting more into the self-hosted game. This DIY approach has given me a much greater appreciation for good software. It doesn’t come without its drawbacks though. Self-hosting requires more time, responsibility, planning, and various chores: data back-ups, log monitoring, ensuring good up-times, etc. It’s not a hobby to choose lightly!
If you decide to dabble with self-hosting, I recommend to start with a VPN. This can help secure an architecture from the start. While a VPN isn’t a one-stop-solution for all security problems, it does limit the attack surface. Plus, there’s the option to assign static IPs to all connected devices – giving complete control over the subnet.
Some things that I self host are:
- This website
- Rental applications for my properties (each LLC has its own)
- Git server
- Syncthing nodes (on VPN)
- Mediawiki (on VPN)
- Plex (on VPN)
For setting up Wireguard, there’s a couple of different types of peer configurations:
- The gateway
- A server for fulfilling requests (in my case, behind NAT)
- A client that connects to a server
For any devices serving requests, you will need to forward requests:
sysctl -w net.ipv4.ip_forward=1
According to the Arch Wiki, you can make this persist by adding the above to
With the right Wireguard configuration and some firewall adjustments, you will be off to the races! You’ll notice that there is a lot of redacted info in these configurations. This speaks to Wireguard’s conciseness.
Address = 10.10.1.1/24 PrivateKey = (hidden) ListenPort = 5555 PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE [Peer] # Linux desktop PublicKey = <<Pubkey of desktop>> # Restrict this peer to solely 10.10.1.2 (32 subnet means 1 ip) # This AllowedIps acts as a routing table. When we need to route traffic # We will look up the public key by ip, encrypt, and then send to the endpoint. # The endpoint is the last known point of access from the last connection AllowedIPs = 10.10.1.2/32 [Peer] # raspberry pi server behind NAT PublicKey = <<Pubkey of pi>> AllowedIPs = 10.10.1.3/32
Linux Desktop Config
This one is simple since we are not servicing requests from inside the VPN.
[Interface] Address = 10.10.1.2/24 # Matches address in gateway config ListenPort = 38820 PrivateKey = (hidden) [Peer] PublicKey = <<Gateway public key>> Endpoint = <<IP address of public server/gateway>> # Only route requests from 10.10.1.* through our remote peer. AllowedIPs = 10.10.1.0/24
Raspberry Pi Config
Besides the typical configuration, we also need the ip tables configured. This can be seen in the PostUp and PostDown. Finally, I specify a persistent keep alive timeout. The alternative would be to forward a port on your router. If you choose to do this, be sure to whitelist this to the gateway host only!
[Interface] Address = 10.10.1.3/24 # Matches address in gateway config ListenPort = 38820 PrivateKey = (hidden) # Punches a hole in the firewall to accept connections on the VPN network 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 [Peer] PublicKey = <<Gateway public key>> Endpoint = <<IP address of public server/gateway>> # We are behind a NAT, so we need a keep alive heartbeat. Otherwise the # connection will drop. # If you want a less chatty solution, you can port forward for the gateway. PersistentKeepalive = 15 # Only route requests from 172.16.0.* through our remote peer. AllowedIPs = 10.10.1.0/24
I could create an Ansible playbook for this, but I tend to subscribe to the YAGNI principle. If I do need to reconfigure something like this, I now have this post to reference :) I hope this helps you as well!