Skip to content

Instantly share code, notes, and snippets.

@gdamjan
Created May 2, 2012 16:43
Show Gist options
  • Save gdamjan/2578080 to your computer and use it in GitHub Desktop.
Save gdamjan/2578080 to your computer and use it in GitHub Desktop.
Reverse ssh tunnel - playground
#! /usr/bin/env python2
import subprocess
import random
import string
import re
import sys
import tempfile
port_re = re.compile(r'^Allocated port (\d+) for remote')
def client(server, user, local_port, ssh_port='22'):
SSH = 'ssh'
if sys.platform == 'win32':
SSH = 'plink.exe'
cmd = ['ssh', '-T', server, '-l', user, '-p', ssh_port,
'-R 0:localhost:%s' % local_port]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# the ssh daemon sends the client this message, about the port it allocated
# on the daemon side. we can't get that info easily in the server script so
# just send it over from client side.
msg = p.stderr.readline()
p.stdin.write(msg)
# now we wait for an url allocation
url = p.stdout.readline()
print url.strip()
# and now just wait forever
p.wait()
def gen_slug(length=6):
'''Generate a random combination of letters and digits'''
return ''.join(random.sample(string.lowercase + string.digits, length))
#
# nginx server config template. takes two parameters the server name
# and the port (allocated by sshd)
#
# the main config will need to have something like:
# include /etc/nginx/dynamic-redirects/*.conf;
#
NGINX_CONF_DIR = '/etc/nginx/dynamic-redirects'
NGINX_TEMPLATE ='''\
server {
listen [::]:80;
listen 80;
server_name %(host)s;
location / {
proxy_pass http://localhost:%(port)s/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
proxy_read_timeout 3600;
}
}
'''
def server(base_name):
# get port from client
# configure nginx
# send url back
msg = sys.stdin.readline()
port = port_re.match(msg).group(1)
slug = gen_slug()
host = slug + '.' + base_name
try:
fp = tempfile.NamedTemporaryFile(suffix='.conf', dir=NGINX_CONF_DIR)
fp.write(NGINX_TEMPLATE % {'host':host, 'port':port})
fp.flush()
subprocess.call(['nginx', '-s', 'reload'])
sys.stdout.write(host + '\r\n')
sys.stdout.flush()
# wait forever
sys.stdin.read()
finaly:
del fp
# at this point the config file is also deleted
subprocess.call(['nginx', '-s', 'reload'])
def main():
if sys.argv[1] == '-s':
server(sys.argv[2])
else:
client(*sys.argv[1:])
if __name__ == '__main__':
main()
#
# A sshd configuration to allow a user with no password
# and a forced command (this script).
#
# the `tunnel` user doesn't have a password
#
'''\
Match User tunnel
ForceCommand /usr/local/bin/reverse-ssh-tunnel.py -s example.com
GatewayPorts yes
AllowTcpForwarding yes
PasswordAuthentication yes
PermitEmptyPasswords yes
'''
@gdamjan
Copy link
Author

gdamjan commented May 3, 2012

On the server side I could also use the uWSGI fastrouter. It would simplify the need to sudo reload nginx since uwsgi can reload it's own config files when they are changed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment