Self-hosted reversed tunnel for integration-heavy network application development
Are you developing some kind of chatbot, or perhaps integrating your app with Facebook Messenger?
You may find yourself in need of a reverse HTTP or TCP tunnel, ngrok is an example of such tunnels.
They are great at the marketing stuff, turning other solutions to dust, but lesser-known open-sourced self-hosted solutions do exist.
The solution is called SSH
SSH has every network tool majority of people want, including a reverse TCP tunnel that lets you expose your local webserver to the internet.
You may surprise at the quality and features provided by the open-sourced software called OpenSSH.
If you have a spare server with SSH connections,
you can turn it into a reverse tunneling server without installing any software.
OpenSSH got you covered.
Cutting to the chase, the command to be run on your localhost is:
ssh -R <remote-listen-address>:<remote-listen-port>:<local-listen-address>:<local-listen-port> -q -C -N <ssh-user>@<ssh-host>
Flags explained
- -R: Forward TCP to the local side
- -q: Quiet mode
- -C: Compress data
- -N: No command, port forwarding only
You may want to add other options, like for authentications; for me, the actual command I use is:
ssh -i ~/.ssh/id_example_server -R localhost:8081:localhost:8081 -q -C -N user@tunnel.example.com
Curious why I only listen to localhost
on the remote server?
Adding some context about the setup
TCP tunneling is hardly the only thing you need for your project. Most of the use cases also require TLS (HTTPS) connections.
That is why I also install NGINX and Let’s Encrypt’s certbot on my server.
I forward the traffic from NGINX to the remote listening address.
The NGINX has been used as a TLS termination proxy.
Below is an example of the end configuration I use for my NGINX server.
server {
listen 443 ssl; # managed by Certbot
server_name tunnel.example.com;
add_header Strict-Transport-Security "max-age=15552000" always;
location / {
proxy_pass http://localhost:8081;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
}
ssl_certificate /etc/letsencrypt/live/tunnel.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/tunnel.example.com/privkey.pem; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
}
server {
listen 80;
server_name tunnel.example.com;
if ($host = tunnel.example.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
return 404; # managed by Certbot
}
And that is also the reason why localhost
is the address for listening on the remote server in my setup.
Happy coding!