Self Hosting Services Behind CGNAT

Introduction

With the IPv4 address space depletion, many ISPs have resorted to CGNAT and the assignment of a private IP address instead of giving their residential customers a public one. This presents a problem for those who want to self host their own services out of their home. Fortunately, WireGuard can help to solve this problem by establishing a VPN tunnel between a host on your network and a VPS with a public IP address. In effect, the VPS becomes the entry point for the service(s) that you wish to host.

You do not need a particularly powerful VPS in order to do this. I use Cloudfanatic and they have a low end VPS with 1GB of RAM and 30GB of storage space for $2.99 a month which is plenty. This guide is written for users of Alma Linux so your distro may vary some. You will need to have some sort of reverse proxy running on your VPS to redirect traffic to the service you are hosting. Setting up the reverse proxy is beyond the scope of this article and will be for a future one.

Setting Up The VPS

Prior to configuring WireGuard, we have some Linux-based configuration to do to ensure the tunnel can be established and proper IP forwarding happens. The first step is setting Linux up for IP forwarding. This will turn IP forwarding on and set it permanently on.

# echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
# sysctl net.ipv4.ip_forward=1

Now we have to open up port 51820/udp on the firewall. This will vary depending on your Linux distro. Red Hat and Fedora-based distros use firewall-cmd. Alma Linux uses firewall-cmd.

# firewall-cmd --zone=public --add-port 51820/udp --permanent
# firewall-cmd --reload

Once the firewall port is open, we have to install the wireguard-tools package. Follow the steps below to install this set of tools.

# dnf install epel-release
# dnf install wireguard-tools

Now it is time to actually begin the configuration of WireGuard itself. The first phase is generating the keys. While not strictly necessary, I do recommend using the additional security of a pre-shared key.

# cd /etc/wireguard
# wg genkey | tee privkey | wg pubkey > pubkey
# openssl rand -base64 32 > presharedkey

Create the wg0.conf file which will contain the WireGuard tunnel configuration. I use the 192.168.254.0/24 address but you could use any private addressing scheme that you want. Substitute ens3 with the network interface of your VPS.

[Interface]
PrivateKey = <private_key>
Address = 192.168.254.1/24
ListenPort = 51820
PostUp = iptables -I FORWARD 1 -i wg0 -j ACCEPT
PostUp = iptables -I FORWARD 2 -o wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -o wg0 -j ACCEPT

[Peer]
PublicKey = <public_key>
PresharedKey = <prehsared_key>
AllowedIPs = 192.168.254.2/32

The next step is to enable and start the WireGuard service. When the WireGuard service gets started, the firewall rules specific to WireGuard forwarding will automatically get created. Similarly, the rules will get removed when the WireGuard service is stopped.

# systemctl enable --now wg-quick@wg0.service

Once you have started WireGuard, do a quick check to make certain that the tunnel interface, wg0, has been created. You should get something similar to what you see below.

# ip address show wg0
3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none 
    inet 192.168.254.1/32 scope global wg0
       valid_lft forever preferred_lft forever

Linux Host

Once the VPS has been configured, it is on to the Linux host in your home. I also happen to be using Alma Linux to run my self hosted services so this will heavily reflect Red Hat and Fedora-based distros. First, we have to enable IP forwarding similarly to the VPS.

# echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
# sysctl net.ipv4.ip_forward=1

Similar to the VPS, we must install the wireguard-tools package.

# dnf install epel-release
# dnf install wireguard-tools

The next step is creating the keys for the Linux host.

# cd /etc/wireguard
# wg genkey | tee privkey | wg pubkey > pubkey
# openssl rand -base64 32 > presharedkey

Create the wg0.conf file.

[Interface]
PrivateKey = <private_key>
Address = 192.168.254.2/24
MTU = 1420

[Peer]
PublicKey = <public_key>
PresharedKey = <preshared_key>
Endpoint = <VPS_IP>:51820
AllowedIPs = 192.168.254.1/32
PersistentKeepalive = 25

Enable and start WireGuard.

# systemctl enable --now wg-quick@wg0.service

Verify that the tunnel has been created.

# ip address show wg0
3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none 
    inet 192.168.254.1/32 scope global wg0
       valid_lft forever preferred_lft forever

Once we have verified that the tunnel has been created, we can now check to see if the handshake was successful by doing the following. Typing wg brings up the WireGuard status. If it was successful, you'll see something similar to the following:

# wg
interface: wg0
  public key: <public_key>
  private key: (hidden)
  listening port: 50209

peer: <peer_public_key>
  preshared key: (hidden)
  endpoint: <VPS_IP>:51820
  allowed ips: 192.168.254.1/32
  latest handshake: 1 minute, 13 seconds ago
  transfer: 359.71 MiB received, 2.50 GiB sent
  persistent keepalive: every 25 seconds

Try pinging the VPS via its WireGuard IP address. You should see something similar to what is shown below:

# ping 192.168.254.1
PING 192.168.254.1 (192.168.254.1) 56(84) bytes of data.
64 bytes from 192.168.254.1: icmp_seq=1 ttl=64 time=17.2 ms
64 bytes from 192.168.254.1: icmp_seq=2 ttl=64 time=17.1 ms
64 bytes from 192.168.254.1: icmp_seq=3 ttl=64 time=18.0 ms
64 bytes from 192.168.254.1: icmp_seq=4 ttl=64 time=27.4 ms
64 bytes from 192.168.254.1: icmp_seq=5 ttl=64 time=20.3 ms
64 bytes from 192.168.254.1: icmp_seq=6 ttl=64 time=53.4 ms
64 bytes from 192.168.254.1: icmp_seq=7 ttl=64 time=19.0 ms
^C
--- 192.168.254.1 ping statistics ---
7 packets transmitted, 7 received, 0% packet loss, time 6009ms
rtt min/avg/max/mdev = 17.134/24.650/53.448/12.207 ms

Conclusion

It is important to note that this is purely a point-to-point tunnel between your home Linux host and your VPS. This is the foundation of setting up your services to make them accessible to the public. As I mentioned before, you will need a reverse proxy like NGINX installed and configured on the VPS and you will reference 192.168.254.2 as the destination for the proxy redirect.

Mastodon