Using SSH Port Forwarding to map to public URL

ssh
docker
network

#1

I’m looking for a way to map various web frontends to the public url on port 80 (one at a time). I tried SSH port forwarding to map this dynamically on the device to reduce overhead compared to a separate nginx container .
Remote port forwarding to a public server works fine. However I’d like to map it locally to access it via the public url. I didn’t get this to work, neither on the Host OS (locally mapping port xy to port 80) nor from within a docker container with SSH exposing port 80.

Is it possible? If so how or why not?


#4

Hello @Hesch,

When a http(s) connection is made to a balena public URL like https://uuid.balena-devices.com, it first reaches the balena cloud and it is then tunnelled over a VPN to the device. On the device, a process named ‘balena-engine-proxy’ (balena’s counterpart to ‘docker-proxy’) receives the traffic on port 80, as can be seen by running the commands lsof and ps on a host OS terminal:

$ lsof -i :80
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
balena-en 1189 root    6u  IPv6  16338      0t0  TCP *:http (LISTEN)

$ ps | grep 1189
 1189 root      844m S    /usr/bin/balena-engine-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.19.0.2 -container-port 80

The app container to which traffic is forwarded by ‘balena-engine-proxy’ depends on the docker-compose.yml ‘ports’ instruction:

Your best bet is probably to have ssh to listen on port 80 in an appropriately configured app container, rather than on the host OS. This is also more flexible because you have more control over what gets installed and run in an app container, compared to the host OS. Inside the app container, a useful Linux command to inspect the listening ports is netstat. In one of my app containers, I get:

$ netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
tcp        0      0 172.19.0.2:80           52.4.252.97:52670       ESTABLISHED
...

The output above shows that a process is listening on port 80 – in my case a web server, but it could be the ssh process in your case – and ‘52.4.252.97’ is one of the balena cloud IP addresses for the public URL VPN tunnel.

If the explanation above hasn’t revealed why your setup isn’t working, I would need to understand a bit more about what exactly you are running:

  • What are your port forwarding endpoints? A command like:
    ssh -L 123:someDestination.com:456 user@someIntermediate.com
    involves 3 (end)points, so to speak:
    1. The machine/device where the ssh command is run, where a socket listens on port 123;
    2. The “intermediate” machine/device (‘someIntermediate.com’) that runs the ssh server;
    3. The port forwarding destination (‘someDestination.com’ port 456) to which traffic from port 123 is forwarded via the intermediate machine/device.
  • The various web frontends you mentioned, are they all running inside a single app container in the balena device? Or each web frontend in a separate app container? Or maybe the web frontends are not running on the device?
  • When you say that “remote port forwarding to a public server works fine”, what are the 3 endpoints as described above?

Having said all that, I would also suggest that you to consider running a haproxy container as an alternative to ssh port forwarding, as done in the following sample project:

This has the advantage that you could run several web frontends simultaneously, rather than one at a time, disambiguating through URL paths. Thanks to balena’s platform, proxy config changes are only a ‘git push’ away!