Remote Server behind CGNAT using Wireguard

Sep 1, 2024

I keep delaying this post because it's something that has already been done, and I don't want to repeat myself. But Jeff Geerling, a Youtuber I follow, posted a video about deploying your own VPN to access your server from a remote location, and I feel like I must write my own experience on the matter.

The differences in my approach

According to Mr. Geerling, he had a public IP (though not static) and pointed his domain to this IP to connect to his network using Wireguard installed by PiVPN. I do not have such luxury, as my public IP is 10.x.x.x through PPPoE to my ISP, Viettel, so using his way was out of the question. I am stuck in a situation known as CGNAT. What's more, he exposed ALL his network out, which, to each of their own, is quite dangerous as the VPN can be used as an attack vector to your own network. I did a similar thing while creating my own VOIP service using Asterisk, but it only lasted a day, and I quickly closed it due to the danger behind it. PiVPN, the tool he used, is quite fascinating and I will look into it in the future. But back when I built my VPN, it was on a virtual server, and I built it manually. I didn't know any automated tool like his, I just followed some guides from DigitalOcean to set up Wireguard, along with fail2ban, ssh. In fact, the reason I chose Wireguard was that it was easier to set up. OpenVPN has a lot of moving parts needed to run, while Wireguard only requires a pair of keys. Granted, there are scripts that help install OpenVPN, but the performance is OpenVPN's Achilles's heel. I only have 1 vCPU, so performance is kind of a big deal. Lastly, his video mentioned "a little extra security on public Wifi" and "bypass content restriction". While my VPN can't bypass any content restriction, it can provide encryption for my traffic on public Wifi, in addition to providing its own DNS server to speed up DNS query.

CGNAT (carrier-grade network address translation)

A blog post on Draytek explains this well, but to quote from a reddit comment as a short version: "CGNAT means your ISP doesn't have enough public IPv4 addresses to assign one to each user. So they are essentially doing to you what your home router does to the Internet by giving you a NAT'ed IP address. Yes, that means you can't run your own servers or forward traffic at home". My situation may not technically be CGNAT, could be simply double NAT from my end and my ISP's, because my "public" IP is not in the CGNAT address block, which is usually from 100.64.0.0 to 100.127.255.255, is 10.x.x.x. I first noticed this phenomenon in 2014, when I installed fiber. Though back then I didn't know how to bridge the ISP router to my TP-Link router, I knew of double NAT and opened similar port on both routers to torrent. However, I checked on https://canyouseeme.org/ and they couldn't see the open port. The torrent could still download, but it couldn't upload. It was strange, because before that, I could see the open port when I was still on ADSL (also double NAT). It was my biggest achievement back in 8^th^ grade, to open port and torrent, or host my own radio using Windows Media Encoder. The second biggest was getting 3D games to run on my Pentium 4 computer.

Setting up virtual server

There are a lot of options when choosing a virtual server provider. I chose Viettel IDC because it was the closest to home, and I also use their fiber service. I bought 1 vCore with 1GB RAM, 20GB SSD, 300 Mbps unlimited bandwidth. I chose Ubuntu Server as the base OS, because I'm used to Debian-based OS. I guess the way to choose your preferences works the same in other providers, whether AWS, GCP, Linode...

Some steps I performed before setting up WIreguard:

- Install ssh to have remote access, instead of using console from browser. Change ssh port, only allow access with key

- Configure firewall using ufw: Deny all incoming, allow ssh port, Wireguard port

- Install fail2ban to limit amount of login attempts

Setting up Wireguard

I followed this guide on how to set up Wireguard Server. First, I generated 2 pairs of keys, one for the server, one for the client using the commands:

wg genkey | sudo tee /etc/wireguard/private.key

sudo cat /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key

Following that, I had to choose a subnet. Since 192.168.0.x, 1.x are all too common, I chose anything from 2.x and up. After that, I went to /etc/wireguard/. I created a config file for my VPN, wg0.conf

[Interface]

Address = 192.168.13.1/24

PostUp = ufw allow 51820/udp

PostUp = ufw route allow in on wg0

PreDown = ufw delete allow 51820/udp

PreDown = ufw route delete allow in on wg0

ListenPort = 51820

PrivateKey = <server private key here>

[Peer]

PublicKey = <client public key here>

AllowedIPs = 192.168.13.2/32

[Interface] is configurations for your own VPN interface, whether it's a server or a client. PostUp and PreDown are commands to run after turning on the VPN server and before shutting down server, respectively. I set up a bunch of commands that open port, allow traffic running inside VPN and vice versa when turning off server. [Peer] are configurations for clients that will connect to the server. Each [Peer] adds another client to the server. AllowedIPs is where you type in the client's VPN IP address. If the client also acts as a router to another subnet, for instance, 192.168.0.x, you can add 192.168.0.0/24 and you can access that subnet from the VPN.

On my home server, not the virtual one. I also created a config file in /etc/wireguard/wg0.conf:

[Interface]

PrivateKey = <client private key here>

Address = 192.168.13.2/24

DNS = 8.8.8.8

[Peer]

PublicKey = <server public key here>

AllowedIPs = 192.168.13.0/24

Endpoint = <server real IP>:51820

PersistentKeepalive = 25

[Peer] here means something different. For a client to connect to a server, it only needs 1 peer. For Endpoint, you need to pinpoint the virtual server's IP address. PersistentKeepalive is necessary to keep the connection up, as we are running essentially a tunnel from the virtual server to the real one. If the client isn't a server, that part won't be needed.

On both servers, I typed in wg-quick up wg0. I pinged from each side with the other's IP address and it worked.

References:

- Jeef Geerling - Build your own private WireGuard VPN with PiVPN

- Information about icanhazip.com, you can use ident.me instead

- ELI5: How does NAT (Network Address Translation) work