Recently we come across a use case that we need to proxy localhost to a https public domain and found ngrok
to be very intuitive and easy to use.
For example, I have a local dev server running on http://localhost:3000
and running ngrok gives me a public address with SSL supported https://some-sub-domain.ngrok.io
However for the free tier it doesn’t support sub-domain reservation. Each time I start ngrok
again it gives me a new address, which makes it difficult to use. So I decide to figure out ways to run this reverse tunneling on my own VPS server on Linode.
Public tutorials on this has been very complete and helpful, but I still take this note down for my specific setup case for future reference and share it out. Maybe your case is very similar to mine and I hope I can bring some approaches here.
This note is majorly based on Alex’s answer at https://www.digitalocean.com/community/questions/self-hosted-ngrok-or-serveo-alternative and I will paste other references along the way.
Before setting up everything, get a A record like tunnel.yourdomain
on Linode DNS provider.
First we need to get a wildcard SSL certificate for our domain (*.domain.com)
. Though SSL is not must need to enable the tunneling, it is strongly recommended for security.
To obtain free SSL certificate from Letsencrypt for my Linode Ubuntu instance, I took the instructions (wildcard section) from https://certbot.eff.org/lets-encrypt/ubuntubionic-nginx (Snap is already included in Ubuntu 18.04 LTS). Here are the commands:
$ sudo snap install core; sudo snap refresh core'
$ sudo apt-get remove certbot # remove existing if any
$ sudo snap install --classic certbot
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
$ sudo snap set certbot trust-plugin-with-root=ok
$ sudo snap install certbot-dns-linode
After this I obtain the wildcard certificate manually. To setup credentials for Linode https://certbot-dns-linode.readthedocs.io/en/stable/
$ sudo certbot certonly \
--dns-linode \
--dns-linode-credentials ~/.secrets/certbot/linode.ini \
--dns-linode-propagation-seconds 120 \
-d *.example.com
Collect the path to those certificates. And use Alex’s Nginx config with http to https redirect:
# Alex's
server {
server_name tunnel.yourdomain;
access_log /var/log/nginx/$host;
listen 443 ssl;
ssl_certificate /path/to/tls/cert/fullchain.pem;
ssl_certificate_key /path/to/tls/cert/privkey.pem;
location / {
proxy_pass http://localhost:3000/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
}
error_page 502 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
# added by me to automatically redirect http to https
server {
if ($host = tunnel.yourdomain) {
return 301 https://$host$request_uri;
}
listen 80;
server_name tunnel.yourdomain;
return 404;
}
Reload nginx, and when client’s localhost is not available, tunnel.yourdomain
will return 404 in my case.
Start localhost:3000 on client and run ssh -R 3000:localhost:3000 yourdomain
will start a ssh session with reverse proxy.
Happy rock on https://tunnel.yourdomain
!