Page 1 of 1

zmNinja connection via ngrok with script for setting up a ngrok connection

Posted: Thu Jan 12, 2023 1:19 am
by Autonomous
With zmNinja it is possible to tunnel into a ZM server on android with ngrok running on termux with ssh local port forwarding. It works great, however with free ngrok accounts you need to constantly obtain the latest hostport. It can be a pain to setup a ngrok connection on your phone manually so I wrote the following script that obtains the current ngrok hostport and sets up the ssh tunnel:

Code: Select all

#!/usr/bin/python3

import os
import subprocess
from os.path import expanduser
import json
import time
import shutil

# Simple function to read variables from secret file

def read_secrets(config=expanduser("~")+'/.local/share/ngrok/secrets.ini'):
    from configparser import ConfigParser
    secrets_object = ConfigParser(
        interpolation=None, inline_comment_prefixes='#')
    secrets_object.optionxform = str
    with open(config) as f:
        secrets_object.read_file(f)
    return secrets_object._sections['secrets']

# Obtain endpoint hostport from ngrok api and connect to remote host via ssh

def connect_remote(username, api_key, local_forward_list):

    curl_path = shutil.which("curl")
    if not curl_path:
        print("curl not found, exiting")
        return 0

    ssh_path = shutil.which("ssh")
    if not ssh_path:
        print("ssh not found, exiting")
        return 0

    # setup curl request command string:
    curl_cmd = curl_path+''' \
    -H "Authorization: Bearer '''+api_key+'''" \
    -H "Ngrok-Version: 2" \
    https://api.ngrok.com/endpoints
    '''
    # send ngrok api request
    ngrok_result = subprocess.check_output(curl_cmd, shell=True)

    # parse json
    ngrok_json = json.loads(ngrok_result)

    # obtain hostport value
    hostport = ngrok_json['endpoints'][0]['hostport'].split(':')

    if not hostport:
        print('no hostport object')
        return 0

    hostname = hostport[0]
    port = hostport[1]

    # add list of local forward parameters from secrets
    local_forward_str = ''
    for forward in local_forward_list:
        local_forward_str += ' -L '+forward

    ssh_cmd = ssh_path+' -p '+port+' -o StrictHostKeyChecking=no '+local_forward_str+' '+username+'@'+hostname

    #invoke remote shell via ngrok tunnel
    return os.system(ssh_cmd)

# main function

secrets = read_secrets()
username = secrets.get('REMOTE_USERNAME')
api_key = secrets.get('NGROK_API_KEY')
local_forward_list = json.loads(secrets.get('LOCAL_FORWARD'))

# connect and resume connection after sleep or connection errors
while(True):

   result = connect_remote(username, api_key, local_forward_list)

   print("ssh return code:",result)
   exit_codes = [0,1,2,65,71,75,77,78,79]

   # exit if ssh code is success or not related to connection errors
   if result in exit_codes:
      quit()

   # sleep for 2 seconds and retry connection
   time.sleep(2)

This script also makes it convenient to make a ssh connection via free ngrok from any computer.

Sample secrets.ini file, located at ~/.local/share/ngrok/secrets.ini

Code: Select all

[secrets]
REMOTE_USERNAME={USERNAME}
NGROK_API_KEY={NGROK_API_KEY}
# local forward format is LOCAL_PORT:REMOTE_IP:REMOTE_PORT
LOCAL_FORWARD=[
        "8280:192.168.2.1:80",
        "8122:192.168.1.1:22"
        ]
zmNinja connect launcher icon:

Image