rtyler

Making a local service public, with Azure Container Instances

Whether I’m sharing a locally developed service with a member of our globally distributed team, or I need to integrate some cloud-based service with local development, I frequently find the need to expose a local TCP service to the public internet. In the past I have tried to use tools such as localtunnel or smee.io, and in both cases I found them lacking; I simply want this TCP port open to the world! Yesterday afternoon I spent some time hacking on the first version of my own little solution: aci-tunnel.

aci-tunnel relies on the Azure CLI and will provision an ephemeral Azure Container Instance, to which an SSH reverse port forwarding tunnel is opened. The screencast below shows an example of using aci-tunnel to expose a locally running Jenkins environment:

The Details

There are two components to aci-tunnel, the first is the custom container which is deployed into Azure. The container is a fairly simple derivative of Alpine Linux with the openssh-server package installed. The daemon is also configured with GatewayPorts yes to enable binding a reverse port forward onto 0.0.0.0 in the container. For added security whenever aci-tunnel launches, it passes along the user’s ~/.ssh/id_rsa.pub along to the instance which is dropped into the container as an authorized_keys file. This ensures that only the user that launches aci-tunnel can access the container.

The container is launched with the ports 22, and whatever the user specifies, open to the public into Azure Container Instances.

On the local side, the aci-tunnel script creates the SSH tunnel with the right arguments to construct the reverse port forwarding enabled.

Once the highly sophisticated tunnel keep-alive command has been interrupted, terminating the SSH tunnel, aci-tunnel then destroys the container in Azure.


Wholly controlling my own tunnel infrastructure works quite well. In my early experimentation I was able to share a local service while sitting on public transit wifi, which was a bit slow but still allowed the HTTP and other TCP requests to transit the link properly.