def get_ssh_client(hostname, username, password): try: ssh_client = paramiko.SSHClient() if settings.ALLOW_SSH_UNKNOWN_HOSTS: # If true, this will permit connection to unknown SSH servers ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # Will establish a connection to specified SSH server on port 22 ssh_client.connect( hostname=hostname, username=username, password=password, ) return ssh_client except AuthenticationException: print('Authentication failed, please verify your credentials: %s') raise AuthenticationException('AuthenticationException: %s') except SSHException as sshException: print('Unable to establish SSH connection: %s' % sshException) raise AuthenticationException('Unable to establish SSH connection: %s' % sshException) except BadHostKeyException as badHostKeyException: print('Unable to verify server\'s host key: %s' % badHostKeyException) raise AuthenticationException('Unable to verify server\'s host key: %s' % badHostKeyException)
def connect_remote(self): # open connection to remote host try: self.client = SSHClient() self.client.load_system_host_keys() self.client.set_missing_host_key_policy(AutoAddPolicy()) self.client.connect(self.host, port=22, username=self.user, password=self.password, look_for_keys=False, timeout=10) self.scp = SCPClient(self.client.get_transport()) sftp = SFTPClient.from_transport(self.client.get_transport()) try: sftp.stat(self.remote_path) except FileNotFoundError: raise AuthenticationException("Remote path does not exist") try: sftp.stat(self.index_path) except FileNotFoundError: raise AuthenticationException("Index path does not exist") return self.client except AuthenticationException as error: logger.info( 'Authentication failed: did you enter the correct username and password?' ) logger.error(error) self.scp = None return error
def wait_for_response(self, event): max_ts = None if self.transport.auth_timeout is not None: max_ts = time.time() + self.transport.auth_timeout while True: event.wait(0.1) if not self.transport.is_active(): e = self.transport.get_exception() if (e is None) or issubclass(e.__class__, EOFError): e = AuthenticationException('Authentication failed.') raise e if event.is_set(): break if max_ts is not None and max_ts <= time.time(): raise AuthenticationException('Authentication timeout.') if not self.is_authenticated(): e = self.transport.get_exception() if e is None: e = AuthenticationException('Authentication failed.') # this is horrible. Python Exception isn't yet descended from # object, so type(e) won't work. :( if issubclass(e.__class__, PartialAuthentication): return e.allowed_types raise e return []
def open(self): '''Initialize shell connection''' self.logger.info('Opening SSH connection') try: ## Extract user/password from protocol and return the following: ## fabric.Connection(host=self.endpoint, user=user, connect_timeout=self.connectTimeout, connect_kwargs) self.client = externalProtocolLibrary.connection( self.runtime, self.protocol, self.endpoint, self.connectTimeout) ## Send something through stdin to verify we have a connection... ## TODO: consider making this more useful by validating something ## like user, an environmental setting, shell type/version, etc self.run('hostname', timeout=3) self.connected = True except NoValidConnectionsError as e: raise NoValidConnectionsError(e.errors) except socket.timeout: raise socket.timeout(str(sys.exc_info()[1])) except AuthenticationException: raise AuthenticationException(str(sys.exc_info()[1])) except: stacktrace = traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]) self.logger.error('Failure in SSH open: {stacktrace!r}', stacktrace=stacktrace) exceptionOnly = traceback.format_exception_only( sys.exc_info()[0], sys.exc_info()[1]) raise EnvironmentError(exceptionOnly) if not self.connected: self.client.close() raise EnvironmentError('No connection established') return
def _paramiko_auth_agent(self, username, password=None): keys = paramiko.Agent().get_keys() if not keys: raise AuthenticationException('auth agent found no keys') saved_exception = AuthenticationException( 'Failed to authenticate with given username') for key in keys: try: fp = hexlify(key.get_fingerprint()) self._dbg(1, 'Trying SSH agent key %s' % fp) self.client.auth_publickey(username, key) return except SSHException, e: saved_exception = e
def undeploy_docker_image(node, node_data, project_id, r): ssh_user = node_data['ssh_username'] ssh_password = node_data['ssh_password'] node_ip = node_data['ip'] ssh_client = paramiko.SSHClient() ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: ssh_client.connect(node_ip, username=ssh_user, password=ssh_password) except AuthenticationException: r.hset(node, 'ssh_access', False) ssh_client.close() logger.error('An error occurred connecting to: ' + node) raise AuthenticationException('An error occurred connecting to: ' + node) try: project_docker_image = os.path.join(settings.DOCKER_REPO, project_id[0:13]) command = 'sudo docker rmi -f ' + project_docker_image ssh_client.exec_command(command) ssh_client.close() except SSHException: ssh_client.close() logger.error('An error occurred creating the Docker image in: ' + node) raise SSHException('An error occurred creating the Docker image in: ' + node)
def auth_interactive(self, transport): """ try keyboard interactive authentication type """ self.logger.debug('try keyboard interactive authentication type') password = self.password def handler(title, instructions, fields): if len(fields) > 1: raise SSHException('Fallback authentication failed') if len(fields) == 0: return [] return [password] my_event = threading.Event() transport.auth_handler = paramiko.AuthHandler(transport) transport.auth_handler.auth_interactive(self.username, handler, my_event, "") my_event.wait(120) if not my_event.is_set(): self.logger.warn('Authenticate timeout') if not transport.is_authenticated(): error = transport.get_exception() if error is None: error = AuthenticationException('Authentication failed') raise error
def test_open_connection_authentication_exception_multiple_retries( self, mock_rsa, sshClient): sshClient.return_value.connect = Mock( side_effect=AuthenticationException()) configurator = ssh_utilities.RemoteConnection( self.hostname, user=self.user, passwd=self.passwd, priv_key_file=self.priv_key_file) self.assertRaises(exception.AuthenticationError, configurator.open_connection)
def test_authentication_failure(self, mck_log, mck_paramiko): """ Check that ssh authentication errors are handled. """ connect_mck = mock.Mock(side_effect=AuthenticationException('error')) mck_paramiko.SSHClient().connect = connect_mck ssh_client = self.collector._ssh_client('127.0.0.1') self.assertIsNone(ssh_client) self.assertTrue(mck_log.error.called)
def _google_authenticator_client_mimic_handler(title, instructions, prompt_list): answers = [] for i in range(len(prompt_list)): prompt, show_input = prompt_list[i] if prompt.strip() == TWO_FACTOR_AUTH_PROMPT: answers.append('12345') else: raise AuthenticationException('More prompts than expected.') return answers
def wait_for_response(self, event): while True: event.wait(0.1) if not self.transport.is_active(): e = self.transport.get_exception() if e is None: e = AuthenticationException('Authentication failed.') raise e if event.isSet(): break if not self.is_authenticated(): e = self.transport.get_exception() if e is None: e = AuthenticationException('Authentication failed.') # this is horrible. python Exception isn't yet descended from # object, so type(e) won't work. :( if issubclass(e.__class__, PartialAuthentication): return e.allowed_types raise e return []
def _finalize_pubkey_algorithm(self, key_type): # Short-circuit for non-RSA keys if "rsa" not in key_type: return key_type self._log( DEBUG, "Finalizing pubkey algorithm for key of type {!r}".format( key_type), ) # Only consider RSA algos from our list, lest we agree on another! my_algos = [x for x in self.transport.preferred_pubkeys if "rsa" in x] self._log(DEBUG, "Our pubkey algorithm list: {}".format(my_algos)) # Short-circuit negatively if user disabled all RSA algos (heh) if not my_algos: raise SSHException( "An RSA key was specified, but no RSA pubkey algorithms are configured!" # noqa ) # Check for server-sig-algs if supported & sent server_algo_str = u( self.transport.server_extensions.get("server-sig-algs", b(""))) pubkey_algo = None if server_algo_str: server_algos = server_algo_str.split(",") self._log(DEBUG, "Server-side algorithm list: {}".format(server_algos)) # Only use algos from our list that the server likes, in our own # preference order. (NOTE: purposefully using same style as in # Transport...expect to refactor later) agreement = list(filter(server_algos.__contains__, my_algos)) if agreement: pubkey_algo = agreement[0] self._log( DEBUG, "Agreed upon {!r} pubkey algorithm".format(pubkey_algo), ) else: self._log(DEBUG, "No common pubkey algorithms exist! Dying.") # TODO: MAY want to use IncompatiblePeer again here but that's # technically for initial key exchange, not pubkey auth. err = "Unable to agree on a pubkey algorithm for signing a {!r} key!" # noqa raise AuthenticationException(err.format(key_type)) else: # Fallback: first one in our (possibly tweaked by caller) list pubkey_algo = my_algos[0] msg = "Server did not send a server-sig-algs list; defaulting to our first preferred algo ({!r})" # noqa self._log(DEBUG, msg.format(pubkey_algo)) self._log( DEBUG, "NOTE: you may use the 'disabled_algorithms' SSHClient/Transport init kwarg to disable that or other algorithms if your server does not support them!", # noqa ) self.transport._agreed_pubkey_algorithm = pubkey_algo return pubkey_algo
def run(self): try: (r, addr) = self.get_connection() # Found that r should be either a socket from the socket library or None self.__inr = r self.__addr = addr # This should be an IP address as a string? or None self._agent.connect() if not isinstance(self._agent, int) and (self._agent._conn is None or not hasattr(self._agent._conn, 'fileno')): raise AuthenticationException("Unable to connect to SSH agent") self._communicate() except: #XXX Not sure what to do here ... raise or pass ? raise
def test_connection_connect_auth_failure(self, mocked_ssh_close): ckey = self._create_credentials_with_key(port=self.ssh_server.port) dc = self._create_device_connection(credentials=ckey) auth_failed = AuthenticationException('Authentication failed.') with mock.patch( 'paramiko.SSHClient.connect', side_effect=auth_failed ) as mocked_connect: dc.connect() self.assertEqual(mocked_connect.call_count, 2) self.assertFalse(dc.is_working) mocked_ssh_close.assert_called_once() if sys.version_info[0:2] > (3, 7): self.assertNotIn('disabled_algorithms', mocked_connect.mock_calls[0].kwargs) self.assertIn('disabled_algorithms', mocked_connect.mock_calls[1].kwargs)
def wait_for_response(self, event): max_ts = None if self.transport.auth_timeout is not None: max_ts = time.time() + self.transport.auth_timeout while True: event.wait(0.1) if not self.transport.is_active(): e = self.transport.get_exception() if e is None or isinstance(e, EOFError): e = AuthenticationException('Authentication failed.') raise e if event.is_set(): break if max_ts is not None and max_ts <= time.time(): raise AuthenticationException('Authentication timeout.') if not self.is_authenticated(): e = self.transport.get_exception() if e is None: e = AuthenticationException('Authentication failed.') if isinstance(e, PartialAuthentication): return e.allowed_types raise e return []
def delete_docker_image(r, project_id): nodes = r.keys('*_node:*') already_deleted = list() for node in nodes: node_data = r.hgetall(node) node_ip = node_data['ip'] if node_ip not in already_deleted: if node_data['ssh_access']: already_deleted.append(node_ip) # threading.Thread(target=undeploy_docker_image, args=(node, node_data, project_id, r,)).start() undeploy_docker_image(node, node_data, project_id, r) else: logger.error('An error occurred connecting to: ' + node) raise AuthenticationException( 'An error occurred connecting to: ' + node)
def open_sftp_connection(self): host = self.host port = self.port user = self.user password = self.password ssh_priv_key = self.ssh_priv_key ssh_priv_key_pass = self.ssh_priv_key_pass # delete files try: print( "Connecting to {0}@{1}:{2}".format(user, host, port) ) ssh = paramiko.SSHClient() # allow connection to the new unknown host ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # open the ssh connection if password: print("Using password") ssh.connect(host, username=user, port=port, password=password) elif ssh_priv_key: print("Using RSA private key") pkey = paramiko.RSAKey.from_private_key_file( ssh_priv_key, ssh_priv_key_pass ) ssh.connect(host, username=user, port=port, pkey=pkey) else: print( "No password or private key provided. Can't proceed" ) raise AuthenticationException # open the sftp session inside the ssh connection print("SSH Connection Successful") return ssh.open_sftp(), ssh except AuthenticationException as e: print("ERROR in {0}: {1}".format(e.__class__, e)) raise AuthenticationException( "Couldn't connect to {0}, due to an Authentication exception.") except NoValidConnectionsError as e: print("ERROR in {0}: {1}".format(e.__class__, e)) raise NoValidConnectionsError( "Couldn't connect to {0}, possible timeout or invalid hostname")
def auth_password(self, transport): self.logger.debug('try keyboard interactive authentication type') my_event = threading.Event() self.logger.debug('login IP:%s username:%s password:%s' % (self.hostname, self.username, self.password)) transport.auth_password(self.username, self.password, my_event) my_event.wait(120) if not my_event.is_set(): self.logger.warn( 'authentication timeout, ip:%s, user:%s, password:%s' % (self.hostname, self.username, self.password)) if not transport.is_authenticated(): error = transport.get_exception() if error in None: error = AuthenticationException('Authentication failed') raise error
def _paramiko_auth_key(self, username, keys, password): if password is None: password = '' saved_exception = AuthenticationException( 'Failed to authenticate with given username and password/key') for pkey_class, filename in keys: try: key = pkey_class.from_private_key_file(filename, password) fp = hexlify(key.get_fingerprint()) self._dbg(1, 'Trying key %s in %s' % (fp, filename)) self.client.auth_publickey(username, key) return except SSHException, e: saved_exception = e except IOError, e: saved_exception = e
def open_sftp_connection(self, options): host = self.get_option("host", options) port = self.get_option("port", options) user = self.get_option("user", options) password = self.get_option("password", options) ssh_priv_key = self.get_option("privateKeyPath", options) ssh_priv_key_pass = self.get_option("privateKeyPass", options) # delete files try: current_app.logger.debug( "Connecting to {0}@{1}:{2}".format(user, host, port) ) ssh = paramiko.SSHClient() # allow connection to the new unknown host ssh.set_missing_host_key_policy(paramiko.RejectPolicy()) # open the ssh connection if password: current_app.logger.debug("Using password") ssh.connect(host, username=user, port=port, password=password) elif ssh_priv_key: current_app.logger.debug("Using RSA private key") pkey = paramiko.RSAKey.from_private_key_file( ssh_priv_key, ssh_priv_key_pass ) ssh.connect(host, username=user, port=port, pkey=pkey) else: current_app.logger.error( "No password or private key provided. Can't proceed" ) raise AuthenticationException # open the sftp session inside the ssh connection return ssh.open_sftp(), ssh except AuthenticationException as e: current_app.logger.error("ERROR in {0}: {1}".format(e.__class__, e)) raise AuthenticationException("Couldn't connect to {0}, due to an Authentication exception.") except NoValidConnectionsError as e: current_app.logger.error("ERROR in {0}: {1}".format(e.__class__, e)) raise NoValidConnectionsError("Couldn't connect to {0}, possible timeout or invalid hostname")
def auth_public_key(self, transport): self.logger.debug('try keyboard interactive authentication type') my_event = threading.Event() try: key = paramiko.RSAKey.from_private_key_file(self.privateKey) except SSHException: key = paramiko.DSSKey.from_private_key_file(self.privateKey) self.logger.debug('login IP:%s username:%s privateKey:%s' % (self.hostname, self.username, self.privateKey)) transport.auth_publickey(self.username, key, my_event) my_event.wait(120) if not my_event.is_set(): self.logger.warn( 'authentication timeout, ip:%s user:%s privateKey:%s' % (self.hostname, self.username, self.privateKey)) if not transport.is_authenticated(): error = transport.get_exception() if error is None: error = AuthenticationException( 'Authentication failed, privateKey: %s' % self.privateKey) raise error
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))
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'] )
def test_authentication_exception(self): self.mock_lookup_keys_manager.return_value.add_public_key = MagicMock(side_effect=AuthenticationException()) result, message, status = self.run_add_pub_key_to_resource() self.assertFalse(result) self.assertEqual(status, 403)