One (potential) downside to running public services on your homelab, is that you expose your IP address. That may, or may not, be a problem — but here are ways around it. The simplest way is to put Cloudflare in front, but this will only handle web traffic, and is a bit of a black box.

Another, more hands-on, approach is to use a VPS (or LXC container); WireGuard and iptables. We will create a secure tunnel between the VPS/container and the homelab HAProxy instance, and forward traffic using iptables.

User --> VPS --> Wireguard tunnel --> Haproxy --> Service
^^^^^^^^^^^^                          ^^^^^^^^^^^^^^^^^^^
  Internet                              The homelab LAN
Table of contents

WireGuard

WireGuard doesn’t have the server/client architecture that other VPN software does (like OpenVPN). Everyone can connect to everyone, it’s just a matter of configuration.

Server

By letting the VPS, or container, be the WireGuard server — we don’t have to open any ports on the home network.

First we need to install WireGuard:

$ sudo apt install wireguard

Then generate the private and public keys:

$ sudo -i
# cd /etc/wireguard/
# umask 077; wg genkey | tee privatekey | wg pubkey > publickey

And make the configuration:

$ sudo vim /etc/wireguard/wg0.conf
[Interface]
PrivateKey = private-key-we-generated-in-previous-step
Address = 10.0.0.1/24
ListenPort = 42295
The port doesn’t matter, just pick one 😃

…enable the service:

$ sudo systemctl enable wg-quick@wg0
$ sudo systemctl start wg-quick@wg0

Let’s check that the interface is active:

$ sudo wg
interface: wg0
  public key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  private key: (hidden)
  listening port: 42295

$ sudo ip a show wg0
5: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 10.0.0.1/24 scope global wg0
       valid_lft forever preferred_lft forever

Lastly: make sure your port, 42295 in this example, is open for UDP traffic.

Client

Now we need to repeat the process, but for the client.

Installing:

$ sudo apt install wireguard

Generating private and public keys:

$ umask 077; wg genkey | tee privatekey | wg pubkey > publickey

Configuring:

$ sudo vim /etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.2/24
ListenPort = 42295
PrivateKey = private-key-we-generated-in-previous-step

[Peer]
PublicKey = public-key-of-the-server
AllowedIPs = 10.0.0.0/24
Endpoint = server-ip:42295
PersistentKeepalive = 15

Enable:

$ sudo systemctl enable wg-quick@wg0
$ sudo systemctl start wg-quick@wg0

Check:

$ sudo wg
$ sudo ip a show wg0

Now we need to add the client public key to the server configuration, to allow the client to connect:

$ sudo vim /etc/wireguard/wg0.conf
# existing server configuration

[Peer]
PublicKey = public-key-of-the-client
AllowedIPs = 10.0.0.2/24

You should now have a tunnel between your local HAProxy and the VPS, the next step is to forward some traffic through it.

iptables

On the server (VPS/container) — we need to define some rules to forward traffic though our WireGuard tunnel:

$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.2:80
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination 10.0.0.2:443
$ sudo iptables -t nat -A POSTROUTING -j MASQUERADE

This will route traffic on TCP port 80 and 443 to IP 10.0.0.2, which is our WireGuard client.

iptables rules are not restored on boot, but there are ways to do this: https://www.cyberciti.biz/faq/how-to-save-iptables-firewall-rules-permanently-on-linux/

I used iptables-persistent.

Enabling packet forwarding:

$ echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
To make this permanent, you can edit /etc/sysctl.conf. Look for net.ipv4.ip_forward.

Wrapping up

Any incoming traffic on our VPS, or container, on port 80 or 443 — will now be forwarded through the WireGuard tunnel to the HAProxy instance in our homelab.

This will work for all TCP and UDP ports, just change your iptables rules accordingly.

Instead of iptables; it is also possible to use HAProxy. This has some advantages, and some drawbacks:

HAProxy > iptables:

  • SSL termination
  • Forward IPv6 traffic to IPv4
  • Hostname based rules

iptables > HAProxy:

  • Simpler configuration
  • Can forward UDP packages

I chose iptables because I’d like all the configuration to be done on my homelab HAProxy, and I needed to forward UDP packages.

Last commit 2024-04-05, with message: Tag cleanup.