Wireguard tunnel for self-hosting

use wireguard to tunnel an external ip to a machine in your LAN

July 15, 2020
sysadmin wireguard vpn self hosting

So you want to self host ?

Finally decided to stop renting servers and have your own platform at home ? Possibly on an old PC, or a tiny SBC ( raspberry, orange pi, etc… ? )

In this article, I am not going to explain how to setup your server at home, but rather quickly outline how to use a wireguard VPN to mask your home public ip, and provide you with a fixed ip for external services. It also is useful if you self host your email, avoiding being flagged as spam because of a residential ip.

Prerequisites:

  • 1 cheap vps somewhere.
  • 1 server at home.

Global steps:

  • Install wireguard on both servers.
  • Configure wireguard on both servers

On the cheap vps:

/etc/wireguard/wg0.conf:

[Interface]
PrivateKey = VPSPRIVATEKEY
ListenPort = 3456
Address = 10.100.100.1/24,fd10:100:100::1/64
PostUp = /etc/wireguard/tunnel.sh up
PostDown = /etc/wireguard/tunnel.sh down

[Peer]
PublicKey = PEERPUBLICKEY
PersistentKeepalive = 25
AllowedIPs = 10.100.100.20/32,fd10:100:100::20/128

/etc/wireguard/tunnel.sh:

#!/bin/bash

if [ "$1" = "up" ]; then
    # global routing config
    iptables -I FORWARD 1 -i wg0 -j ACCEPT; 
    ip6tables -I FORWARD 1 -i wg0 -j ACCEPT; 
    iptables -t nat -I POSTROUTING 1 -o VPSPUBLICIFACE -j MASQUERADE;
    ip6tables -t nat -I POSTROUTING 1 -o VPSPUBLICIFACE -j MASQUERADE;
    echo 1 > /proc/sys/net/ipv4/ip_forward;
    echo 1 > /proc/sys/net/ipv6/conf/all/forwarding;
    
    # Setup NAT for ports to redirect to the self hosted box by hand
    iptables -I PREROUTING 1 -t nat -i VPSPUBLICIFACE -p tcp --dport 6482 -j DNAT --to 10.100.100.20:6482
    iptables -I PREROUTING 1 -t nat -i VPSPUBLICIFACE -p udp --dport 6482 -j DNAT --to 10.100.100.20:6482
    iptables -I FORWARD 1 -p tcp -d 10.100.100.20 --dport 6482 -j ACCEPT
    iptables -I FORWARD 1 -p udp -d 10.100.100.20 --dport 6482 -j ACCEPT

    # Or do a bunch of ports in batch
    for port in {8,4897,4546,80,443}; do
        iptables -I PREROUTING 1 -t nat -i VPSPUBLICIFACE -p tcp --dport $port -j DNAT --to 10.100.100.20:$port
        iptables -I FORWARD 1 -p tcp -d 10.100.100.20 --dport $port -j ACCEPT
    done
elif [ "$1" = "down" ]; then
    # Undo specific NAT
    iptables -D PREROUTING -t nat -i VPSPUBLICIFACE -p tcp --dport 6482 -j DNAT --to 10.100.100.20:6482
    iptables -D PREROUTING -t nat -i VPSPUBLICIFACE -p udp --dport 6482 -j DNAT --to 10.100.100.20:6482
    iptables -D FORWARD -p tcp -d 10.100.100.20 --dport 6482 -j ACCEPT
    iptables -D FORWARD -p udp -d 10.100.100.20 --dport 6482 -j ACCEPT
    
    for port in {8,4897,4546,80,443}; do
        iptables -D PREROUTING -t nat -i VPSPUBLICIFACE -p tcp --dport $port -j DNAT --to 10.100.100.20:$port
        iptables -D FORWARD -p tcp -d 10.100.100.20 --dport $port -j ACCEPT
    done

    # Undo global routing config
    iptables -D FORWARD -i wg0 -j ACCEPT; 
    ip6tables -D FORWARD -i wg0 -j ACCEPT; 
    iptables -t nat -D POSTROUTING -o VPSPUBLICIFACE -j MASQUERADE;
    ip6tables -t nat -D POSTROUTING -o VPSPUBLICIFACE -j MASQUERADE;
fi

On the server at home:

/etc/wireguard/wg0.conf:

