diff options
Diffstat (limited to 'demos')
-rwxr-xr-x | demos/demo.py | 187 | ||||
-rwxr-xr-x | demos/demo_keygen.py | 174 | ||||
-rw-r--r-- | demos/demo_server.py | 185 | ||||
-rw-r--r-- | demos/demo_sftp.py | 143 | ||||
-rw-r--r-- | demos/demo_simple.py | 116 | ||||
-rw-r--r-- | demos/forward.py | 257 | ||||
-rw-r--r-- | demos/interactive.py | 101 | ||||
-rwxr-xr-x | demos/rforward.py | 230 | ||||
-rw-r--r-- | demos/test_rsa.key | 15 | ||||
-rw-r--r-- | demos/user_rsa_key | 15 | ||||
-rw-r--r-- | demos/user_rsa_key.pub | 1 |
11 files changed, 1424 insertions, 0 deletions
diff --git a/demos/demo.py b/demos/demo.py new file mode 100755 index 0000000..5252db7 --- /dev/null +++ b/demos/demo.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python + +# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> +# +# 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 distributed 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +import base64 +from binascii import hexlify +import getpass +import os +import select +import socket +import sys +import time +import traceback +from paramiko.py3compat import input + +import paramiko + +try: + import interactive +except ImportError: + from . 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 = 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 = 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 = 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 = 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 as 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 hostname not in keys: + print("*** WARNING: Unknown host key!") + elif key.get_name() not in keys[hostname]: + 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 = 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!\n") + interactive.interactive_shell(chan) + chan.close() + t.close() + +except Exception as 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_keygen.py b/demos/demo_keygen.py new file mode 100755 index 0000000..12637ed --- /dev/null +++ b/demos/demo_keygen.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python + +# Copyright (C) 2010 Sofian Brabez <sbz@6dev.net> +# +# 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 distributed 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import sys + +from binascii import hexlify +from optparse import OptionParser + +from paramiko import DSSKey +from paramiko import RSAKey +from paramiko.ssh_exception import SSHException +from paramiko.py3compat import u + +usage = """ +%prog [-v] [-b bits] -t type [-N new_passphrase] [-f output_keyfile]""" + +default_values = { + "ktype": "dsa", + "bits": 1024, + "filename": "output", + "comment": "", +} + +key_dispatch_table = {"dsa": DSSKey, "rsa": RSAKey} + + +def progress(arg=None): + + if not arg: + sys.stdout.write("0%\x08\x08\x08 ") + sys.stdout.flush() + elif arg[0] == "p": + sys.stdout.write("25%\x08\x08\x08\x08 ") + sys.stdout.flush() + elif arg[0] == "h": + sys.stdout.write("50%\x08\x08\x08\x08 ") + sys.stdout.flush() + elif arg[0] == "x": + sys.stdout.write("75%\x08\x08\x08\x08 ") + sys.stdout.flush() + + +if __name__ == "__main__": + + phrase = None + pfunc = None + + parser = OptionParser(usage=usage) + parser.add_option( + "-t", + "--type", + type="string", + dest="ktype", + help="Specify type of key to create (dsa or rsa)", + metavar="ktype", + default=default_values["ktype"], + ) + parser.add_option( + "-b", + "--bits", + type="int", + dest="bits", + help="Number of bits in the key to create", + metavar="bits", + default=default_values["bits"], + ) + parser.add_option( + "-N", + "--new-passphrase", + dest="newphrase", + help="Provide new passphrase", + metavar="phrase", + ) + parser.add_option( + "-P", + "--old-passphrase", + dest="oldphrase", + help="Provide old passphrase", + metavar="phrase", + ) + parser.add_option( + "-f", + "--filename", + type="string", + dest="filename", + help="Filename of the key file", + metavar="filename", + default=default_values["filename"], + ) + parser.add_option( + "-q", "--quiet", default=False, action="store_false", help="Quiet" + ) + parser.add_option( + "-v", "--verbose", default=False, action="store_true", help="Verbose" + ) + parser.add_option( + "-C", + "--comment", + type="string", + dest="comment", + help="Provide a new comment", + metavar="comment", + default=default_values["comment"], + ) + + (options, args) = parser.parse_args() + + if len(sys.argv) == 1: + parser.print_help() + sys.exit(0) + + for o in list(default_values.keys()): + globals()[o] = getattr(options, o, default_values[o.lower()]) + + if options.newphrase: + phrase = getattr(options, "newphrase") + + if options.verbose: + pfunc = progress + sys.stdout.write( + "Generating priv/pub %s %d bits key pair (%s/%s.pub)..." + % (ktype, bits, filename, filename) + ) + sys.stdout.flush() + + if ktype == "dsa" and bits > 1024: + raise SSHException("DSA Keys must be 1024 bits") + + if ktype not in key_dispatch_table: + raise SSHException( + "Unknown %s algorithm to generate keys pair" % ktype + ) + + # generating private key + prv = key_dispatch_table[ktype].generate(bits=bits, progress_func=pfunc) + prv.write_private_key_file(filename, password=phrase) + + # generating public key + pub = key_dispatch_table[ktype](filename=filename, password=phrase) + with open("%s.pub" % filename, "w") as f: + f.write("%s %s" % (pub.get_name(), pub.get_base64())) + if options.comment: + f.write(" %s" % comment) + + if options.verbose: + print("done.") + + hash = u(hexlify(pub.get_fingerprint())) + print( + "Fingerprint: %d %s %s.pub (%s)" + % ( + bits, + ":".join([hash[i : 2 + i] for i in range(0, len(hash), 2)]), + filename, + ktype.upper(), + ) + ) diff --git a/demos/demo_server.py b/demos/demo_server.py new file mode 100644 index 0000000..6cb2dc5 --- /dev/null +++ b/demos/demo_server.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python + +# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> +# +# 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 distributed 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import base64 +from binascii import hexlify +import os +import socket +import sys +import threading +import traceback + +import paramiko +from paramiko.py3compat import b, u, decodebytes + + +# 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: " + u(hexlify(host_key.get_fingerprint()))) + + +class Server(paramiko.ServerInterface): + # 'data' is the output of base64.b64encode(key) + # (using the "user_rsa_key" files) + data = ( + b"AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp" + b"fAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMC" + b"KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT" + b"UWT10hcuO4Ks8=" + ) + good_pub_key = paramiko.RSAKey(data=decodebytes(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: " + u(hexlify(key.get_fingerprint()))) + if (username == "robey") and (key == self.good_pub_key): + return paramiko.AUTH_SUCCESSFUL + return paramiko.AUTH_FAILED + + def check_auth_gssapi_with_mic( + self, username, gss_authenticated=paramiko.AUTH_FAILED, cc_file=None + ): + """ + .. note:: + We are just checking in `AuthHandler` that the given user is a + valid krb5 principal! We don't check if the krb5 principal is + allowed to log in on the server, because there is no way to do that + in python. So if you develop your own SSH server with paramiko for + a certain platform like Linux, you should call ``krb5_kuserok()`` in + your local kerberos library to make sure that the krb5_principal + has an account on the server and is allowed to log in as a user. + + .. seealso:: + `krb5_kuserok() man page + <http://www.unix.com/man-page/all/3/krb5_kuserok/>`_ + """ + if gss_authenticated == paramiko.AUTH_SUCCESSFUL: + return paramiko.AUTH_SUCCESSFUL + return paramiko.AUTH_FAILED + + def check_auth_gssapi_keyex( + self, username, gss_authenticated=paramiko.AUTH_FAILED, cc_file=None + ): + if gss_authenticated == paramiko.AUTH_SUCCESSFUL: + return paramiko.AUTH_SUCCESSFUL + return paramiko.AUTH_FAILED + + def enable_auth_gssapi(self): + return True + + def get_allowed_auths(self, username): + return "gssapi-keyex,gssapi-with-mic,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 + + +DoGSSAPIKeyExchange = 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 as 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 as e: + print("*** Listen/accept failed: " + str(e)) + traceback.print_exc() + sys.exit(1) + +print("Got a connection!") + +try: + t = paramiko.Transport(client, gss_kex=DoGSSAPIKeyExchange) + t.set_gss_host(socket.getfqdn("")) + 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: + 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.is_set(): + 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 as 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 100644 index 0000000..dbcb2cb --- /dev/null +++ b/demos/demo_sftp.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python + +# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> +# +# 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 distributed 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# based on code provided by raymond mosteller (thanks!) + +import base64 +import getpass +import os +import socket +import sys +import traceback + +import paramiko +from paramiko.py3compat import input + + +# setup logging +paramiko.util.log_to_file("demo_sftp.log") + +# Paramiko client configuration +UseGSSAPI = True # enable GSS-API / SSPI authentication +DoGSSAPIKeyExchange = True +Port = 22 + +# get hostname +username = "" +if len(sys.argv) > 1: + hostname = sys.argv[1] + if hostname.find("@") >= 0: + username, hostname = hostname.split("@") +else: + hostname = input("Hostname: ") +if len(hostname) == 0: + print("*** Hostname required.") + sys.exit(1) + +if hostname.find(":") >= 0: + hostname, portstr = hostname.split(":") + Port = int(portstr) + + +# get username +if username == "": + default_username = getpass.getuser() + username = input("Username [%s]: " % default_username) + if len(username) == 0: + username = default_username +if not UseGSSAPI: + password = getpass.getpass("Password for %s@%s: " % (username, hostname)) +else: + password = None + + +# 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 hostname in host_keys: + 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( + hostkey, + username, + password, + gss_host=socket.getfqdn(hostname), + gss_auth=UseGSSAPI, + gss_kex=DoGSSAPIKeyExchange, + ) + sftp = paramiko.SFTPClient.from_transport(t) + + # dirlist on remote host + dirlist = sftp.listdir(".") + print("Dirlist: %s" % dirlist) + + # copy this demo onto the server + try: + sftp.mkdir("demo_sftp_folder") + except IOError: + print("(assuming demo_sftp_folder/ already exists)") + with sftp.open("demo_sftp_folder/README", "w") as f: + f.write("This was created by demo_sftp.py.\n") + with open("demo_sftp.py", "r") as f: + data = f.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 + with sftp.open("demo_sftp_folder/README", "r") as f: + data = f.read() + with open("README_demo_sftp", "w") as f: + f.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 as 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 100644 index 0000000..bd932c3 --- /dev/null +++ b/demos/demo_simple.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python + +# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> +# +# 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 distributed 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +import base64 +import getpass +import os +import socket +import sys +import traceback +from paramiko.py3compat import input + +import paramiko + +try: + import interactive +except ImportError: + from . import interactive + + +# setup logging +paramiko.util.log_to_file("demo_simple.log") +# Paramiko client configuration +UseGSSAPI = ( + paramiko.GSS_AUTH_AVAILABLE +) # enable "gssapi-with-mic" authentication, if supported by your python installation +DoGSSAPIKeyExchange = ( + paramiko.GSS_AUTH_AVAILABLE +) # enable "gssapi-kex" key exchange, if supported by your python installation +# UseGSSAPI = False +# DoGSSAPIKeyExchange = False +port = 22 + +# get hostname +username = "" +if len(sys.argv) > 1: + hostname = sys.argv[1] + if hostname.find("@") >= 0: + username, hostname = hostname.split("@") +else: + hostname = input("Hostname: ") +if len(hostname) == 0: + print("*** Hostname required.") + sys.exit(1) + +if hostname.find(":") >= 0: + hostname, portstr = hostname.split(":") + port = int(portstr) + + +# get username +if username == "": + default_username = getpass.getuser() + username = input("Username [%s]: " % default_username) + if len(username) == 0: + username = default_username +if not UseGSSAPI and not DoGSSAPIKeyExchange: + password = getpass.getpass("Password for %s@%s: " % (username, hostname)) + + +# now, connect and use paramiko Client to negotiate SSH2 across the connection +try: + client = paramiko.SSHClient() + client.load_system_host_keys() + client.set_missing_host_key_policy(paramiko.WarningPolicy()) + print("*** Connecting...") + if not UseGSSAPI and not DoGSSAPIKeyExchange: + client.connect(hostname, port, username, password) + else: + try: + client.connect( + hostname, + port, + username, + gss_auth=UseGSSAPI, + gss_kex=DoGSSAPIKeyExchange, + ) + except Exception: + # traceback.print_exc() + password = getpass.getpass( + "Password for %s@%s: " % (username, hostname) + ) + client.connect(hostname, port, username, password) + + chan = client.invoke_shell() + print(repr(client.get_transport())) + print("*** Here we go!\n") + interactive.interactive_shell(chan) + chan.close() + client.close() + +except Exception as e: + print("*** Caught exception: %s: %s" % (e.__class__, e)) + traceback.print_exc() + try: + client.close() + except: + pass + sys.exit(1) diff --git a/demos/forward.py b/demos/forward.py new file mode 100644 index 0000000..869e390 --- /dev/null +++ b/demos/forward.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python + +# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> +# +# 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 distributed 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 + +try: + import SocketServer +except ImportError: + import socketserver as 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 as 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) + + peername = self.request.getpeername() + chan.close() + self.request.close() + verbose("Tunnel closed from %r" % (peername,)) + + +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] <ssh-server>[:<server-port>]", + 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 as 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..16eae0e --- /dev/null +++ b/demos/interactive.py @@ -0,0 +1,101 @@ +# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> +# +# 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 distributed 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +import socket +import sys +from paramiko.py3compat import u + +# 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 = u(chan.recv(1024)) + if len(x) == 0: + sys.stdout.write("\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..200634a --- /dev/null +++ b/demos/rforward.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python + +# Copyright (C) 2008 Robey Pointer <robeypointer@gmail.com> +# +# 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 distributed 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 as 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] <ssh-server>[:<server-port>]", + 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 as 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 |