From ed280d5ac360e2af796e9bd973d7b4df89f0c449 Mon Sep 17 00:00:00 2001 From: "Jeremy T. Bouse" Date: Fri, 27 Nov 2009 16:20:12 -0500 Subject: Imported Upstream version 1.7.4 --- demos/demo.py | 180 +++++++++++++++++++++++++++++++++++++++++++++++++ demos/demo_server.py | 146 +++++++++++++++++++++++++++++++++++++++ demos/demo_sftp.py | 119 ++++++++++++++++++++++++++++++++ demos/demo_simple.py | 101 +++++++++++++++++++++++++++ demos/forward.py | 180 +++++++++++++++++++++++++++++++++++++++++++++++++ demos/interactive.py | 97 ++++++++++++++++++++++++++ demos/rforward.py | 167 +++++++++++++++++++++++++++++++++++++++++++++ demos/test_rsa.key | 15 +++++ demos/user_rsa_key | 15 +++++ demos/user_rsa_key.pub | 1 + 10 files changed, 1021 insertions(+) create mode 100755 demos/demo.py create mode 100755 demos/demo_server.py create mode 100755 demos/demo_sftp.py create mode 100755 demos/demo_simple.py create mode 100755 demos/forward.py create mode 100644 demos/interactive.py create mode 100755 demos/rforward.py create mode 100644 demos/test_rsa.key create mode 100644 demos/user_rsa_key create mode 100644 demos/user_rsa_key.pub (limited to 'demos') diff --git a/demos/demo.py b/demos/demo.py new file mode 100755 index 0000000..c8346ce --- /dev/null +++ b/demos/demo.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python + +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +import base64 +from binascii import hexlify +import getpass +import os +import select +import socket +import sys +import threading +import time +import traceback + +import paramiko +import interactive + + +def agent_auth(transport, username): + """ + Attempt to authenticate to the given transport using any of the private + keys available from an SSH agent. + """ + + agent = paramiko.Agent() + agent_keys = agent.get_keys() + if len(agent_keys) == 0: + return + + for key in agent_keys: + print 'Trying ssh-agent key %s' % hexlify(key.get_fingerprint()), + try: + transport.auth_publickey(username, key) + print '... success!' + return + except paramiko.SSHException: + print '... nope.' + + +def manual_auth(username, hostname): + default_auth = 'p' + auth = raw_input('Auth by (p)assword, (r)sa key, or (d)ss key? [%s] ' % default_auth) + if len(auth) == 0: + auth = default_auth + + if auth == 'r': + default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa') + path = raw_input('RSA key [%s]: ' % default_path) + if len(path) == 0: + path = default_path + try: + key = paramiko.RSAKey.from_private_key_file(path) + except paramiko.PasswordRequiredException: + password = getpass.getpass('RSA key password: ') + key = paramiko.RSAKey.from_private_key_file(path, password) + t.auth_publickey(username, key) + elif auth == 'd': + default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_dsa') + path = raw_input('DSS key [%s]: ' % default_path) + if len(path) == 0: + path = default_path + try: + key = paramiko.DSSKey.from_private_key_file(path) + except paramiko.PasswordRequiredException: + password = getpass.getpass('DSS key password: ') + key = paramiko.DSSKey.from_private_key_file(path, password) + t.auth_publickey(username, key) + else: + pw = getpass.getpass('Password for %s@%s: ' % (username, hostname)) + t.auth_password(username, pw) + + +# setup logging +paramiko.util.log_to_file('demo.log') + +username = '' +if len(sys.argv) > 1: + hostname = sys.argv[1] + if hostname.find('@') >= 0: + username, hostname = hostname.split('@') +else: + hostname = raw_input('Hostname: ') +if len(hostname) == 0: + print '*** Hostname required.' + sys.exit(1) +port = 22 +if hostname.find(':') >= 0: + hostname, portstr = hostname.split(':') + port = int(portstr) + +# now connect +try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((hostname, port)) +except Exception, e: + print '*** Connect failed: ' + str(e) + traceback.print_exc() + sys.exit(1) + +try: + t = paramiko.Transport(sock) + try: + t.start_client() + except paramiko.SSHException: + print '*** SSH negotiation failed.' + sys.exit(1) + + try: + keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts')) + except IOError: + try: + keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts')) + except IOError: + print '*** Unable to open host keys file' + keys = {} + + # check server's host key -- this is important. + key = t.get_remote_server_key() + if not keys.has_key(hostname): + print '*** WARNING: Unknown host key!' + elif not keys[hostname].has_key(key.get_name()): + print '*** WARNING: Unknown host key!' + elif keys[hostname][key.get_name()] != key: + print '*** WARNING: Host key has changed!!!' + sys.exit(1) + else: + print '*** Host key OK.' + + # get username + if username == '': + default_username = getpass.getuser() + username = raw_input('Username [%s]: ' % default_username) + if len(username) == 0: + username = default_username + + agent_auth(t, username) + if not t.is_authenticated(): + manual_auth(username, hostname) + if not t.is_authenticated(): + print '*** Authentication failed. :(' + t.close() + sys.exit(1) + + chan = t.open_session() + chan.get_pty() + chan.invoke_shell() + print '*** Here we go!' + print + interactive.interactive_shell(chan) + chan.close() + t.close() + +except Exception, e: + print '*** Caught exception: ' + str(e.__class__) + ': ' + str(e) + traceback.print_exc() + try: + t.close() + except: + pass + sys.exit(1) + + diff --git a/demos/demo_server.py b/demos/demo_server.py new file mode 100755 index 0000000..e7b0e61 --- /dev/null +++ b/demos/demo_server.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python + +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +import base64 +from binascii import hexlify +import os +import socket +import sys +import threading +import traceback + +import paramiko + + +# setup logging +paramiko.util.log_to_file('demo_server.log') + +host_key = paramiko.RSAKey(filename='test_rsa.key') +#host_key = paramiko.DSSKey(filename='test_dss.key') + +print 'Read key: ' + hexlify(host_key.get_fingerprint()) + + +class Server (paramiko.ServerInterface): + # 'data' is the output of base64.encodestring(str(key)) + # (using the "user_rsa_key" files) + data = 'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp' + \ + 'fAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMC' + \ + 'KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT' + \ + 'UWT10hcuO4Ks8=' + good_pub_key = paramiko.RSAKey(data=base64.decodestring(data)) + + def __init__(self): + self.event = threading.Event() + + def check_channel_request(self, kind, chanid): + if kind == 'session': + return paramiko.OPEN_SUCCEEDED + return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED + + def check_auth_password(self, username, password): + if (username == 'robey') and (password == 'foo'): + return paramiko.AUTH_SUCCESSFUL + return paramiko.AUTH_FAILED + + def check_auth_publickey(self, username, key): + print 'Auth attempt with key: ' + hexlify(key.get_fingerprint()) + if (username == 'robey') and (key == self.good_pub_key): + return paramiko.AUTH_SUCCESSFUL + return paramiko.AUTH_FAILED + + def get_allowed_auths(self, username): + return 'password,publickey' + + def check_channel_shell_request(self, channel): + self.event.set() + return True + + def check_channel_pty_request(self, channel, term, width, height, pixelwidth, + pixelheight, modes): + return True + + +# now connect +try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(('', 2200)) +except Exception, e: + print '*** Bind failed: ' + str(e) + traceback.print_exc() + sys.exit(1) + +try: + sock.listen(100) + print 'Listening for connection ...' + client, addr = sock.accept() +except Exception, e: + print '*** Listen/accept failed: ' + str(e) + traceback.print_exc() + sys.exit(1) + +print 'Got a connection!' + +try: + t = paramiko.Transport(client) + try: + t.load_server_moduli() + except: + print '(Failed to load moduli -- gex will be unsupported.)' + raise + t.add_server_key(host_key) + server = Server() + try: + t.start_server(server=server) + except paramiko.SSHException, x: + print '*** SSH negotiation failed.' + sys.exit(1) + + # wait for auth + chan = t.accept(20) + if chan is None: + print '*** No channel.' + sys.exit(1) + print 'Authenticated!' + + server.event.wait(10) + if not server.event.isSet(): + print '*** Client never asked for a shell.' + sys.exit(1) + + chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') + chan.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') + chan.send('Happy birthday to Robot Dave!\r\n\r\n') + chan.send('Username: ') + f = chan.makefile('rU') + username = f.readline().strip('\r\n') + chan.send('\r\nI don\'t like you, ' + username + '.\r\n') + chan.close() + +except Exception, e: + print '*** Caught exception: ' + str(e.__class__) + ': ' + str(e) + traceback.print_exc() + try: + t.close() + except: + pass + sys.exit(1) + diff --git a/demos/demo_sftp.py b/demos/demo_sftp.py new file mode 100755 index 0000000..a823fb3 --- /dev/null +++ b/demos/demo_sftp.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python + +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +# based on code provided by raymond mosteller (thanks!) + +import base64 +import getpass +import os +import socket +import sys +import traceback + +import paramiko + + +# setup logging +paramiko.util.log_to_file('demo_sftp.log') + +# get hostname +username = '' +if len(sys.argv) > 1: + hostname = sys.argv[1] + if hostname.find('@') >= 0: + username, hostname = hostname.split('@') +else: + hostname = raw_input('Hostname: ') +if len(hostname) == 0: + print '*** Hostname required.' + sys.exit(1) +port = 22 +if hostname.find(':') >= 0: + hostname, portstr = hostname.split(':') + port = int(portstr) + + +# get username +if username == '': + default_username = getpass.getuser() + username = raw_input('Username [%s]: ' % default_username) + if len(username) == 0: + username = default_username +password = getpass.getpass('Password for %s@%s: ' % (username, hostname)) + + +# get host key, if we know one +hostkeytype = None +hostkey = None +try: + host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts')) +except IOError: + try: + # try ~/ssh/ too, because windows can't have a folder named ~/.ssh/ + host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts')) + except IOError: + print '*** Unable to open host keys file' + host_keys = {} + +if host_keys.has_key(hostname): + hostkeytype = host_keys[hostname].keys()[0] + hostkey = host_keys[hostname][hostkeytype] + print 'Using host key of type %s' % hostkeytype + + +# now, connect and use paramiko Transport to negotiate SSH2 across the connection +try: + t = paramiko.Transport((hostname, port)) + t.connect(username=username, password=password, hostkey=hostkey) + sftp = paramiko.SFTPClient.from_transport(t) + + # dirlist on remote host + dirlist = sftp.listdir('.') + print "Dirlist:", dirlist + + # copy this demo onto the server + try: + sftp.mkdir("demo_sftp_folder") + except IOError: + print '(assuming demo_sftp_folder/ already exists)' + sftp.open('demo_sftp_folder/README', 'w').write('This was created by demo_sftp.py.\n') + data = open('demo_sftp.py', 'r').read() + sftp.open('demo_sftp_folder/demo_sftp.py', 'w').write(data) + print 'created demo_sftp_folder/ on the server' + + # copy the README back here + data = sftp.open('demo_sftp_folder/README', 'r').read() + open('README_demo_sftp', 'w').write(data) + print 'copied README back here' + + # BETTER: use the get() and put() methods + sftp.put('demo_sftp.py', 'demo_sftp_folder/demo_sftp.py') + sftp.get('demo_sftp_folder/README', 'README_demo_sftp') + + t.close() + +except Exception, e: + print '*** Caught exception: %s: %s' % (e.__class__, e) + traceback.print_exc() + try: + t.close() + except: + pass + sys.exit(1) diff --git a/demos/demo_simple.py b/demos/demo_simple.py new file mode 100755 index 0000000..5bcc214 --- /dev/null +++ b/demos/demo_simple.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python + +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +import base64 +import getpass +import os +import socket +import sys +import traceback + +import paramiko +import interactive + + +# setup logging +paramiko.util.log_to_file('demo_simple.log') + +# get hostname +username = '' +if len(sys.argv) > 1: + hostname = sys.argv[1] + if hostname.find('@') >= 0: + username, hostname = hostname.split('@') +else: + hostname = raw_input('Hostname: ') +if len(hostname) == 0: + print '*** Hostname required.' + sys.exit(1) +port = 22 +if hostname.find(':') >= 0: + hostname, portstr = hostname.split(':') + port = int(portstr) + + +# get username +if username == '': + default_username = getpass.getuser() + username = raw_input('Username [%s]: ' % default_username) + if len(username) == 0: + username = default_username +password = getpass.getpass('Password for %s@%s: ' % (username, hostname)) + + +# get host key, if we know one +hostkeytype = None +hostkey = None +try: + host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts')) +except IOError: + try: + # try ~/ssh/ too, because windows can't have a folder named ~/.ssh/ + host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts')) + except IOError: + print '*** Unable to open host keys file' + host_keys = {} + +if host_keys.has_key(hostname): + hostkeytype = host_keys[hostname].keys()[0] + hostkey = host_keys[hostname][hostkeytype] + print 'Using host key of type %s' % hostkeytype + + +# now, connect and use paramiko Transport to negotiate SSH2 across the connection +try: + t = paramiko.Transport((hostname, port)) + t.connect(username=username, password=password, hostkey=hostkey) + chan = t.open_session() + chan.get_pty() + chan.invoke_shell() + print '*** Here we go!' + print + interactive.interactive_shell(chan) + chan.close() + t.close() + +except Exception, e: + print '*** Caught exception: %s: %s' % (e.__class__, e) + traceback.print_exc() + try: + t.close() + except: + pass + sys.exit(1) diff --git a/demos/forward.py b/demos/forward.py new file mode 100755 index 0000000..33c98ab --- /dev/null +++ b/demos/forward.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python + +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Sample script showing how to do local port forwarding over paramiko. + +This script connects to the requested SSH server and sets up local port +forwarding (the openssh -L option) from a local port through a tunneled +connection to a destination reachable from the SSH server machine. +""" + +import getpass +import os +import socket +import select +import SocketServer +import sys +from optparse import OptionParser + +import paramiko + +SSH_PORT = 22 +DEFAULT_PORT = 4000 + +g_verbose = True + + +class ForwardServer (SocketServer.ThreadingTCPServer): + daemon_threads = True + allow_reuse_address = True + + +class Handler (SocketServer.BaseRequestHandler): + + def handle(self): + try: + chan = self.ssh_transport.open_channel('direct-tcpip', + (self.chain_host, self.chain_port), + self.request.getpeername()) + except Exception, e: + verbose('Incoming request to %s:%d failed: %s' % (self.chain_host, + self.chain_port, + repr(e))) + return + if chan is None: + verbose('Incoming request to %s:%d was rejected by the SSH server.' % + (self.chain_host, self.chain_port)) + return + + verbose('Connected! Tunnel open %r -> %r -> %r' % (self.request.getpeername(), + chan.getpeername(), (self.chain_host, self.chain_port))) + while True: + r, w, x = select.select([self.request, chan], [], []) + if self.request in r: + data = self.request.recv(1024) + if len(data) == 0: + break + chan.send(data) + if chan in r: + data = chan.recv(1024) + if len(data) == 0: + break + self.request.send(data) + chan.close() + self.request.close() + verbose('Tunnel closed from %r' % (self.request.getpeername(),)) + + +def forward_tunnel(local_port, remote_host, remote_port, transport): + # this is a little convoluted, but lets me configure things for the Handler + # object. (SocketServer doesn't give Handlers any way to access the outer + # server normally.) + class SubHander (Handler): + chain_host = remote_host + chain_port = remote_port + ssh_transport = transport + ForwardServer(('', local_port), SubHander).serve_forever() + + +def verbose(s): + if g_verbose: + print s + + +HELP = """\ +Set up a forward tunnel across an SSH server, using paramiko. A local port +(given with -p) is forwarded across an SSH session to an address:port from +the SSH server. This is similar to the openssh -L option. +""" + + +def get_host_port(spec, default_port): + "parse 'hostname:22' into a host and port, with the port optional" + args = (spec.split(':', 1) + [default_port])[:2] + args[1] = int(args[1]) + return args[0], args[1] + + +def parse_options(): + global g_verbose + + parser = OptionParser(usage='usage: %prog [options] [:]', + version='%prog 1.0', description=HELP) + parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True, + help='squelch all informational output') + parser.add_option('-p', '--local-port', action='store', type='int', dest='port', + default=DEFAULT_PORT, + help='local port to forward (default: %d)' % DEFAULT_PORT) + parser.add_option('-u', '--user', action='store', type='string', dest='user', + default=getpass.getuser(), + help='username for SSH authentication (default: %s)' % getpass.getuser()) + parser.add_option('-K', '--key', action='store', type='string', dest='keyfile', + default=None, + help='private key file to use for SSH authentication') + parser.add_option('', '--no-key', action='store_false', dest='look_for_keys', default=True, + help='don\'t look for or use a private key file') + parser.add_option('-P', '--password', action='store_true', dest='readpass', default=False, + help='read password (for key or password auth) from stdin') + parser.add_option('-r', '--remote', action='store', type='string', dest='remote', default=None, metavar='host:port', + help='remote host and port to forward to') + options, args = parser.parse_args() + + if len(args) != 1: + parser.error('Incorrect number of arguments.') + if options.remote is None: + parser.error('Remote address required (-r).') + + g_verbose = options.verbose + server_host, server_port = get_host_port(args[0], SSH_PORT) + remote_host, remote_port = get_host_port(options.remote, SSH_PORT) + return options, (server_host, server_port), (remote_host, remote_port) + + +def main(): + options, server, remote = parse_options() + + password = None + if options.readpass: + password = getpass.getpass('Enter SSH password: ') + + client = paramiko.SSHClient() + client.load_system_host_keys() + client.set_missing_host_key_policy(paramiko.WarningPolicy()) + + verbose('Connecting to ssh host %s:%d ...' % (server[0], server[1])) + try: + client.connect(server[0], server[1], username=options.user, key_filename=options.keyfile, + look_for_keys=options.look_for_keys, password=password) + except Exception, e: + print '*** Failed to connect to %s:%d: %r' % (server[0], server[1], e) + sys.exit(1) + + verbose('Now forwarding port %d to %s:%d ...' % (options.port, remote[0], remote[1])) + + try: + forward_tunnel(options.port, remote[0], remote[1], client.get_transport()) + except KeyboardInterrupt: + print 'C-c: Port forwarding stopped.' + sys.exit(0) + + +if __name__ == '__main__': + main() diff --git a/demos/interactive.py b/demos/interactive.py new file mode 100644 index 0000000..b80e661 --- /dev/null +++ b/demos/interactive.py @@ -0,0 +1,97 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +import socket +import sys + +# windows does not have termios... +try: + import termios + import tty + has_termios = True +except ImportError: + has_termios = False + + +def interactive_shell(chan): + if has_termios: + posix_shell(chan) + else: + windows_shell(chan) + + +def posix_shell(chan): + import select + + oldtty = termios.tcgetattr(sys.stdin) + try: + tty.setraw(sys.stdin.fileno()) + tty.setcbreak(sys.stdin.fileno()) + chan.settimeout(0.0) + + while True: + r, w, e = select.select([chan, sys.stdin], [], []) + if chan in r: + try: + x = chan.recv(1024) + if len(x) == 0: + print '\r\n*** EOF\r\n', + break + sys.stdout.write(x) + sys.stdout.flush() + except socket.timeout: + pass + if sys.stdin in r: + x = sys.stdin.read(1) + if len(x) == 0: + break + chan.send(x) + + finally: + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty) + + +# thanks to Mike Looijmans for this code +def windows_shell(chan): + import threading + + sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n") + + def writeall(sock): + while True: + data = sock.recv(256) + if not data: + sys.stdout.write('\r\n*** EOF ***\r\n\r\n') + sys.stdout.flush() + break + sys.stdout.write(data) + sys.stdout.flush() + + writer = threading.Thread(target=writeall, args=(chan,)) + writer.start() + + try: + while True: + d = sys.stdin.read(1) + if not d: + break + chan.send(d) + except EOFError: + # user hit ^Z or F6 + pass diff --git a/demos/rforward.py b/demos/rforward.py new file mode 100755 index 0000000..810fea6 --- /dev/null +++ b/demos/rforward.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python + +# Copyright (C) 2008 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Sample script showing how to do remote port forwarding over paramiko. + +This script connects to the requested SSH server and sets up remote port +forwarding (the openssh -R option) from a remote port through a tunneled +connection to a destination reachable from the local machine. +""" + +import getpass +import os +import socket +import select +import sys +import threading +from optparse import OptionParser + +import paramiko + +SSH_PORT = 22 +DEFAULT_PORT = 4000 + +g_verbose = True + + +def handler(chan, host, port): + sock = socket.socket() + try: + sock.connect((host, port)) + except Exception, e: + verbose('Forwarding request to %s:%d failed: %r' % (host, port, e)) + return + + verbose('Connected! Tunnel open %r -> %r -> %r' % (chan.origin_addr, + chan.getpeername(), (host, port))) + while True: + r, w, x = select.select([sock, chan], [], []) + if sock in r: + data = sock.recv(1024) + if len(data) == 0: + break + chan.send(data) + if chan in r: + data = chan.recv(1024) + if len(data) == 0: + break + sock.send(data) + chan.close() + sock.close() + verbose('Tunnel closed from %r' % (chan.origin_addr,)) + + +def reverse_forward_tunnel(server_port, remote_host, remote_port, transport): + transport.request_port_forward('', server_port) + while True: + chan = transport.accept(1000) + if chan is None: + continue + thr = threading.Thread(target=handler, args=(chan, remote_host, remote_port)) + thr.setDaemon(True) + thr.start() + + +def verbose(s): + if g_verbose: + print s + + +HELP = """\ +Set up a reverse forwarding tunnel across an SSH server, using paramiko. A +port on the SSH server (given with -p) is forwarded across an SSH session +back to the local machine, and out to a remote site reachable from this +network. This is similar to the openssh -R option. +""" + + +def get_host_port(spec, default_port): + "parse 'hostname:22' into a host and port, with the port optional" + args = (spec.split(':', 1) + [default_port])[:2] + args[1] = int(args[1]) + return args[0], args[1] + + +def parse_options(): + global g_verbose + + parser = OptionParser(usage='usage: %prog [options] [:]', + version='%prog 1.0', description=HELP) + parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True, + help='squelch all informational output') + parser.add_option('-p', '--remote-port', action='store', type='int', dest='port', + default=DEFAULT_PORT, + help='port on server to forward (default: %d)' % DEFAULT_PORT) + parser.add_option('-u', '--user', action='store', type='string', dest='user', + default=getpass.getuser(), + help='username for SSH authentication (default: %s)' % getpass.getuser()) + parser.add_option('-K', '--key', action='store', type='string', dest='keyfile', + default=None, + help='private key file to use for SSH authentication') + parser.add_option('', '--no-key', action='store_false', dest='look_for_keys', default=True, + help='don\'t look for or use a private key file') + parser.add_option('-P', '--password', action='store_true', dest='readpass', default=False, + help='read password (for key or password auth) from stdin') + parser.add_option('-r', '--remote', action='store', type='string', dest='remote', default=None, metavar='host:port', + help='remote host and port to forward to') + options, args = parser.parse_args() + + if len(args) != 1: + parser.error('Incorrect number of arguments.') + if options.remote is None: + parser.error('Remote address required (-r).') + + g_verbose = options.verbose + server_host, server_port = get_host_port(args[0], SSH_PORT) + remote_host, remote_port = get_host_port(options.remote, SSH_PORT) + return options, (server_host, server_port), (remote_host, remote_port) + + +def main(): + options, server, remote = parse_options() + + password = None + if options.readpass: + password = getpass.getpass('Enter SSH password: ') + + client = paramiko.SSHClient() + client.load_system_host_keys() + client.set_missing_host_key_policy(paramiko.WarningPolicy()) + + verbose('Connecting to ssh host %s:%d ...' % (server[0], server[1])) + try: + client.connect(server[0], server[1], username=options.user, key_filename=options.keyfile, + look_for_keys=options.look_for_keys, password=password) + except Exception, e: + print '*** Failed to connect to %s:%d: %r' % (server[0], server[1], e) + sys.exit(1) + + verbose('Now forwarding remote port %d to %s:%d ...' % (options.port, remote[0], remote[1])) + + try: + reverse_forward_tunnel(options.port, remote[0], remote[1], client.get_transport()) + except KeyboardInterrupt: + print 'C-c: Port forwarding stopped.' + sys.exit(0) + + +if __name__ == '__main__': + main() diff --git a/demos/test_rsa.key b/demos/test_rsa.key new file mode 100644 index 0000000..f50e9c5 --- /dev/null +++ b/demos/test_rsa.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz +oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/ +d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB +gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0 +EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon +soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H +tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU +avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA +4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g +H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv +qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV +HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc +nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7 +-----END RSA PRIVATE KEY----- diff --git a/demos/user_rsa_key b/demos/user_rsa_key new file mode 100644 index 0000000..ee64f23 --- /dev/null +++ b/demos/user_rsa_key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDI7iK3d8eWYZlYloat94c5VjtFY7c/0zuGl8C7uMnZ3t6i2G99 +66hEW0nCFSZkOW5F0XKEVj+EUCHvo8koYC6wiohAqWQnEwIoOoh7GSAcB8gP/qaq ++adIl/Rvlby/mHakj+y05LBND6nFWHAn1y1gOFFKUXSJNRZPXSFy47gqzwIBIwKB +gQCbANjz7q/pCXZLp1Hz6tYHqOvlEmjK1iabB1oqafrMpJ0eibUX/u+FMHq6StR5 +M5413BaDWHokPdEJUnabfWXXR3SMlBUKrck0eAer1O8m78yxu3OEdpRk+znVo4DL +guMeCdJB/qcF0kEsx+Q8HP42MZU1oCmk3PbfXNFwaHbWuwJBAOQ/ry/hLD7AqB8x +DmCM82A9E59ICNNlHOhxpJoh6nrNTPCsBAEu/SmqrL8mS6gmbRKUaya5Lx1pkxj2 +s/kWOokCQQDhXCcYXjjWiIfxhl6Rlgkk1vmI0l6785XSJNv4P7pXjGmShXfIzroh +S8uWK3tL0GELY7+UAKDTUEVjjQdGxYSXAkEA3bo1JzKCwJ3lJZ1ebGuqmADRO6UP +40xH977aadfN1mEI6cusHmgpISl0nG5YH7BMsvaT+bs1FUH8m+hXDzoqOwJBAK3Z +X/za+KV/REya2z0b+GzgWhkXUGUa/owrEBdHGriQ47osclkUgPUdNqcLmaDilAF4 +1Z4PHPrI5RJIONAx+JECQQC/fChqjBgFpk6iJ+BOdSexQpgfxH/u/457W10Y43HR +soS+8btbHqjQkowQ/2NTlUfWvqIlfxs6ZbFsIp/HrhZL +-----END RSA PRIVATE KEY----- diff --git a/demos/user_rsa_key.pub b/demos/user_rsa_key.pub new file mode 100644 index 0000000..ac722f1 --- /dev/null +++ b/demos/user_rsa_key.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hpfAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMCKDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iTUWT10hcuO4Ks8= robey@ralph.lag.net -- cgit v1.2.3