Marco Amann  -  2021-06-18

Global local port

How to expose a local port on your development machine to the internet with a proper domain name, TLS cert and some access control? Here’s my solution using SSH and a VPS.

The requirements are simple:

  • We need to expose an application running on a local development machine behind a firewall and NAT to the internet.
  • We further want to have a proper domain name and valid TLS.

Why?

Why expose development setups at all? Aren’t they meant to be run only locally? While this is true, there are some things that are different when running on localhost. An example for this is the secure context that makes exceptions for *.localhost, 127.0.0.1 and possibly ::1/128, see the spec.

Don’t geht me wrong: Development services are insecure by design, so exposing them without a bit of protection is risky. So please proceed with caution.

Rejected Solutions

Some designs did not work quite well for me, maybe you have more luck with these.

NAT + Open Firewall + global IP

The easiest way to expose some service probably is to do exactly that, directly expose the socket the application is listening on. Let’s assume your application runs on your laptop that is conncted to the internet via wifi, having a local IP address provided by your router. This means you have to open the port in the host firewall, then in the router firewall and add a dst-nat rule. But if you develop with multiple setups, forwarding the current host and port combination means changing the rules in the cumbersome router UI every time.

Further, this approach does not directly provide TLS. And for me, handling TLS certs on a dev machine is not an option. Even a real fqdn is tricky if you don’t have a static IP address in your home network.

IPv6

Luckily, I have a small (dynamic) IPv6 subnet assigned to my connection. Giving all devices in the LAN a publicly routable IPv6 address removes the need for NAT but still requires you to set up the firewall correctly.

I decided I do not want to touch the router every time the port I want to expose changes.

Possibly the Easiest Solution

On my VPS, certbot is automatically setup to request certificates for the nginx instance. So by pointing a sub-domain to the server and adding that to the certbot config, TLS was solved. Although a routed solution would have been cooler (but harder to secure), the nginx does TLS offloading and forwards the now unencrypted HTTP traffic to a local port on the server.

This is where SSH comes to the mix: with a reverse ssh tunnel, exposing a local ports is easy. At least if you are the only one doing so. To forward your local dev setup on port 8080 to port 4269, the following command would to the job:

ssh -R 127.0.0.1:4269:127.0.0.1:8080 -N vps

Sprinkles of security

As stated initially, one should not expose applications that run in an insecure development mode to the wider internet. So how do we secure this setup? For me I chose to simply whitelist the IP range my connection gets addresses assigned from. Not great, not terrible.


Associated Tags: