How to expose any port to the Internet without exposing your private IP (for free).
Today I will show you how to easly setup exposure of any port without exposing your IP, this solution also bypasses situation when your ISP is not giving possibility to have private (own) IP address which is not behind any NAT.
I used this solution to create access (for myself) to SSH and locally hosted security camera system at my home.
1. Setup NGrok
First of all you need to have an ngrok account[1] - free account is enough for our purposes. Generally ngrok is a reverse-proxy service where you need tun ngrok client on target machine, client enables connection with ngrok server and then you are able to connect with your machine (where ngrok client is running) via Internet.
Free acount forwards local port as random remote port (usually under const hostname:
0.tcp.ngrok.io
for tcp tunnels)
After you have account you need to download client and activate account with your API key (follow the instuctions from web service).
2. Verify that tunnel works
Create ngrok config file (under ~/.ngrok2/ngrok.yml
):
authtoken: <YOUR_TOKEN_HERE>
tunnels:
sshglob:
proto: tcp
addr: 22
Above config setups tunnel sshglob
which forwards locally hosted tcp/22
- in this case SSH service. You should change these values according to your needs.
Now you can try to start ngrok ngrok start sshglob
:
ngrok by @inconshreveable (Ctrl+C to quit)
Session Status online
Account whoami (Plan: Free)
Version 2.3.35
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding tcp://0.tcp.ngrok.io:13036 -> localhost:22
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
Now you should be able to connect with ssh (in my case) with command: ssh -p 13036 whoami@0.tcp.ngrok.io
.
If you don’t have any issues with accessing you machine over Internet you are free to follow next steps :) (otherwise check Troubleshooting section).
3. Bypass random port issue
If you run ngrok couple of times you should notice that every time you service is running at random port, to bypass this issue you can by a premium or make some additional steps presented below.
Install dependencies
sudo apt install -y python3 python3-pip curl jq
pip3 install telegram
Setup Telegram bot
For myself I decided to create Telegram[2] bot which will send me external hostname and random IP address. Ofc you need to have created Telegram account to follow next steps.
- Start conversation with telegram Botfather.
- Type commands in Botfather conversation:
/newbot
mysuper_bot # replace "mysuper" with your bot name
- Copy API key and start conversation with your bot
- Type something in the chat & run the below code:
from sys import argv
import telegram
bot = telegram.Bot(token="YOUR_API_KEY")
print(bot.getUpdates())
- Copy
chat_id
- Create script
tsend.py
:
from sys import argv
import telegram
bot = telegram.Bot(token="YOUR_API_KEY") # FIXME your api key
chat_id = 1234 # FIXME YOUR_CHAT_ID
raw_msg = argv[1].replace('"','').replace('tcp://','') # "tcp://0.tcp.ngrok.io:10361"
ip,port = raw_msg.split(':')
message = f'{ip}:{port}'
bot.send_message(chat_id=chat_id, text=message)
- Check if you are able to send message to you from your bot by typing
python3 tsend.py hello
.
Make your service always available
Final step, below script will:
- Start ngrok tunnel if it is not running
- Send you actual
IP:PORT
thanks to telegram bot
Below script is designed to be run from cron, thanks to this solution it will be run automatically i.e every 5 minutes:
*/5 * * * * path/to/monitor.sh
# monitor.sh
#!/bin/bash
NGROK='/usr/local/bin/ngrok'
DIR="$HOME/ngrok-apps" # directory containing this script and tsend.py
URL_DIR='/tmp/ngrok-url'
# check if it's working
if [ ! "$(pidof ngrok)" ]; then
echo "[ngrok] is not working, starting new instance..."
$NGROK start sshglob > /dev/null &
sleep 3
fi
# get current status of tunnel
NEW_STATUS="$(curl -s http://127.0.0.1:4040/api/tunnels | jq '.tunnels[0] .public_url')"
echo "[ngrok] $(date) $NEW_STATUS"
OLD_STATUS=""
if [ -e "$URL_DIR" ]; then
OLD_STATUS="$(cat $URL_DIR)"
fi
# check if differ
if [ "$NEW_STATUS" != "$OLD_STATUS" ]; then
echo "[ngrok] New status detected! Updating IP"
echo "$NEW_STATUS" > "$URL_DIR"
python3 "$DIR/tsend.py" "$NEW_STATUS"
fi
If everything works you should receive notification to your telegram if IP or port will change.
Troubleshooting
My ngrok tunnel is started, but I can’t access service
Is this service running also on localhost? Or can you reach shown ngrok host (IP)?
Can I use something else than telegram?
Yes, but I had already telegram in place. If you need you can use i.e Discord, Slack, mail, etc. (but you need to modify tsend.py
script)