[Interface]
PrivateKey = PEERPRIVATEKEY
Address = 10.100.100.20/32,fd10:100:100::20/128
Table = off
PostUp = /etc/wireguard/tunnel.sh up
PostDown = /etc/wireguard/tunnel.sh down

[Peer]
PublicKey = VPSPUBLICKEY
Endpoint = VPSPUBLICIP:3456
AllowedIPs = 0.0.0.0/0,::/0
PersistentKeepalive = 21

/etc/wireguard/tunnel.sh:

#!/bin/bash

if [ "$1" = "up" ]; then
        # add vpn net routes
	ip route add 10.100.100.0/24 dev wg0
	ip -6 route add fd10:100:100::/56 dev wg0
        # add any local route that could be useful since default gateway will change
	route add -net 192.168.0.0/24 gw 10.0.0.1
        # we are going to mark the traffic and use a different table
        # this will allow to route req coming from the lan interface to go back through it
        # ( think if you want to route some things through the vpn OR your local LAN ip )
	wg set wg0 fwmark 100
	ip route add default dev wg0 table 100
	ip -6 route add default dev wg0 table 100

        # now let's mark packets coming from the LAN iface
        # Set the LAN mark LOCALIFACE = eth0 for example
        iptables -I PREROUTING 1 -t mangle -i LOCALIFACE -j MARK --set-mark 10
        iptables -I PREROUTING 2 -t mangle -i LOCALIFACE -j CONNMARK --save-mark
        iptables -I OUTPUT 1 -t mangle -j CONNMARK --restore-mark
        iptables -I POSTROUTING 1 -t mangle -j CONNMARK --restore-mark
        ip6tables -I PREROUTING 1 -t mangle -i LOCALIFACE -j MARK --set-mark 10
        ip6tables -I PREROUTING 2 -t mangle -i LOCALIFACE -j CONNMARK --save-mark
        ip6tables -I OUTPUT 1 -t mangle -j CONNMARK --restore-mark
        ip6tables -I POSTROUTING 1 -t mangle -j CONNMARK --restore-mark

        # and now set the default routes to use depending on the fw mark
	ip rule add table main suppress_prefixlength 0 priority 99
	ip rule add fwmark 10 table main priority 100
	ip rule add not fwmark 100 table 100 priority 101
	ip -6 rule add table main suppress_prefixlength 0 priority 99
	ip -6 rule add fwmark 10 table main priority 100
	ip -6 rule add not fwmark 100 table 100 priority 101

elif [ "$1" = "down" ]; then
        # let's tear down everything
	ip route del 10.100.100.0/24 dev wg0
	ip -6 route del fd10:100:100::/56 dev wg0
	ip route flush table 100
	ip -6 route flush table 100
	ip rule del not fwmark 100 table 100
	ip rule del fwmark 10 table main
	ip rule del table main suppress_prefixlength 0
	ip -6 rule del not fwmark 100 table 100
	ip -6 rule del fwmark 10 table main
	ip -6 rule del table main suppress_prefixlength 0
        iptables -D PREROUTING -t mangle -i LOCALIFACE -j MARK --set-mark 1
        iptables -D PREROUTING -t mangle -i LOCALIFACE -j CONNMARK --save-mark
        iptables -D OUTPUT -t mangle -j CONNMARK --restore-mark
        iptables -D POSTROUTING -t mangle -j CONNMARK --restore-mark
        ip6tables -D PREROUTING -t mangle -i LOCALIFACE -j MARK --set-mark 1
        ip6tables -D PREROUTING -t mangle -i LOCALIFACE -j CONNMARK --save-mark
        ip6tables -D OUTPUT -t mangle -j CONNMARK --restore-mark
        ip6tables -D POSTROUTING -t mangle -j CONNMARK --restore-mark
fi

Last steps:

  • restart wg-quick@wg0 on each server
  • check pings etc…
  • enjoy :)

Conclusion:

This is a simple setup to forward some ports from a cheap machine to a server in your local lan. Of course this does NOT cover LAN isolation at home, your home firewall, the general firewall of the VPS, or even using NGINX as a proxy on the cheap VPS to terminate services locally on forward to your home server ( which should make for an other article ).

This setup will allow your home server to route everything by default through the wireguard vpn, but to also respond via the LAN interface when requests are received through it.