def _init_session(self, retries=1): logger.debug("Starting new session for %s@%s:%s", self.user, self.host, self.port) self.session = Session() self.session.options_set(options.USER, self.user) self.session.options_set(options.HOST, self.host) self.session.options_set_port(self.port) if self.gssapi_server_identity: self.session.options_set(options.GSSAPI_SERVER_IDENTITY, self.gssapi_server_identity) if self.gssapi_client_identity: self.session.options_set(options.GSSAPI_CLIENT_IDENTITY, self.gssapi_client_identity) if self.gssapi_client_identity or self.gssapi_server_identity: self.session.options_set_gssapi_delegate_credentials( self.gssapi_delegate_credentials) self.session.set_socket(self.sock) logger.debug("Session started, connecting with existing socket") try: self._session_connect() except Exception as ex: if retries < self.num_retries: return self._connect_init_session_retry(retries=retries + 1) msg = "Error connecting to host %s:%s - %s" logger.error(msg, self.host, self.port, ex) ex.host = self.host ex.port = self.port raise ex
def __init__(self, host, username, port, key_file=None, debug=False): """Initialize the connection manager. :param host: IP or Domain to connect to. :type host: String :param username: Username for the connection. :type username: String :param port: Port number used to connect to the remote server. :type port: Int :param key_file: SSH key file used to connect. :type key_file: String :param debug: Enable or disable debug mode :type debug: Boolean """ self.log = logger.getLogger(name="directord-ssh", debug_logging=debug) self.key_file = key_file self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((host, port)) self.session = Session() self.session.options_set(options.HOST, host) self.session.options_set(options.USER, username) self.session.options_set_port(port) self.session.set_socket(self.sock) self.session.connect() self.log.debug("Handshake with [ %s ] on port [ %s ] complete.", host, port) self.channels = dict() self.host = host self.username = username self.key_file = key_file
class SSHTestCase(unittest.TestCase): @classmethod def sign_cert(cls): cmd = [ 'ssh-keygen', '-s', CA_USER_KEY, '-n', USER, '-I', 'tests', USER_CERT_PUB_KEY, ] subprocess.check_call(cmd) @classmethod def setUpClass(cls): _mask = int('0600') if version_info <= (2, ) else 0o600 for _file in [PKEY_FILENAME, USER_CERT_PRIV_KEY, CA_USER_KEY]: os.chmod(_file, _mask) cls.sign_cert() cls.server = OpenSSHServer() cls.server.start_server() @classmethod def tearDownClass(cls): cls.server.stop() del cls.server def setUp(self): self.host = '127.0.0.1' self.port = 2222 self.cmd = 'echo me' self.resp = u'me' self.user_key = PKEY_FILENAME self.user_pub_key = PUB_FILE self.user_ca_key = USER_CERT_PRIV_KEY self.user_cert_file = USER_CERT_FILE self.user = USER sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host, self.port)) self.sock = sock self.session = Session() self.session.options_set(options.HOST, self.host) self.session.options_set_port(self.port) self.session.options_set(options.USER, self.user) self.session.set_socket(sock) self.pkey = import_privkey_file(self.user_key) # self.session.options_set(options.LOG_VERBOSITY, '1') def tearDown(self): del self.session def _auth(self): self.assertEqual(self.session.connect(), 0) self.assertEqual(self.session.userauth_publickey(self.pkey), 0)
def test_socket_connect(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host, self.port)) session = Session() session.options_set(options.USER, self.user) session.options_set(options.HOST, self.host) session.options_set_port(self.port) self.assertEqual(session.set_socket(sock), 0) self.assertEqual(session.connect(), 0) self.assertRaises(RequestDenied, session.userauth_none) self.assertEqual(session.userauth_publickey(self.pkey), 0)
def setUp(self): self.host = '127.0.0.1' self.port = 2222 self.cmd = 'echo me' self.resp = u'me' self.user_key = PKEY_FILENAME self.user_pub_key = PUB_FILE self.user = pwd.getpwuid(os.geteuid()).pw_name # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # sock.connect((self.host, self.port)) # self.sock = sock self.session = Session() self.session.options_set(options.HOST, self.host) self.session.options_set_port(self.port) self.session.options_set(options.USER, self.user) self.pkey = import_privkey_file(self.user_key)
def setUp(self): self.host = '127.0.0.1' self.port = 2222 self.cmd = 'echo me' self.resp = u'me' self.user_key = PKEY_FILENAME self.user_pub_key = PUB_FILE self.user_ca_key = USER_CERT_PRIV_KEY self.user_cert_file = USER_CERT_FILE self.user = USER sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host, self.port)) self.sock = sock self.session = Session() self.session.options_set(options.HOST, self.host) self.session.options_set_port(self.port) self.session.options_set(options.USER, self.user) self.session.set_socket(sock) self.pkey = import_privkey_file(self.user_key)
def _init(self, retries=1): logger.debug("Starting new session for %s@%s:%s", self.user, self.host, self.port) self.session = Session() self.session.options_set(options.USER, self.user) self.session.options_set(options.HOST, self.host) self.session.options_set_port(self.port) if self.gssapi_server_identity: self.session.options_set(options.GSSAPI_SERVER_IDENTITY, self.gssapi_server_identity) if self.gssapi_client_identity: self.session.options_set(options.GSSAPI_CLIENT_IDENTITY, self.gssapi_client_identity) if self.gssapi_client_identity or self.gssapi_server_identity: self.session.options_set_gssapi_delegate_credentials( self.gssapi_delegate_credentials) self.session.set_socket(self.sock) logger.debug("Session started, connecting with existing socket") try: self.session.connect() except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Error connecting to host %s:%s - %s" logger.error(msg, self.host, self.port, ex) ex.host = self.host ex.port = self.port raise ex try: self.auth() except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Authentication error while connecting to %s:%s - %s" ex = AuthenticationException(msg, self.host, self.port, ex) ex.host = self.host ex.port = self.port raise ex logger.debug("Authentication completed successfully - " "setting session to non-blocking mode") self.session.set_blocking(0)
class SSHTestCase(unittest.TestCase): @classmethod def setUpClass(cls): _mask = int('0600') if version_info <= (2,) else 0o600 os.chmod(PKEY_FILENAME, _mask) cls.server = OpenSSHServer() cls.server.start_server() @classmethod def tearDownClass(cls): cls.server.stop() del cls.server def setUp(self): self.host = '127.0.0.1' self.port = 2222 self.cmd = 'echo me' self.resp = u'me' self.user_key = PKEY_FILENAME self.user_pub_key = PUB_FILE self.user = pwd.getpwuid(os.geteuid()).pw_name sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host, self.port)) self.sock = sock self.session = Session() self.session.options_set(options.HOST, self.host) self.session.options_set_port(self.port) self.session.options_set(options.USER, self.user) self.session.set_socket(sock) self.pkey = import_privkey_file(self.user_key) # self.session.options_set(options.LOG_VERBOSITY, '1') def tearDown(self): del self.session def _auth(self): self.assertEqual(self.session.connect(), 0) self.assertEqual( self.session.userauth_publickey(self.pkey), 0)
def go(): while True: try: s = Session() s.options_set(options.HOST, HOST) s.connect() sleep(2147483647) except Exception as e: global count print(str(e) + ': died #' + str(count)) if 'Connection reset' in str(e): break count += 1 pass
class SSHClient(BaseSSHClient): """ssh-python based non-blocking client.""" def __init__(self, host, user=None, password=None, port=None, pkey=None, cert_file=None, num_retries=DEFAULT_RETRIES, retry_delay=RETRY_DELAY, allow_agent=True, timeout=None, identity_auth=True, gssapi_auth=False, gssapi_server_identity=None, gssapi_client_identity=None, gssapi_delegate_credentials=False, _auth_thread_pool=True): """:param host: Host name or IP to connect to. :type host: str :param user: User to connect as. Defaults to logged in user. :type user: str :param password: Password to use for password authentication. :type password: str :param port: SSH port to connect to. Defaults to SSH default (22) :type port: int :param pkey: Private key file path to use for authentication. Path must be either absolute path or relative to user home directory like ``~/<path>``. :type pkey: str :param cert_file: Public key signed certificate file to use for authentication. The corresponding private key must also be provided via ``pkey`` parameter. For example ``pkey='id_rsa',cert_file='id_rsa-cert.pub'`` for RSA signed certificate. Path must be absolute or relative to user home directory. :type cert_file: str :param num_retries: (Optional) Number of connection and authentication attempts before the client gives up. Defaults to 3. :type num_retries: int :param retry_delay: Number of seconds to wait between retries. Defaults to :py:class:`pssh.constants.RETRY_DELAY` :type retry_delay: int :param timeout: (Optional) If provided, all commands will timeout after <timeout> number of seconds. :type timeout: int :param allow_agent: (Optional) set to False to disable connecting to the system's SSH agent. Currently unused. :type allow_agent: bool :param identity_auth: (Optional) set to False to disable attempting to authenticate with default identity files from `pssh.clients.base_ssh_client.BaseSSHClient.IDENTITIES` :type identity_auth: bool :param gssapi_server_identity: Enable GSS-API authentication. Uses GSS-MIC key exchange. Enabled if either gssapi_server_identity or gssapi_client_identity are provided. :type gssapi_auth: bool :type gssapi_server_identity: str :param gssapi_server_identity: Set GSSAPI server identity. :type gssapi_server_identity: str :param gssapi_client_identity: Set GSSAPI client identity. :type gssapi_client_identity: str :param gssapi_delegate_credentials: Enable/disable server credentials delegation. :type gssapi_delegate_credentials: bool :raises: :py:class:`pssh.exceptions.PKeyFileError` on errors finding provided private key. """ self.cert_file = _validate_pkey_path(cert_file, host) self.gssapi_auth = gssapi_auth self.gssapi_server_identity = gssapi_server_identity self.gssapi_client_identity = gssapi_client_identity self.gssapi_delegate_credentials = gssapi_delegate_credentials super(SSHClient, self).__init__(host, user=user, password=password, port=port, pkey=pkey, num_retries=num_retries, retry_delay=retry_delay, allow_agent=allow_agent, _auth_thread_pool=_auth_thread_pool, timeout=timeout, identity_auth=identity_auth) def disconnect(self): """Close socket if needed.""" if self.sock is not None and not self.sock.closed: self.sock.close() def _agent_auth(self): self.session.userauth_agent(self.user) def _keepalive(self): pass def _init_session(self, retries=1): logger.debug("Starting new session for %s@%s:%s", self.user, self.host, self.port) self.session = Session() self.session.options_set(options.USER, self.user) self.session.options_set(options.HOST, self.host) self.session.options_set_port(self.port) if self.gssapi_server_identity: self.session.options_set(options.GSSAPI_SERVER_IDENTITY, self.gssapi_server_identity) if self.gssapi_client_identity: self.session.options_set(options.GSSAPI_CLIENT_IDENTITY, self.gssapi_client_identity) if self.gssapi_client_identity or self.gssapi_server_identity: self.session.options_set_gssapi_delegate_credentials( self.gssapi_delegate_credentials) self.session.set_socket(self.sock) logger.debug("Session started, connecting with existing socket") try: self._session_connect() except Exception as ex: if retries < self.num_retries: return self._connect_init_session_retry(retries=retries + 1) msg = "Error connecting to host %s:%s - %s" logger.error(msg, self.host, self.port, ex) ex.host = self.host ex.port = self.port raise ex def _session_connect(self): self.session.connect() def auth(self): if self.gssapi_auth or (self.gssapi_server_identity or self.gssapi_client_identity): try: return self.session.userauth_gssapi() except Exception as ex: logger.error( "GSSAPI authentication with server id %s and client id %s failed - %s", self.gssapi_server_identity, self.gssapi_client_identity, ex) return super(SSHClient, self).auth() def _password_auth(self): self.session.userauth_password(self.user, self.password) def _pkey_auth(self, pkey_file, password=None): pkey = import_privkey_file( pkey_file, passphrase=password if password is not None else '') if self.cert_file is not None: logger.debug( "Certificate file set - trying certificate authentication") self._import_cert_file(pkey) self.session.userauth_publickey(pkey) def _import_cert_file(self, pkey): cert_key = import_cert_file(self.cert_file) self.session.userauth_try_publickey(cert_key) copy_cert_to_privkey(cert_key, pkey) logger.debug("Imported certificate file %s for pkey %s", self.cert_file, self.pkey) def _shell(self, channel): return self._eagain(channel.request_shell) def _open_session(self): channel = self.session.channel_new() channel.set_blocking(0) self._eagain(channel.open_session) return channel def open_session(self): """Open new channel from session.""" logger.debug("Opening new channel on %s", self.host) try: channel = self._open_session() except Exception as ex: raise SessionError(ex) return channel def _make_output_readers(self, channel, stdout_buffer, stderr_buffer): _stdout_reader = spawn(self._read_output_to_buffer, channel, stdout_buffer) _stderr_reader = spawn(self._read_output_to_buffer, channel, stderr_buffer, is_stderr=True) return _stdout_reader, _stderr_reader def execute(self, cmd, use_pty=False, channel=None): """Execute command on remote host. :param cmd: The command string to execute. :type cmd: str :param use_pty: Whether or not to request a PTY on the channel executing command. :type use_pty: bool :param channel: Channel to use. New channel is created if not provided. :type channel: :py:class:`ssh.channel.Channel`""" channel = self.open_session() if not channel else channel if use_pty: self._eagain(channel.request_pty, timeout=self.timeout) logger.debug("Executing command '%s'", cmd) self._eagain(channel.request_exec, cmd, timeout=self.timeout) return channel def _read_output_to_buffer(self, channel, _buffer, is_stderr=False): while True: self.poll(timeout=self.timeout) try: size, data = channel.read_nonblocking(is_stderr=is_stderr) except EOF: _buffer.eof.set() sleep(.1) return if size > 0: _buffer.write(data) else: # Yield event loop to other greenlets if we have no data to # send back, meaning the generator does not yield and can there # for block other generators/greenlets from running. sleep(.1) def wait_finished(self, host_output, timeout=None): """Wait for EOF from channel and close channel. Used to wait for remote command completion and be able to gather exit code. :param host_output: Host output of command to wait for. :type host_output: :py:class:`pssh.output.HostOutput` :param timeout: Timeout value in seconds - defaults to no timeout. :type timeout: float :raises: :py:class:`pssh.exceptions.Timeout` after <timeout> seconds if timeout set. """ if not isinstance(host_output, HostOutput): raise ValueError("%s is not a HostOutput object" % (host_output, )) channel = host_output.channel if channel is None: return logger.debug("Sending EOF on channel %s", channel) self._eagain(channel.send_eof, timeout=self.timeout) logger.debug("Waiting for readers, timeout %s", timeout) with GTimeout(seconds=timeout, exception=Timeout): joinall((host_output.buffers.stdout.reader, host_output.buffers.stderr.reader)) logger.debug("Readers finished, closing channel") self.close_channel(channel) def finished(self, channel): """Checks if remote command has finished - has server sent client EOF. :rtype: bool """ if channel is None: return return channel.is_eof() def get_exit_status(self, channel): """Get exit status code for channel or ``None`` if not ready. :param channel: The channel to get status from. :type channel: :py:mod:`ssh.channel.Channel` :rtype: int or ``None`` """ if not channel.is_eof(): return return channel.get_exit_status() def close_channel(self, channel): """Close channel. :param channel: The channel to close. :type channel: :py:class:`ssh.channel.Channel` """ logger.debug("Closing channel") self._eagain(channel.close, timeout=self.timeout) def poll(self, timeout=None): """ssh-python based co-operative gevent poll on session socket.""" self._poll_errcodes( self.session.get_poll_flags, SSH_READ_PENDING, SSH_WRITE_PENDING, timeout=timeout, ) def _eagain(self, func, *args, **kwargs): """Run function given and handle EAGAIN for an ssh-python session""" return self._eagain_errcode(func, SSH_AGAIN, *args, **kwargs) def _eagain_write(self, write_func, data, timeout=None): return self._eagain_write_errcode(write_func, data, SSH_AGAIN, timeout=timeout)
class SSHConnect: """Context manager to remotely connect to servers using libssh. The connection manager requires an SSH key to be defined, and exist, however, upon enter the system will use the SSH agent is defined. """ def __init__(self, host, username, port, key_file=None, debug=False): """Initialize the connection manager. :param host: IP or Domain to connect to. :type host: String :param username: Username for the connection. :type username: String :param port: Port number used to connect to the remote server. :type port: Int :param key_file: SSH key file used to connect. :type key_file: String :param debug: Enable or disable debug mode :type debug: Boolean """ self.log = logger.getLogger(name="directord-ssh", debug_logging=debug) self.key_file = key_file self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((host, port)) self.session = Session() self.session.options_set(options.HOST, host) self.session.options_set(options.USER, username) self.session.options_set_port(port) self.session.set_socket(self.sock) self.session.connect() self.log.debug("Handshake with [ %s ] on port [ %s ] complete.", host, port) self.channels = dict() self.host = host self.username = username self.key_file = key_file def _userauth_publickey_fromfile(self, key_file): """Import a private key file. :param key_file: Fully qualified path to an ssh key file. :type key_file: String """ key = ssh_key.import_privkey_file(key_file) self.session.userauth_publickey(key) def set_auth(self): """Set the ssh session auth.""" if self.key_file: self._userauth_publickey_fromfile(key_file=self.key_file) self.log.debug("Key file [ %s ] added", self.key_file) else: try: self.session.userauth_agent(self.username) self.log.debug("User agent based authentication enabled") except Exception as e: self.log.warning( "SSH Agent connection has failed: %s." " Attempting to connect with the user's implicit ssh key.", str(e), ) home = os.path.abspath(os.path.expanduser("~")) default_keyfile = os.path.join(home, ".ssh/id_rsa") if os.path.exists(default_keyfile): self._userauth_publickey_fromfile(key_file=default_keyfile) self.log.debug("Implicit key file [ %s ] added", self.key_file) else: self.log.critical( "No implicit key found [ %s ]. Setup user-agent" " authentication or use the --key-file" " argument to explicitly set the required" " ssh key.", default_keyfile, ) raise SystemExit("Authentication failure") def __enter__(self): """Connect to the remote node and return the ssh and session objects. :returns: Tuple """ self.set_auth() return self def __exit__(self, *args, **kwargs): """Upon exit, close the ssh connection.""" for key, value in self.channels.items(): if hasattr(value, "close"): value.close() self.log.debug("%s channel is closed.", key) self.session.disconnect() self.log.debug("SSH session is closed.")
import os import pwd import socket from ssh.session import Session from ssh import options # Linux only USERNAME = pwd.getpwuid(os.geteuid()).pw_name HOST = 'localhost' PORT = 22 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((HOST, PORT)) s = Session() s.options_set(options.HOST, HOST) s.options_set(options.USER, USERNAME) s.options_set_port(PORT) s.set_socket(sock) s.connect() # Authenticate with agent s.userauth_agent(USERNAME) chan = s.channel_new() chan.open_session() chan.request_exec('echo me') size, data = chan.read() while size > 0: print(data.strip())
class SSHClient(BaseSSHClient): """ssh-python based non-blocking client.""" def __init__(self, host, user=None, password=None, port=None, pkey=None, num_retries=DEFAULT_RETRIES, retry_delay=RETRY_DELAY, allow_agent=True, timeout=None, identity_auth=True, gssapi_auth=False, gssapi_server_identity=None, gssapi_client_identity=None, gssapi_delegate_credentials=False, _auth_thread_pool=True): """:param host: Host name or IP to connect to. :type host: str :param user: User to connect as. Defaults to logged in user. :type user: str :param password: Password to use for password authentication. :type password: str :param port: SSH port to connect to. Defaults to SSH default (22) :type port: int :param pkey: Private key file path to use for authentication. Path must be either absolute path or relative to user home directory like ``~/<path>``. :type pkey: str :param num_retries: (Optional) Number of connection and authentication attempts before the client gives up. Defaults to 3. :type num_retries: int :param retry_delay: Number of seconds to wait between retries. Defaults to :py:class:`pssh.constants.RETRY_DELAY` :type retry_delay: int :param timeout: (Optional) If provided, all commands will timeout after <timeout> number of seconds. :type timeout: int :param allow_agent: (Optional) set to False to disable connecting to the system's SSH agent. Currently unused. :type allow_agent: bool :param identity_auth: (Optional) set to False to disable attempting to authenticate with default identity files from `pssh.clients.base_ssh_client.BaseSSHClient.IDENTITIES` :type identity_auth: bool :param gssapi_server_identity: Enable GSS-API authentication. Uses GSS-MIC key exchange. Enabled if either gssapi_server_identity or gssapi_client_identity are provided. :type gssapi_auth: bool :type gssapi_server_identity: str :param gssapi_server_identity: Set GSSAPI server identity. :type gssapi_server_identity: str :param gssapi_client_identity: Set GSSAPI client identity. :type gssapi_client_identity: str :param gssapi_delegate_credentials: Enable/disable server credentials delegation. :type gssapi_delegate_credentials: bool :raises: :py:class:`pssh.exceptions.PKeyFileError` on errors finding provided private key. """ self.gssapi_auth = gssapi_auth self.gssapi_server_identity = gssapi_server_identity self.gssapi_client_identity = gssapi_client_identity self.gssapi_delegate_credentials = gssapi_delegate_credentials super(SSHClient, self).__init__(host, user=user, password=password, port=port, pkey=pkey, num_retries=num_retries, retry_delay=retry_delay, allow_agent=allow_agent, _auth_thread_pool=_auth_thread_pool, timeout=timeout, identity_auth=identity_auth) self._stdout_buffer = BytesIO() self._stderr_buffer = BytesIO() self._stdout_reader = None self._stderr_reader = None self._stdout_read = False self._stderr_read = False def disconnect(self): """Close socket if needed.""" if self.sock is not None and not self.sock.closed: logger.debug("Closing socket") self.sock.close() def _init(self, retries=1): logger.debug("Starting new session for %s@%s:%s", self.user, self.host, self.port) self.session = Session() self.session.options_set(options.USER, self.user) self.session.options_set(options.HOST, self.host) self.session.options_set_port(self.port) if self.gssapi_server_identity: self.session.options_set(options.GSSAPI_SERVER_IDENTITY, self.gssapi_server_identity) if self.gssapi_client_identity: self.session.options_set(options.GSSAPI_CLIENT_IDENTITY, self.gssapi_client_identity) if self.gssapi_client_identity or self.gssapi_server_identity: self.session.options_set_gssapi_delegate_credentials( self.gssapi_delegate_credentials) self.session.set_socket(self.sock) logger.debug("Session started, connecting with existing socket") try: self.session.connect() except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Error connecting to host %s:%s - %s" logger.error(msg, self.host, self.port, ex) ex.host = self.host ex.port = self.port raise ex try: self.auth() except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Authentication error while connecting to %s:%s - %s" ex = AuthenticationException(msg, self.host, self.port, ex) ex.host = self.host ex.port = self.port raise ex logger.debug("Authentication completed successfully - " "setting session to non-blocking mode") self.session.set_blocking(0) def auth(self): if self.pkey is not None: logger.debug("Proceeding with private key file authentication") return self._pkey_auth(self.pkey, self.password) if self.allow_agent: try: self.session.userauth_agent(self.user) except Exception as ex: logger.debug( "Agent auth failed with %s, " "continuing with other authentication methods", ex) else: logger.debug("Authentication with SSH Agent succeeded.") return if self.gssapi_auth or (self.gssapi_server_identity or self.gssapi_client_identity): try: self.session.userauth_gssapi() except Exception as ex: logger.error( "GSSAPI authentication with server id %s and client id %s failed - %s", self.gssapi_server_identity, self.gssapi_client_identity, ex) if self.identity_auth: try: self._identity_auth() except AuthenticationException: if self.password is None: raise logger.debug("Private key auth failed, trying password") self._password_auth() def _password_auth(self): if not self.password: raise AuthenticationException("All authentication methods failed") try: self.session.userauth_password(self.password) except Exception as ex: raise AuthenticationException( "Password authentication failed - %s", ex) def _pkey_auth(self, pkey, password=None): password = b'' if not password else password pkey = import_privkey_file(pkey, passphrase=password) self.session.userauth_publickey(pkey) def open_session(self): """Open new channel from session.""" logger.debug("Opening new channel on %s", self.host) try: channel = self.session.channel_new() while channel == SSH_AGAIN: wait_select(self.session, timeout=self.timeout) channel = self.session.channel_new() logger.debug("Channel %s created, opening session", channel) channel.set_blocking(0) while channel.open_session() == SSH_AGAIN: logger.debug( "Channel open session blocked, waiting on socket..") wait_select(self.session, timeout=self.timeout) # Select on open session can dead lock without # yielding event loop sleep(.1) except Exception as ex: raise SessionError(ex) return channel def execute(self, cmd, use_pty=False, channel=None): """Execute command on remote host. :param cmd: The command string to execute. :type cmd: str :param use_pty: Whether or not to request a PTY on the channel executing command. :type use_pty: bool :param channel: Channel to use. New channel is created if not provided. :type channel: :py:class:`ssh.channel.Channel`""" channel = self.open_session() if not channel else channel if use_pty: eagain(self.session, channel.request_pty, timeout=self.timeout) eagain(self.session, channel.request_exec, cmd, timeout=self.timeout) self._stderr_read = False self._stdout_read = False self._stdout_buffer = BytesIO() self._stderr_buffer = BytesIO() self._stdout_reader = spawn(self._read_output_to_buffer, channel) self._stderr_reader = spawn(self._read_output_to_buffer, channel, is_stderr=True) self._stdout_reader.start() self._stderr_reader.start() return channel def read_stderr(self, channel, timeout=None): """Read standard error buffer from channel. Returns a generator of line by line output. :param channel: Channel to read output from. :type channel: :py:class:`ssh2.channel.Channel` :rtype: generator """ _buffer_name = 'stderr' _buffer = self._stderr_buffer _flag = self._stderr_read _reader = self._stderr_reader return self._read_output(_buffer, _buffer_name, _flag, _reader, channel, timeout=timeout, is_stderr=True) def read_output(self, channel, timeout=None, is_stderr=False): """Read standard output buffer from channel. Returns a generator of line by line output. :param channel: Channel to read output from. :type channel: :py:class:`ssh2.channel.Channel` :rtype: generator """ _buffer_name = 'stdout' _buffer = self._stdout_buffer _flag = self._stdout_read _reader = self._stdout_reader return self._read_output(_buffer, _buffer_name, _flag, _reader, channel, timeout=timeout) def _read_output(self, _buffer, _buffer_name, _flag, _reader, channel, timeout=None, is_stderr=False): if _flag is True: logger.debug("Output for %s has already been read", _buffer_name) raise StopIteration logger.debug("Waiting for %s reader", _buffer_name) timeout = timeout if timeout else self.timeout try: _reader.get(timeout=timeout) except GeventTimeout as ex: raise Timeout(ex) if _buffer.getvalue() == '': logger.debug("Reader finished and output empty for %s", _buffer_name) raise StopIteration logger.debug("Reading from %s buffer", _buffer_name) for line in _buffer.getvalue().splitlines(): yield line if is_stderr: self._stderr_read = True else: self._stdout_read = True def _read_output_to_buffer(self, channel, is_stderr=False): _buffer_name = 'stderr' if is_stderr else 'stdout' _buffer = self._stderr_buffer if is_stderr else self._stdout_buffer logger.debug("Starting output generator on channel %s for %s", channel, _buffer_name) while True: wait_select(self.session, timeout=self.timeout) try: size, data = channel.read_nonblocking(is_stderr=is_stderr) except EOF: logger.debug( "Channel is at EOF trying to read %s - " "reader exiting", _buffer_name) sleep(.1) return if size > 0: logger.debug("Writing %s bytes to %s buffer", size, _buffer_name) _buffer.write(data) else: # Yield event loop to other greenlets if we have no data to # send back, meaning the generator does not yield and can there # for block other generators/greenlets from running. logger.debug("No data for %s, waiting", _buffer_name) sleep(.1) def wait_finished(self, channel, timeout=None): """Wait for EOF from channel and close channel. Used to wait for remote command completion and be able to gather exit code. :param channel: The channel to use. :type channel: :py:class:`ssh.channel.Channel` """ if channel is None: return timeout = timeout if timeout else self.timeout logger.debug("Sending EOF on channel %s", channel) eagain(self.session, channel.send_eof, timeout=timeout) try: self._stdout_reader.get(timeout=timeout) self._stderr_reader.get(timeout=timeout) except GeventTimeout as ex: logger.debug("Timed out waiting for readers..") raise Timeout(ex) else: logger.debug("Readers finished, closing channel") # Close channel self.close_channel(channel) def finished(self, channel): """Checks if remote command has finished - has server sent client EOF. :rtype: bool """ if channel is None: return return channel.is_eof() def get_exit_status(self, channel): """Get exit status from channel if ready else return `None`. :rtype: int or `None` """ if not channel.is_eof(): return return channel.get_exit_status() def close_channel(self, channel): """Close channel. :param channel: The channel to close. :type channel: :py:class:`ssh.channel.Channel` """ logger.debug("Closing channel") eagain(self.session, channel.close, timeout=self.timeout)
def test_should_not_segfault(self): session = Session() self.assertEqual(session.get_error(), '') self.assertRaises(InvalidAPIUse, session.userauth_none) self.assertRaises(InvalidAPIUse, session.userauth_publickey, self.pkey) key = import_pubkey_file(self.user_pub_key) self.assertRaises(InvalidAPIUse, session.userauth_try_publickey, key) self.assertRaises(InvalidAPIUse, session.userauth_publickey_auto, '') self.assertRaises(InvalidAPIUse, session.channel_new) self.assertRaises(InvalidAPIUse, session.get_disconnect_message) self.assertRaises(InvalidAPIUse, session.get_issue_banner) self.assertRaises(InvalidAPIUse, session.get_openssh_version) self.assertIsNone(session.dump_knownhost()) self.assertIsNone(session.get_clientbanner()) self.assertIsNone(session.get_serverbanner()) self.assertIsNone(session.get_kex_algo()) self.assertIsNone(session.get_cipher_in()) self.assertIsNone(session.get_cipher_out()) self.assertIsNone(session.get_hmac_in()) self.assertIsNone(session.get_hmac_out()) self.assertIsNotNone(session.get_error_code()) session.connector_new()
def test_non_blocking_connect(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host, self.port)) session = Session() session.options_set(options.USER, self.user) session.options_set(options.HOST, self.host) session.options_set_port(self.port) self.assertEqual(session.set_socket(sock), 0) session.set_blocking(0) rc = session.connect() while rc == SSH_AGAIN: wait_socket(session, sock) rc = session.connect() self.assertEqual(rc, 0) rc = session.userauth_publickey(self.pkey) while rc == SSH_AUTH_AGAIN: wait_socket(session, sock) rc = session.userauth_publickey(self.pkey) self.assertEqual(rc, 0)
def test_set_timeout(self): session = Session() self.assertEqual(session.options_set(options.TIMEOUT, "1000"), 0) self.assertEqual(session.options_set(options.TIMEOUT_USEC, "1000"), 0) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host, self.port)) session = Session() session.options_set(options.USER, self.user) session.options_set(options.HOST, self.host) session.options_set_port(self.port) self.assertEqual(session.set_socket(sock), 0) self.assertEqual(session.options_set(options.TIMEOUT, "1000"), 0) self.assertEqual(session.options_set(options.TIMEOUT_USEC, "1000"), 0)