def test_reraising_bad_host_key_exc(self): self.client.password = "******" self.client.private_key = self.rsakey exc = paramiko.BadHostKeyException(None, None, None) self.mock_connect.side_effect = exc self.assertRaises(paramiko.BadHostKeyException, self.client.connect)
def is_missing_host_key(self, client, hostname, key): k = client._host_keys.lookup(hostname) if k is None: return True host_key = k.get(key.get_name(), None) if host_key is None: return True if host_key != key: raise paramiko.BadHostKeyException(hostname, key, host_key)
def connect(self, hostname, sock, port=22, username=None, password=None, pkey=None, key_filename=None, timeout=None, allow_agent=True, look_for_keys=True): t = self._transport = ssh.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 ssh.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)
def test_logging_when_badhostkey(self, mock_LOG): """Test when raising BadHostKeyException.""" self.client.password = "******" self.client.private_key = self.rsakey exc = paramiko.BadHostKeyException(None, None, None) self.mock_connect.side_effect = exc try: self.client.connect() except paramiko.BadHostKeyException: pass mock_LOG.info.assert_called_with( "ssh://%s@%s:%d failed: %s. " "You might have a bad key entry on your server, " "but this is a security issue and won't be handled " "automatically. To fix this you can remove the " "host entry for this host from the /.ssh/known_hosts file", 'test-user', '123.456.789.0', 22, exc)
def missing_host_key(self, client, hostname, key): if self.gw.ssh_host_key: # Verify host key line = base64.b64decode(self.gw.ssh_host_key).decode() entry = paramiko.hostkeys.HostKeyEntry.from_line(line) if hostname not in entry.hostnames or key != entry.key: raise paramiko.BadHostKeyException(hostname, key, entry.key) else: # Auto-add host key entry = paramiko.hostkeys.HostKeyEntry([hostname], key) line = entry.to_line() self.gw.ssh_host_key = base64.b64encode(line.encode()) self.gw.ssh_host_key_filename = SSH_KNOWN_HOSTS self.gw.message_post(body=(_("Added host key for '%s' (%s)") % (self.gw.server, self.gw.ssh_host_fingerprint))) _logger.warning("Added host key for '%s' (%s)", self.gw.server, self.gw.ssh_host_fingerprint)
def test_SFTP_handle_connection_exceptions(self, mock_ssh): """SFTP should catch exceptions raised when trying to connect""" # Before you implement the logging you'll want to test for it here exceptions = [ paramiko.AuthenticationException, paramiko.BadHostKeyException(mock.Mock(), mock.Mock(), mock.Mock()), paramiko.SSHException ] mock_ssh.return_value.connect.side_effect = exceptions for exc in exceptions: with self.subTest(msg=str(exc)): with self.assertLogs(msutils.uploading.logger, 'ERROR') as cm: try: msutils.uploading.send_pages_sftp( pages=self.mock_pages, **self.call_args) except Exception as e: self.fail(str(e)) self.assertGreaterEqual(len(cm.output), 1)
def connect(cls, host, port, hostkey, userkey, keypass=None, username=None, password=None): """ Create a new SFTP client and connect to C{host}. Returns None if creation or connection fails. C{username} and C{password} are optional and not needed for the authentication, that is by default public key. @param host: the host name or ip address. @type host: str @param port: the host port. @type port: int @param hostkey: the path to the host's public key. @type hostkey: str @param userkey: the path to the user's private key. @type userkey: str @param keypass: password for encrypted key. @type keypass: str @param username: the user's name (optional). @type username: str @param password: the user's password (optional). @type password: str @return: L{SFTPClient} or None. @rtype: L{SFTPClient} """ known_host = '' # file format "ssh-rsa AAA.... user@somemachine" with open(hostkey, 'rb') as f: known_host = f.read() # get the base64 encoded data of the host's public key known_host = known_host.split(' ')[1] client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect((host, int(port))) transport = paramiko.Transport(client_socket) transport.start_client() key_data = transport.get_remote_server_key().get_base64() if not known_host == key_data: raise paramiko.BadHostKeyException(host, key_data, known_host) key = paramiko.RSAKey.from_private_key_file(userkey, keypass) username = username if username else '' try: transport.auth_publickey(username, key) except Exception: transport.auth_password(username, password) chan = transport.open_session() if chan is None: return None chan.invoke_subsystem('sftp') return cls(chan)
def connect_keyboard_interactive(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): """ modified from paramiko.SSHClient.connect() and paramiko.Transport.auth_interactive() so that it will not cause failed authentication attempts on hosts that allow "keyboard_interactive" but not "password" authentication (even though those two are essentially the same thing). """ 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 sock.connect(addr) t = self._transport = paramiko.Transport(sock) t.use_compression(compress=compress) if self._log_channel is not None: t.set_log_channel(self._log_channel) t.start_client() paramiko.resource.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 paramiko.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 def handler(title, instructions, fields): if len(fields) > 1: raise SSHException('Fallback authentication failed.') if len(fields) == 0: # for some reason, at least on os x, a 2nd request will # be made with zero fields requested. maybe it's just # to try to fake out automated scripting of the exact # type we're doing here. *shrug* :) return [] return [password] self._transport.auth_interactive(username, handler)