Many times during developing some application it becomes important to show the progress to someone over the internet.

Most of the time we end up using some screen sharing software. That, however, can only show my screen to the other person. They cannot really interact with the application.

We can use some software for remote desktop sharing but then I cannot work while the other person is using my screen.

One very nice solution is ngrok, which essentially creates a tunnel from the local system to ngrok’s servers and gives a nice URL.

ngrok

However, I was a bit concerned since all my traffic was getting redirected via ngrok’s servers.

I was looking around for something which would do the same thing but via my servers and finally came across inlets. This is an open source project, written in Go, which can be easily self hosted.

From their site

inlets creates a tunnel between two networks using a websocket and optional TLS for encryption. The main use-case for inlets is to expose a private API or service on the Internet, or to gain incoming network access (ingress) to a private network.

For this to work, three things are required –

1 inlets needs to be set up on a server (preferably over https)
2 inlets has to be running locally on a client
3 a program to run locally which serves over localhost

Thanks to Ruan’s blog post which got me going on this.

Configure inlets to run on server

Running inlets on the server is actually very straightforward.

curl -sLS https://get.inlets.dev | sudo sh

This puts the inlets program into /usr/local/bin/inlets so that it’s accessible everywhere.

Create a token variable

This should be a sufficiently long arbitrary string to provide security to the connection

$> export INLETSTOKEN=$(head -c 16 /dev/urandom | shasum | cut -d" " -f1)
$> echo $INLETSTOKEN
99377fd98062f1559aef634dba7d508443f58979

To see it –

$> echo $INLETSTOKEN
99377fd98062f1559aef634dba7d508443f58979

Copy paste it since it is going to be required from the local machine

Run inlets server

Now run inlets with this token

$> inlets server --port=8000 --token=$INLETSTOKEN
2020/02/22 02:04:14 Welcome to inlets.dev! Find out more at https://github.com/inlets/inlets
2020/02/22 02:04:14 Starting server - version 2.6.4
2020/02/22 02:04:14 Server token: "99377fd98062f1559aef634dba7d508443f58979"
2020/02/22 02:04:14 Control Plane Listening on :8000
2020/02/22 02:04:14 Data Plane Listening on :8000

Configure nginx to direct a URL to inlets server

This was the bigger challenge for me as I had to try different variations of the nginx configuration until I could get this working!

You should use a URL which should only be used for Nginx. So, something like –

inlets.<your domain>

Create a nginx conf file.

server {

  listen			80;
  listen			443 ssl;
  server_name		<inlets URL>;

  ssl_certificate <SSL certificate path>;
  ssl_certificate_key <SSL certificate key>;
  include /etc/letsencrypt/options-ssl-nginx.conf;
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

  error_log 		/var/log/nginx/inlets_error.log;
  access_log 		/var/log/nginx/inlets_access.log;

  location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;

    proxy_pass http://ws-backend;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }


  location /tunnel {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;
    proxy_pass "http://127.0.0.1:8000/tunnel";
  }

}

upstream ws-backend {
  server 127.0.0.1:8000;
}

I created separate log files for error and access so that I could identify the issues in case it doesn’t work.

Reload nginx for the configuration to take effect –

$> nginx -s reload

run a program locally which serves over localhost

I ran a simple rack server for testing purposes. This is a ‘config.ru‘ file –

run lambda { |env|
  [
    200,
    {'Content-Type' => 'text/html'},
    ['Hello World!']
  ]
}

And ran it using –

$> rackup
* Version 3.11.2 (ruby 2.4.2-p198), codename: Love Song
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://localhost:9292
Use Ctrl-C to stop

run inlets locally on your computer

Download inlets to the local system and run it using –

inlets client --remote wss://localhost.abhij.it --upstream=<inlets URL>=http://127.0.0.1:9292 --token=99377fd98062f1559aef634dba7d508443f58979

The token should be the same as the one used on the server.

Now, visit your URL to see your progrom output on a website!

Additional considerations on server

Since the inlets server is running in the foreground, it’s better to make it a service so that it runs in the background always. Also, it should start on boot.

Also, the inlets auth token should be saved in file so that the inlets server can access it.

save auth token to a file
$> echo "AUTHTOKEN=99377fd98062f1559aef634dba7d508443f58979" > /etc/defaults/inlets
create an inlets service file and save it in /etc/systemd/system/inlets.service
$> cat /etc/systemd/system/inlets.service
[Unit]
Description=Inlets Server Service
After=network.target

[Service]
Type=simple
Restart=always
RestartSec=1
StartLimitInterval=0
EnvironmentFile=/etc/defaults/inlets
ExecStart=/usr/local/bin/inlets server --port=8000 --token="${AUTHTOKEN}"

[Install]
WantedBy=multi-user.target

Stop the server if it is running in the foreground and start the inlets service

$> systemctl start inlets

Additional considerations on local computer

For running inlets on my local computer I just created an alias –

alias pipe=./path_to_inlets client --remote wss://localhost.abhij.it --upstream=<inlets URL>=http://127.0.0.1:$1 --token=99377fd98062f1559aef634dba7d508443f58979

Here, the $1 variable gets replaced with whatever port I give from the command line.

So, now I just have to run –

$> pipe 9292