Aapeli Vuorinen

A private ngrok with WireGuard and Nginx

Say you host a home lab behind a residential ISP, and they do annoying things like throttle or block ports, or something like that. So one funky way of getting around this is to set up a machine on a cloud provider with unrestricted ports, then just reverse proxy any connections from there and through WireGuard all the way back to your home lab. That way you also don’t have to keep any permanent ports open on your home connection and no one will know what’s going on there. As far as I’m aware, this kind of setup is basically what ngrok does, but these are obviously useful for completely different use cases!

Here’s how to do it.

Set up the tunnel on the proxy

I’m doing this once again on AWS EC2 as my proxy, with a basic Ubuntu 20.04 AMI. Install WireGuard:

sudo apt-get update
sudo apt-get install -y wireguard

Generate some keys (note, in reality you probably don’t want to generate the server key here, you only need the server public key on the proxy machine):

PROXY_PUBLIC_IP=$(curl -s https://checkip.amazonaws.com)

PROXY_PRIVATE_KEY=$(wg genkey)
PROXY_PUBLIC_KEY=$(echo $PROXY_PRIVATE_KEY | wg pubkey)

SERVER_PRIVATE_KEY=$(wg genkey)
SERVER_PUBLIC_KEY=$(echo $SERVER_PRIVATE_KEY | wg pubkey)

Create the config file:

sudo tee /etc/wireguard/wg0.conf << EOF
[Interface]
ListenPort = 51820
PrivateKey = $PROXY_PRIVATE_KEY
Address = 10.99.0.1/16

[Peer]
PublicKey = $SERVER_PUBLIC_KEY
AllowedIPs = 10.99.0.2/32
EOF

Bring up the interface:

sudo wg-quick up wg0

Generate the server config:

cat << EOF
[Interface]
PrivateKey = $SERVER_PRIVATE_KEY
Address = 10.99.0.2/32

[Peer]
PublicKey = $PROXY_PUBLIC_KEY
AllowedIPs = 10.99.0.1/32
Endpoint = $PROXY_PUBLIC_IP:51820
PersistentKeepalive = 25
EOF

Copy this config as you’ll need it on the server. (You can drop the PersistentKeepalive if you’re not behind a NAT.)

Set up the tunnel on the server

Now on the server paste the config from above into /etc/wireguard/wg0.conf, then start the interface. On a Mac I just paste this into the WireGuard app and click “save”.

(Optional) Test the tunnel

You should now have a WireGuard tunnel between the two, so if you run nc -l 5005 on the proxy, and echo "This is a secret" | nc 10.99.0.1 5005 on the server, that message should pop up on the proxy.

Set up proxying

Now say you want to run a secure website through the proxy, in such a way that when people hit the IP address of the proxy, it’ll ship the raw packets through the WireGuard tunnel to the server and serve the site from there. There’s a multitude of ways to do this. You could use iptables, but maybe the easiest is to just set up Nginx to reverse proxy TCP.

Install Nginx on the proxy:

sudo apt-get install -y nginx

Then add this after the first few directives into /etc/nginx/nginx.conf (feel free to delete other unnecessary stuff like the default HTTP proxy):

# add to /etc/nginx/nginx.conf
stream {
        server {
                listen 443;
                proxy_pass 10.99.0.2:8443;
        }
}

You can customise your ports as you wish, this will proxy 443 to port 8443 on the server.

Reload Nginx:

sudo systemctl reload nginx.service

Now run something on port 8443 on the server, and you should be able to access it through the proxy’s IP address on port 443!

Autostart on boot

To start on boot, just run:

sudo systemctl enable wg-quick@wg0.service