Reverse SSH Tunnel Launcher
Here is a script which helps with starting a reverse SSH tunnel. It gets the instructions from a web server, in the form of a JSON file, which determines at what time to open the tunnel(s). The JSON file also determines when to check for a JSON file next time. It operates on a daily schedule and uses the HH:MM format.
The idea is to leave this script running on a box behind a firewall. It is hardcoded to only keep the tunnel open for 5 minutes before closing it again. So to get a connection that lasts longer, a new tunnel probably has to be made manually after connecting.
The Python code:
#!/usr/bin/python import urllib2 import json import subprocess import signal import datetime import time import logging import sys config_url = "http://192.168.0.1/config.json" ssh_host = "192.168.0.1" ssh_port = 22 local_port = 1337 tunnel_open_time = 300 # In seconds. tunnel = None def alarm_handler(signum, frame): global tunnel logger.info("Terminating current tunnel.") tunnel.kill(); tunnel = None def minutes_until(minute_ts): if minute_ts == None: return (24 * 60) now = datetime.datetime.now().timetuple() now_ts = (now.tm_hour * 60) + now.tm_min if now_ts > minute_ts: # Already passed, will be next day. return minute_ts - now_ts + (24 * 60) else: return minute_ts - now_ts def update_config(): try: config = json.load(urllib2.urlopen(config_url)) # Pick closest time to update next time: closest_config_ts = None for uc in config["ConfigUpdate"]: config_ts = (int(uc.split(":")[0]) * 60) + int(uc.split(":")[1]) if minutes_until(config_ts) < minutes_until(closest_config_ts): closest_config_ts = config_ts tunnel_ts = list() for to in config["TunnelOpen"]: tunnel_ts.append((int(to.split(":")[0]) * 60) + int(to.split(":")[1])) logger.debug("Config timestamp: %d" % (closest_config_ts)) logger.debug("Tunnel timestamps: %s" % (str(tunnel_ts))) return closest_config_ts, tunnel_ts except Exception, e: logger.error("Exception during config update: %s" % (e)) return 0, [] if __name__ == "__main__": logger = logging.getLogger('reverse_tunnel') logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler()) # Get initial configuration: config_ts, tunnel_ts = update_config() if len(tunnel_ts) == 0: sys.exit(1) # Exit right away if initial configuration fails. signal.signal(signal.SIGALRM, alarm_handler) while True: now = datetime.datetime.now().timetuple() now_ts = (now.tm_hour * 60) + now.tm_min # Is it time for a config update? if now_ts == config_ts: logger.info("Timed config update.") config_ts, tunnel_ts = update_config() if config_ts == now_ts: config_ts = config_ts - 2 # Prevent immediate update. # Is it time to open a new tunnel? for ts in tunnel_ts: if now_ts == ts: if tunnel == None: logger.info("Opening new tunnel.") tunnel = subprocess.Popen(["ssh", "-R", str(local_port) + ":localhost:22", "-N", "-p", str(ssh_port), ssh_host]) signal.alarm(tunnel_open_time) else: logger.warning("Tunnel already running.") time.sleep(10)
Example JSON file:
{ "ConfigUpdate" : ["17:00"], "TunnelOpen" : ["18:00","19:00","20:00"] }