Self-Hosting #0 - VPN

The self-hosted service that I use most of the time (without even noticing it) is probably my VPN. It allows me to reach my machines (home server, Raspberry Pis etc.) from everywhere outside of my home network.
A few year ago setting up a VPN was a time consuming task because the available software (like OpenVPN) was very hard to use and maintain.
That changed with the release of WireGuard which is now part of the Linux kernel and provides clients for all major platforms.
I run wireguard on a Hetzner cloud-VM with a static IP address which allows me to access it from basically everywhere.

Server configuration

The configuration of the server is very simple:

[matthias@mcloud-vpn ~]$ sudo cat /etc/wireguard/wg0-server.conf

[Interface]
Address = 172.17.21.1/24
PrivateKey = <server private key>
ListenPort = <server port>

# Matthias Desktop PC
[Peer]
PublicKey = <PC public key>
AllowedIPs = 172.17.21.2/32

# Matthias iPhone
[Peer]
PublicKey = <Smartphone public key>
AllowedIPs = 172.17.21.3/32

...

For security reasons I want to restrict the access between clients in the VPN.
To archive that I maintain a bash-script with iptables rules.
It first flushes all exiting iptables rules and assures that the operating system has ipv4 forwarding is enabled:

#!/usr/bin/env bash

# Allow forwarding
sysctl -w net.ipv4.ip_forward=1

# Flush iptables
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -t nat -F
iptables -t mangle -F
iptables -F
iptables -X

# Reject forwarding between VPN clients!
iptables -I FORWARD -i wg0-server -o wg0-server -j REJECT

To allow the connection between two clients in the VPN forwarding between them has to be accepted in both directions:

function allow() {
    iptables -I FORWARD -i wg0-server -s "$1/32" -d "$2/32" -j ACCEPT
    iptables -I FORWARD -i wg0-server -s "$2/32" -d "$1/32" -j ACCEPT
    echo "Allow $1 <-> $2"
}

allow 172.17.21.1 172.17.21.2

It's also possible to only allow one specific (TCP) port:

function allowPort() {
    iptables -I FORWARD -i wg0-server -s "$1" -d "$2" -p tcp --dport "$3" -j ACCEPT
    iptables -I FORWARD -i wg0-server -s "$2" -d "$1" -p tcp -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    echo "Allow $1 <-> $2:$3"
}

allowPort 172.17.21.1/32 172.17.21.2/32 445

Client configuration

Wireguard uses the same configuration format for servers and clients. Configuring clients is as simple as configuring the server. The configuration of the peer "Matthias Desktop PC" looks like this:

[Interface]
PrivateKey = <Client private key>
Address=172.17.21.2/32

[Peer]
PublicKey = <Server public key>
Endpoint = <Server ip>:<Server port>
AllowedIPs = 172.17.20.0/22
PersistentKeepalive = 21

As you can see, I use the ipv4 subnet 172.17.20.0/22 for my VPN.

Testing

I can access my bitwarden instance that I host at home via LTE. Success!

Side note: I tried to provide as much information as possible about my setup without publishing security relevant information. Because VPN server is probably the most security-relevant part of my whole infrastructure, I won't publish any information about its IP, Port or firewall.