def test_BadHostKeyException(self): got_key = RSAKey.generate(2048) wanted_key = RSAKey.generate(2048) exc = BadHostKeyException("myhost", got_key, wanted_key) expected = "Host key for server 'myhost' does not match: got '{}', expected '{}'" # noqa assert str(exc) == expected.format(got_key.get_base64(), wanted_key.get_base64())
def test_open_connection_badhostkey_exception(self, mock_rsa, sshClient): got_key = pkey.PKey("sadad") expected_key = pkey.PKey("sadadaa") sshClient.return_value.connect = Mock( side_effect=BadHostKeyException( self.hostname, got_key, expected_key)) configurator = ssh_utilities.RemoteConnection( self.hostname, user=self.user, passwd=self.passwd, priv_key_file=self.priv_key_file) self.assertRaises(BadHostKeyException, configurator.open_connection)
def connect(self, ssh=None): """Connects the ssh instance. If :param:`ssh` is not provided will connect `self.ssh`. """ ssh = ssh if ssh else self.ssh ssh.load_system_host_keys() if self.trusted_host: ssh.set_missing_host_key_policy(AutoAddPolicy()) while True: try: ssh.connect( self.hostname, username=self.remote_user, timeout=self.timeout, compress=True, ) print('Connected to host {}. '.format(self.hostname)) break except (socket.timeout, ConnectionRefusedError) as e: print('{}. {} for {}@{}. Retrying ...'.format( timezone.now(), str(e), self.remote_user, self.hostname)) time.sleep(5) except AuthenticationException as e: raise AuthenticationException('Got {} for user {}@{}'.format( str(e)[0:-1], self.remote_user, self.hostname)) except BadHostKeyException as e: raise BadHostKeyException( 'Add server to known_hosts on host {}.' ' Got {}.'.format(e, self.hostname)) except socket.gaierror: raise socket.gaierror( 'Hostname {} not known or not available'.format( self.hostname)) except ConnectionResetError as e: raise ConnectionResetError('{} for {}@{}'.format( str(e), self.remote_user, self.hostname)) except SSHException as e: raise SSHException('{} for {}@{}'.format( str(e), self.remote_user, self.hostname))
def _paramiko_connect(self): # Find supported address families. addrinfo = socket.getaddrinfo(self.host, self.port) for family, socktype, proto, canonname, sockaddr in addrinfo: af = family addr = sockaddr if socktype == socket.SOCK_STREAM: break # Open a socket. sock = socket.socket(af, socket.SOCK_STREAM) try: sock.settimeout(self.connect_timeout or None) except: pass sock.connect(addr) # Init the paramiko protocol. t = paramiko.Transport(sock) t.banner_timeout = self.banner_timeout t.start_client() ResourceManager.register(self, t) # Check system host keys. server_key = t.get_remote_server_key() keytype = server_key.get_name() our_server_key = self._system_host_keys.get(self.host, {}).get(keytype, None) if our_server_key is None: our_server_key = self._host_keys.get(self.host, {}).get(keytype, None) if our_server_key is None: self._missing_host_key(server_key) # if the callback returns, assume the key is ok our_server_key = server_key if server_key != our_server_key: raise BadHostKeyException(self.host, server_key, our_server_key) t.set_keepalive(self.KEEPALIVE_INTERVAL) return t
def test_execute_command_invokes_ssh_identity_possibly_changed_notification( self): """ When a BadHostKeyException, THEN we send "ssh_identity_possibly_changed" notification """ ssh = mock.Mock() ssh.execute_command.side_effect = BadHostKeyException( 'test', mock.Mock(), mock.Mock()) notifier = mock.Mock() node = Node({'healthchecks': []}, {}, ssh) node.set_notifier(notifier) try: node.execute_command('curl https://iwa-ait.org') except BadHostKeyException: notifier.ssh_identity_possibly_changed.assert_called_once() return self.fail('BadHostKeyException not thrown')
def connect( self, hostname, port=SSH_PORT, username=None, password=None, pkey=None, key_filename=None, timeout=None, allow_agent=True, look_for_keys=True, compress=False, sock=None, gss_auth=False, gss_kex=False, gss_deleg_creds=True, gss_host=None, banner_timeout=None, auth_timeout=None, gss_trust_dns=True, ): """ Connect to an SSH server and authenticate to it. The server's host key is checked against the system host keys (see `load_system_host_keys`) and any local host keys (`load_host_keys`). If the server's hostname is not found in either set of host keys, the missing host key policy is used (see `set_missing_host_key_policy`). The default policy is to reject the key and raise an `.SSHException`. Authentication is attempted in the following order of priority: - The ``pkey`` or ``key_filename`` passed in (if any) - ``key_filename`` may contain OpenSSH public certificate paths as well as regular private-key paths; when files ending in ``-cert.pub`` are found, they are assumed to match a private key, and both components will be loaded. (The private key itself does *not* need to be listed in ``key_filename`` for this to occur - *just* the certificate.) - Any key we can find through an SSH agent - Any "id_rsa", "id_dsa" or "id_ecdsa" key discoverable in ``~/.ssh/`` - When OpenSSH-style public certificates exist that match an existing such private key (so e.g. one has ``id_rsa`` and ``id_rsa-cert.pub``) the certificate will be loaded alongside the private key and used for authentication. - Plain username/password auth, if a password was given If a private key requires a password to unlock it, and a password is passed in, that password will be used to attempt to unlock the key. :param str hostname: the server to connect to :param int port: the server port to connect to :param str username: the username to authenticate as (defaults to the current local username) :param str password: a password to use for authentication or for unlocking a private key :param .PKey pkey: an optional private key to use for authentication :param str key_filename: the filename, or list of filenames, of optional private key(s) and/or certs to try for authentication :param float timeout: an optional timeout (in seconds) for the TCP connect :param bool allow_agent: set to False to disable connecting to the SSH agent :param bool look_for_keys: set to False to disable searching for discoverable private key files in ``~/.ssh/`` :param bool compress: set to True to turn on compression :param socket sock: an open socket or socket-like object (such as a `.Channel`) to use for communication to the target host :param bool gss_auth: ``True`` if you want to use GSS-API authentication :param bool gss_kex: Perform GSS-API Key Exchange and user authentication :param bool gss_deleg_creds: Delegate GSS-API client credentials or not :param str gss_host: The targets name in the kerberos database. default: hostname :param bool gss_trust_dns: Indicates whether or not the DNS is trusted to securely canonicalize the name of the host being connected to (default ``True``). :param float banner_timeout: an optional timeout (in seconds) to wait for the SSH banner to be presented. :param float auth_timeout: an optional timeout (in seconds) to wait for an authentication response. :raises: `.BadHostKeyException` -- if the server's host key could not be verified :raises: `.AuthenticationException` -- if authentication failed :raises: `.SSHException` -- if there was any other error connecting or establishing an SSH session :raises socket.error: if a socket error occurred while connecting .. versionchanged:: 1.15 Added the ``banner_timeout``, ``gss_auth``, ``gss_kex``, ``gss_deleg_creds`` and ``gss_host`` arguments. .. versionchanged:: 2.3 Added the ``gss_trust_dns`` argument. """ if not sock: errors = {} # Try multiple possible address families (e.g. IPv4 vs IPv6) to_try = list(self._families_and_addresses(hostname, port)) for af, addr in to_try: try: sock = socket.socket(af, socket.SOCK_STREAM) if timeout is not None: try: sock.settimeout(timeout) except: pass retry_on_signal(lambda: sock.connect(addr)) # Break out of the loop on success break except socket.error as e: # Raise anything that isn't a straight up connection error # (such as a resolution error) if e.errno not in (ECONNREFUSED, EHOSTUNREACH): raise # Capture anything else so we know how the run looks once # iteration is complete. Retain info about which attempt # this was. errors[addr] = e # Make sure we explode usefully if no address family attempts # succeeded. We've no way of knowing which error is the "right" # one, so we construct a hybrid exception containing all the real # ones, of a subclass that client code should still be watching for # (socket.error) if len(errors) == len(to_try): raise NoValidConnectionsError(errors) t = self._transport = Transport(sock, gss_kex=gss_kex, gss_deleg_creds=gss_deleg_creds) t.use_compression(compress=compress) t.set_gss_host( # t.hostname may be None, but GSS-API requires a target name. # Therefore use hostname as fallback. gss_host=gss_host or hostname, trust_dns=gss_trust_dns, gssapi_requested=gss_auth or gss_kex, ) if self._log_channel is not None: t.set_log_channel(self._log_channel) if banner_timeout is not None: t.banner_timeout = banner_timeout if auth_timeout is not None: t.auth_timeout = auth_timeout if port == SSH_PORT: server_hostkey_name = hostname else: server_hostkey_name = "[%s]:%d" % (hostname, port) our_server_keys = None our_server_keys = self._system_host_keys.get(server_hostkey_name) if our_server_keys is None: our_server_keys = self._host_keys.get(server_hostkey_name) if our_server_keys is not None: keytype = our_server_keys.keys()[0] sec_opts = t.get_security_options() other_types = [x for x in sec_opts.key_types if x != keytype] sec_opts.key_types = [keytype] + other_types t.start_client(timeout=timeout) # If GSS-API Key Exchange is performed we are not required to check the # host key, because the host is authenticated via GSS-API / SSPI as # well as our client. if not self._transport.gss_kex_used: server_key = t.get_remote_server_key() if our_server_keys is None: # will raise exception if the key is rejected self._policy.missing_host_key(self, server_hostkey_name, server_key) else: our_key = our_server_keys.get(server_key.get_name()) if our_key != server_key: if our_key is None: our_key = list(our_server_keys.values())[0] raise BadHostKeyException(hostname, server_key, our_key) if username is None: username = getpass.getuser() if key_filename is None: key_filenames = [] elif isinstance(key_filename, string_types): key_filenames = [key_filename] else: key_filenames = key_filename self._auth( username, password, pkey, key_filenames, allow_agent, look_for_keys, gss_auth, gss_kex, gss_deleg_creds, t.gss_host, )
def connect(self, hostname, port=SSH_PORT, username=None, password=None, pkey=None, key_filename=None, timeout=None, allow_agent=True, look_for_keys=True, compress=False, sock=None, gss_auth=False, gss_kex=False, gss_deleg_creds=True, gss_host=None, banner_timeout=None): """ Connect to an SSH server and authenticate to it. The server's host key is checked against the system host keys (see `load_system_host_keys`) and any local host keys (`load_host_keys`). If the server's hostname is not found in either set of host keys, the missing host key policy is used (see `set_missing_host_key_policy`). The default policy is to reject the key and raise an `.SSHException`. Authentication is attempted in the following order of priority: - The ``pkey`` or ``key_filename`` passed in (if any) - Any key we can find through an SSH agent - Any "id_rsa", "id_dsa" or "id_ecdsa" key discoverable in ``~/.ssh/`` - Plain username/password auth, if a password was given If a private key requires a password to unlock it, and a password is passed in, that password will be used to attempt to unlock the key. :param str hostname: the server to connect to :param int port: the server port to connect to :param str username: the username to authenticate as (defaults to the current local username) :param str password: a password to use for authentication or for unlocking a private key :param .PKey pkey: an optional private key to use for authentication :param str key_filename: the filename, or list of filenames, of optional private key(s) to try for authentication :param float timeout: an optional timeout (in seconds) for the TCP connect :param bool allow_agent: set to False to disable connecting to the SSH agent :param bool look_for_keys: set to False to disable searching for discoverable private key files in ``~/.ssh/`` :param bool compress: set to True to turn on compression :param socket sock: an open socket or socket-like object (such as a `.Channel`) to use for communication to the target host :param bool gss_auth: ``True`` if you want to use GSS-API authentication :param bool gss_kex: Perform GSS-API Key Exchange and user authentication :param bool gss_deleg_creds: Delegate GSS-API client credentials or not :param str gss_host: The targets name in the kerberos database. default: hostname :param float banner_timeout: an optional timeout (in seconds) to wait for the SSH banner to be presented. :raises BadHostKeyException: if the server's host key could not be verified :raises AuthenticationException: if authentication failed :raises SSHException: if there was any other error connecting or establishing an SSH session :raises socket.error: if a socket error occurred while connecting .. versionchanged:: 1.15 Added the ``banner_timeout``, ``gss_auth``, ``gss_kex``, ``gss_deleg_creds`` and ``gss_host`` arguments. """ if not sock: for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM): if socktype == socket.SOCK_STREAM: af = family addr = sockaddr break else: # some OS like AIX don't indicate SOCK_STREAM support, so just guess. :( af, _, _, _, addr = socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM) sock = socket.socket(af, socket.SOCK_STREAM) if timeout is not None: try: sock.settimeout(timeout) except: pass retry_on_signal(lambda: sock.connect(addr)) t = self._transport = Transport(sock, gss_kex=gss_kex, gss_deleg_creds=gss_deleg_creds) t.use_compression(compress=compress) if gss_kex and gss_host is None: t.set_gss_host(hostname) elif gss_kex and gss_host is not None: t.set_gss_host(gss_host) else: pass if self._log_channel is not None: t.set_log_channel(self._log_channel) if banner_timeout is not None: t.banner_timeout = banner_timeout t.start_client() ResourceManager.register(self, t) server_key = t.get_remote_server_key() keytype = server_key.get_name() if port == SSH_PORT: server_hostkey_name = hostname else: server_hostkey_name = "[%s]:%d" % (hostname, port) # If GSS-API Key Exchange is performed we are not required to check the # host key, because the host is authenticated via GSS-API / SSPI as # well as our client. if not self._transport.use_gss_kex: our_server_key = self._system_host_keys.get( server_hostkey_name, {}).get(keytype, None) if our_server_key is None: our_server_key = self._host_keys.get(server_hostkey_name, {}).get(keytype, None) if our_server_key is None: # will raise exception if the key is rejected; let that fall out self._policy.missing_host_key(self, server_hostkey_name, server_key) # if the callback returns, assume the key is ok our_server_key = server_key if server_key != our_server_key: raise BadHostKeyException(hostname, server_key, our_server_key) if username is None: username = getpass.getuser() if key_filename is None: key_filenames = [] elif isinstance(key_filename, string_types): key_filenames = [key_filename] else: key_filenames = key_filename if gss_host is None: gss_host = hostname self._auth(username, password, pkey, key_filenames, allow_agent, look_for_keys, gss_auth, gss_kex, gss_deleg_creds, gss_host)
def connect( self, hostname, port=SSH_PORT, username=None, password=None, pkey=None, key_filename=None, timeout=None, allow_agent=True, look_for_keys=True, compress=False, sock=None, gss_auth=False, gss_kex=False, gss_deleg_creds=True, gss_host=None, banner_timeout=None, auth_timeout=None, gss_trust_dns=True, passphrase=None, ): """ Connect to an SSH server and authenticate to it. The server's host key is checked against the system host keys (see `load_system_host_keys`) and any local host keys (`load_host_keys`). If the server's hostname is not found in either set of host keys, the missing host key policy is used (see `set_missing_host_key_policy`). The default policy is to reject the key and raise an `.SSHException`. Authentication is attempted in the following order of priority: - The ``pkey`` or ``key_filename`` passed in (if any) - ``key_filename`` may contain OpenSSH public certificate paths as well as regular private-key paths; when files ending in ``-cert.pub`` are found, they are assumed to match a private key, and both components will be loaded. (The private key itself does *not* need to be listed in ``key_filename`` for this to occur - *just* the certificate.) - Any key we can find through an SSH agent - Any "id_rsa", "id_dsa", "id_ecdsa", or "id_ed25519" key discoverable in ``~/.ssh/`` - When OpenSSH-style public certificates exist that match an existing such private key (so e.g. one has ``id_rsa`` and ``id_rsa-cert.pub``) the certificate will be loaded alongside the private key and used for authentication. - Plain username/password auth, if a password was given If a private key requires a password to unlock it, and a password is passed in, that password will be used to attempt to unlock the key. :param str hostname: the server to connect to :param int port: the server port to connect to :param str username: the username to authenticate as (defaults to the current local username) :param str password: Used for password authentication; is also used for private key decryption if ``passphrase`` is not given. :param str passphrase: Used for decrypting private keys. :param .PKey pkey: an optional private key to use for authentication :param str key_filename: the filename, or list of filenames, of optional private key(s) and/or certs to try for authentication :param float timeout: an optional timeout (in seconds) for the TCP connect :param bool allow_agent: set to False to disable connecting to the SSH agent :param bool look_for_keys: set to False to disable searching for discoverable private key files in ``~/.ssh/`` :param bool compress: set to True to turn on compression :param socket sock: an open socket or socket-like object (such as a `.Channel`) to use for communication to the target host :param bool gss_auth: ``True`` if you want to use GSS-API authentication :param bool gss_kex: Perform GSS-API Key Exchange and user authentication :param bool gss_deleg_creds: Delegate GSS-API client credentials or not :param str gss_host: The targets name in the kerberos database. default: hostname :param bool gss_trust_dns: Indicates whether or not the DNS is trusted to securely canonicalize the name of the host being connected to (default ``True``). :param float banner_timeout: an optional timeout (in seconds) to wait for the SSH banner to be presented. :param float auth_timeout: an optional timeout (in seconds) to wait for an authentication response. :raises: `.BadHostKeyException` -- if the server's host key could not be verified :raises: `.AuthenticationException` -- if authentication failed :raises: `.SSHException` -- if there was any other error connecting or establishing an SSH session :raises socket.error: if a socket error occurred while connecting .. versionchanged:: 1.15 Added the ``banner_timeout``, ``gss_auth``, ``gss_kex``, ``gss_deleg_creds`` and ``gss_host`` arguments. .. versionchanged:: 2.3 Added the ``gss_trust_dns`` argument. .. versionchanged:: 2.4 Added the ``passphrase`` argument. """ if not sock: sock = retry_on_signal(lambda: socket.create_connection( (hostname, port), timeout)) t = self._transport = Transport(sock, gss_kex=gss_kex, gss_deleg_creds=gss_deleg_creds) t.use_compression(compress=compress) t.set_gss_host( # t.hostname may be None, but GSS-API requires a target name. # Therefore use hostname as fallback. gss_host=gss_host or hostname, trust_dns=gss_trust_dns, gssapi_requested=gss_auth or gss_kex, ) if self._log_channel is not None: t.set_log_channel(self._log_channel) if banner_timeout is not None: t.banner_timeout = banner_timeout if auth_timeout is not None: t.auth_timeout = auth_timeout if port == SSH_PORT: server_hostkey_name = hostname else: server_hostkey_name = "[{}]:{}".format(hostname, port) our_server_keys = None our_server_keys = self._system_host_keys.get(server_hostkey_name) if our_server_keys is None: our_server_keys = self._host_keys.get(server_hostkey_name) if our_server_keys is not None: keytype = our_server_keys.keys()[0] sec_opts = t.get_security_options() other_types = [x for x in sec_opts.key_types if x != keytype] sec_opts.key_types = [keytype] + other_types t.start_client(timeout=timeout) # If GSS-API Key Exchange is performed we are not required to check the # host key, because the host is authenticated via GSS-API / SSPI as # well as our client. if not self._transport.gss_kex_used: server_key = t.get_remote_server_key() if our_server_keys is None: # will raise exception if the key is rejected self._policy.missing_host_key(self, server_hostkey_name, server_key) else: our_key = our_server_keys.get(server_key.get_name()) if our_key != server_key: if our_key is None: our_key = list(our_server_keys.values())[0] raise BadHostKeyException(hostname, server_key, our_key) if username is None: username = getpass.getuser() if key_filename is None: key_filenames = [] elif isinstance(key_filename, string_types): key_filenames = [key_filename] else: key_filenames = key_filename self._auth( username, password, pkey, key_filenames, allow_agent, look_for_keys, gss_auth, gss_kex, gss_deleg_creds, t.gss_host, passphrase, )
def connect(self, hostname, port=SSH_PORT, username=None, password=None, pkey=None, key_filename=None, timeout=None, allow_agent=True, look_for_keys=True): """ Connect to an SSH server and authenticate to it. The server's host key is checked against the system host keys (see L{load_system_host_keys}) and any local host keys (L{load_host_keys}). If the server's hostname is not found in either set of host keys, the missing host key policy is used (see L{set_missing_host_key_policy}). The default policy is to reject the key and raise an L{SSHException}. Authentication is attempted in the following order of priority: - The C{pkey} or C{key_filename} passed in (if any) - Any key we can find through an SSH agent - Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/} - Plain username/password auth, if a password was given If a private key requires a password to unlock it, and a password is passed in, that password will be used to attempt to unlock the key. @param hostname: the server to connect to @type hostname: str @param port: the server port to connect to @type port: int @param username: the username to authenticate as (defaults to the current local username) @type username: str @param password: a password to use for authentication or for unlocking a private key @type password: str @param pkey: an optional private key to use for authentication @type pkey: L{PKey} @param key_filename: the filename, or list of filenames, of optional private key(s) to try for authentication @type key_filename: str or list(str) @param timeout: an optional timeout (in seconds) for the TCP connect @type timeout: float @param allow_agent: set to False to disable connecting to the SSH agent @type allow_agent: bool @param look_for_keys: set to False to disable searching for discoverable private key files in C{~/.ssh/} @type look_for_keys: bool @raise BadHostKeyException: if the server's host key could not be verified @raise AuthenticationException: if authentication failed @raise SSHException: if there was any other error connecting or establishing an SSH session @raise socket.error: if a socket error occurred while connecting """ for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM): if socktype == socket.SOCK_STREAM: af = family addr = sockaddr break else: raise SSHException('No suitable address family for %s' % hostname) sock = socket.socket(af, socket.SOCK_STREAM) if timeout is not None: try: sock.settimeout(timeout) except: pass sock.connect(addr) t = self._transport = Transport(sock) if self._log_channel is not None: t.set_log_channel(self._log_channel) t.start_client() ResourceManager.register(self, t) server_key = t.get_remote_server_key() keytype = server_key.get_name() if port == SSH_PORT: server_hostkey_name = hostname else: server_hostkey_name = "[%s]:%d" % (hostname, port) our_server_key = self._system_host_keys.get(server_hostkey_name, {}).get(keytype, None) if our_server_key is None: our_server_key = self._host_keys.get(server_hostkey_name, {}).get(keytype, None) if our_server_key is None: # will raise exception if the key is rejected; let that fall out self._policy.missing_host_key(self, server_hostkey_name, server_key) # if the callback returns, assume the key is ok our_server_key = server_key if server_key != our_server_key: raise BadHostKeyException(hostname, server_key, our_server_key) if username is None: username = getpass.getuser() if key_filename is None: key_filenames = [] elif isinstance(key_filename, (str, unicode)): key_filenames = [key_filename] else: key_filenames = key_filename self._auth(username, password, pkey, key_filenames, allow_agent, look_for_keys)
class DecoratorConnectHostTestCase(TestCase): def setUp(self): self.host_ssh = HostSSH( address='fake_address', username='******', password='******' ) self.fake_func = MagicMock( return_value={ 'stdout': 'fake_stdout', 'stdin': 'fake_stdin', 'stderr': 'fake_stderr', 'exception': '', } ) self.decorated = connect_host(self.fake_func) @patch.object(HostSSH, 'connect') def test_call_connect_when_not_error(self, connect_mock): output = self.decorated(self.host_ssh) self.assertTrue(connect_mock.called) self.assertTrue(self.fake_func.called) self.assertEqual(output['stdout'], 'fake_stdout') self.assertEqual(output['exception'], '') @patch.object(HostSSH, 'connect', side_effect=BadHostKeyException( 'fake_hostname', MagicMock(), MagicMock() )) def test_call_connect_bad_host_exeption(self, connect_mock): output = self.decorated(self.host_ssh) self.assertTrue(connect_mock.called) self.assertFalse(self.fake_func.called) self.assertEqual(output['stdout'], '') self.assertIn( "fake_hostname", output['exception'] ) @patch.object(HostSSH, 'connect', side_effect=SSHException( 'fake err msg' )) def test_call_connect_ssh_exeption(self, connect_mock): output = self.decorated(self.host_ssh) self.assertTrue(connect_mock.called) self.assertFalse(self.fake_func.called) self.assertEqual(output['stdout'], '') self.assertEqual( "fake err msg", output['exception'] ) @patch.object(HostSSH, 'connect', side_effect=AuthenticationException( 'fake err msg' )) def test_call_connect_auth_exeption(self, connect_mock): output = self.decorated(self.host_ssh) self.assertTrue(connect_mock.called) self.assertFalse(self.fake_func.called) self.assertEqual(output['stdout'], '') self.assertEqual( "fake err msg", output['exception'] ) @patch.object(HostSSH, 'connect', side_effect=socker_err( 'fake err msg' )) def test_call_connect_socket_exeption(self, connect_mock): output = self.decorated(self.host_ssh) self.assertTrue(connect_mock.called) self.assertFalse(self.fake_func.called) self.assertEqual(output['stdout'], '') self.assertEqual( "fake err msg", output['exception'] )
import pickle import pytest from paramiko import RSAKey from paramiko.ssh_exception import ( BadAuthenticationType, PartialAuthentication, ChannelException, BadHostKeyException, ProxyCommandFailure, ) @pytest.mark.parametrize(['exc'], [ (BadAuthenticationType("Bad authentication type", ["ok", "also-ok"]), ), (PartialAuthentication(["ok", "also-ok"]), ), (BadHostKeyException("myhost", RSAKey.generate(2048), RSAKey.generate(2048)), ), (ProxyCommandFailure("nc servername 22", 1), ), (ChannelException(17, "whatever"), ), ]) def test_ssh_exception_strings(exc): assert isinstance(str(exc), str) assert isinstance(repr(exc), str) if type(exc) != BadHostKeyException: ne = pickle.loads(pickle.dumps(exc)) assert type(ne) == type(exc)
def connect(self, hostname, port=22, username=None, password=None, pkey=None, key_filename=None, timeout=None): """ Connect to an SSH server and authenticate to it. The server's host key is checked against the system host keys (see L{load_system_host_keys}) and any local host keys (L{load_host_keys}). If the server's hostname is not found in either set of host keys, the missing host key policy is used (see L{set_missing_host_key_policy}). The default policy is to reject the key and raise an L{SSHException}. Authentication is attempted in the following order of priority: - The C{pkey} or C{key_filename} passed in (if any) - Any key we can find through an SSH agent - Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/} - Plain username/password auth, if a password was given If a private key requires a password to unlock it, and a password is passed in, that password will be used to attempt to unlock the key. @param hostname: the server to connect to @type hostname: str @param port: the server port to connect to @type port: int @param username: the username to authenticate as (defaults to the current local username) @type username: str @param password: a password to use for authentication or for unlocking a private key @type password: str @param pkey: an optional private key to use for authentication @type pkey: L{PKey} @param key_filename: the filename of an optional private key to use for authentication @type key_filename: str @param timeout: an optional timeout (in seconds) for the TCP connect @type timeout: float @raise BadHostKeyException: if the server's host key could not be verified @raise AuthenticationException: if authentication failed @raise SSHException: if there was any other error connecting or establishing an SSH session """ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if timeout is not None: try: sock.settimeout(timeout) except: pass sock.connect((hostname, port)) t = self._transport = Transport(sock) if self._log_channel is not None: t.set_log_channel(self._log_channel) t.start_client() ResourceManager.register(self, t) server_key = t.get_remote_server_key() keytype = server_key.get_name() our_server_key = self._system_host_keys.get(hostname, {}).get(keytype, None) if our_server_key is None: our_server_key = self._host_keys.get(hostname, {}).get(keytype, None) if our_server_key is None: # will raise exception if the key is rejected; let that fall out self._policy.missing_host_key(self, hostname, server_key) # if the callback returns, assume the key is ok our_server_key = server_key if server_key != our_server_key: raise BadHostKeyException(hostname, server_key, our_server_key) if username is None: username = getpass.getuser() self._auth(username, password, pkey, key_filename)
def connect(self, hostname, port=SSH_PORT, username=None, password=None, pkey=None, key_filename=None, timeout=None, allow_agent=True, look_for_keys=True, compress=False, sock=None, gss_auth=False, gss_kex=False, gss_deleg_creds=True, gss_host=None, banner_timeout=None, check_key=False): """ Connect to an SSH server and authenticate to it. The server's host key is checked against the system host keys (see `load_system_host_keys`) and any local host keys (`load_host_keys`). If the server's hostname is not found in either set of host keys, the missing host key policy is used (see `set_missing_host_key_policy`). The default policy is to reject the key and raise an `.SSHException`. Authentication is attempted in the following order of priority: - The ``pkey`` or ``key_filename`` passed in (if any) - Any key we can find through an SSH agent - Any "id_rsa", "id_dsa" or "id_ecdsa" key discoverable in ``~/.ssh/`` - Plain username/password auth, if a password was given If a private key requires a password to unlock it, and a password is passed in, that password will be used to attempt to unlock the key. :param str hostname: the server to connect to :param int port: the server port to connect to :param str username: the username to authenticate as (defaults to the current local username) :param str password: a password to use for authentication or for unlocking a private key :param .PKey pkey: an optional private key to use for authentication :param str key_filename: the filename, or list of filenames, of optional private key(s) to try for authentication :param float timeout: an optional timeout (in seconds) for the TCP connect :param bool allow_agent: set to False to disable connecting to the SSH agent :param bool look_for_keys: set to False to disable searching for discoverable private key files in ``~/.ssh/`` :param bool compress: set to True to turn on compression :param socket sock: an open socket or socket-like object (such as a `.Channel`) to use for communication to the target host :param bool gss_auth: ``True`` if you want to use GSS-API authentication :param bool gss_kex: Perform GSS-API Key Exchange and user authentication :param bool gss_deleg_creds: Delegate GSS-API client credentials or not :param str gss_host: The targets name in the kerberos database. default: hostname :param float banner_timeout: an optional timeout (in seconds) to wait for the SSH banner to be presented. :param bool check_key: set to True to perform server-based pubkey checking. This option mimics openssh behavior and could fix auth issues for some SSH server implementations. :raises BadHostKeyException: if the server's host key could not be verified :raises AuthenticationException: if authentication failed :raises SSHException: if there was any other error connecting or establishing an SSH session :raises socket.error: if a socket error occurred while connecting .. versionchanged:: 1.15 Added the ``banner_timeout``, ``gss_auth``, ``gss_kex``, ``gss_deleg_creds`` and ``gss_host`` arguments. """ if not sock: errors = {} # Try multiple possible address families (e.g. IPv4 vs IPv6) to_try = list(self._families_and_addresses(hostname, port)) for af, addr in to_try: try: sock = socket.socket(af, socket.SOCK_STREAM) if timeout is not None: try: sock.settimeout(timeout) except: pass retry_on_signal(lambda: sock.connect(addr)) # Break out of the loop on success break except socket.error as e: # Raise anything that isn't a straight up connection error # (such as a resolution error) if e.errno not in (ECONNREFUSED, EHOSTUNREACH): raise # Capture anything else so we know how the run looks once # iteration is complete. Retain info about which attempt # this was. errors[addr] = e # Make sure we explode usefully if no address family attempts # succeeded. We've no way of knowing which error is the "right" # one, so we construct a hybrid exception containing all the real # ones, of a subclass that client code should still be watching for # (socket.error) if len(errors) == len(to_try): raise NoValidConnectionsError(errors) t = self._transport = Transport(sock, gss_kex=gss_kex, gss_deleg_creds=gss_deleg_creds) t.use_compression(compress=compress) if gss_kex and gss_host is None: t.set_gss_host(hostname) elif gss_kex and gss_host is not None: t.set_gss_host(gss_host) else: pass if self._log_channel is not None: t.set_log_channel(self._log_channel) if banner_timeout is not None: t.banner_timeout = banner_timeout t.start_client() ResourceManager.register(self, t) server_key = t.get_remote_server_key() keytype = server_key.get_name() if port == SSH_PORT: server_hostkey_name = hostname else: server_hostkey_name = "[%s]:%d" % (hostname, port) # If GSS-API Key Exchange is performed we are not required to check the # host key, because the host is authenticated via GSS-API / SSPI as # well as our client. if not self._transport.use_gss_kex: our_server_key = self._system_host_keys.get( server_hostkey_name, {}).get(keytype, None) if our_server_key is None: our_server_key = self._host_keys.get(server_hostkey_name, {}).get(keytype, None) if our_server_key is None: # will raise exception if the key is rejected; let that fall out self._policy.missing_host_key(self, server_hostkey_name, server_key) # if the callback returns, assume the key is ok our_server_key = server_key if server_key != our_server_key: raise BadHostKeyException(hostname, server_key, our_server_key) if username is None: username = getpass.getuser() if key_filename is None: key_filenames = [] elif isinstance(key_filename, string_types): key_filenames = [key_filename] else: key_filenames = key_filename if gss_host is None: gss_host = hostname self._auth(username, password, pkey, key_filenames, allow_agent, look_for_keys, gss_auth, gss_kex, gss_deleg_creds, gss_host, check_key)