September 22, 2020

ngrok alternative: localtunnel + Caddy + Lets Encrypt


Update: See more up to date post ngrok alternative²: frp + Caddy + Lets Encrypt


Sometimes you want to show localhost to the world or need real world access for testing or callback urls. Usually you’re using ngrok. Thats okay, i’ll do too.

But also sometimes, you are a paranoid bastard and there’s no way you’re routing your data through alien infrastructure.

This is where localtunnel kicks in. For wildcard subdomains, you’ll also need a wildcard subdomain dns entry and a loadbalancer/reverse proxy. This could be done using e.g. Route53 and and ALB with wildcard certificates on AWS or using Caddy and Let’s Encrypt on your own infrastructure, like i did.

Setup

localtunnel server

  • prepare dedicated user:
    • sudo useradd -m -s /bin/bash -r localtunnel
  • install nodejs 10 (e.g. using packages or your favourite virtual nodejs environment)
  • fetch sources:
    • git clone git://github.com/defunctzombie/localtunnel-server.git
  • apply patch from PR 107
    • cd localtunnel-server
    • git apply <(curl -sL https://patch-diff.githubusercontent.com/raw/localtunnel/server/pull/107.diff)
  • install modules:
    • npm i
  • create systemd unit:
    • add the following to /etc/systemd/system/localtunnel.service (adjust e.g. port, port-range, domain to your like)
[Unit]
Description=localtunnel server
After=network.target
Documentation=https://github.com/localtunnel/server

[Service]
WorkingDirectory=/home/localtunnel/localtunnel-server
ExecStart=/usr/bin/node -r esm bin/server --port 1234 --domain your-subdomain.example.com --client-min-port-range 3000 --client-max-port-range 3010 --host 127.0.0.1
User=localtunnel
Group=localtunnel
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
  • start and enable:
    • sudo systemctl enable --now localtunnel.service

Caddy

  • add subdomain and wildcard domain (w/ on demand tls) to your Caddyfile
your-subdomain.example.com {
  reverse_proxy http://127.0.0.1:1234
}

*.your-subdomain.example.com {
  reverse_proxy http://127.0.0.1:1234
  tls {
    on_demand
  }
}

Iptables

Add a rule which allows incoming TCP traffic within the port range specified (e.g. 3000-3010).

Example:

iptables -A INPUT -p tcp --match multiport --dports 3000:3010 -j ACCEPT