def _connect(self): host_id = self._host_cfg['host_id'] host, port = self._host_cfg[host_id, 'host'] user = self._host_cfg[host_id, 'user'] passwd = self._host_cfg[host_id, 'password'] timeout = self._host_cfg[host_id, 'timeout'] or None known_hosts = self._host_cfg[host_id, 'known_hosts'] key_type = self._host_cfg[host_id, 'key_type'] key_file = self._host_cfg[host_id, 'key_file'] key_pass = self._host_cfg[host_id, 'key_pass'] try: if key_type: key = _KEY_TYPES[key_type](filename=key_file, password=key_pass) _logger.debug('private key: %s', key.get_name()) else: key = None hostname = utils.format_knownhost(host, port) hostkeys = HostKeys(known_hosts) transport = Transport((host, port)) transport.start_client(timeout=timeout) hostkey = transport.get_remote_server_key() if not hostkeys.check(hostname, hostkey): raise SSHException('Incorrect hostkey') if key: transport.auth_publickey(user, key) else: transport.auth_password(user, passwd) client = transport.open_sftp_client() client.get_channel().settimeout(timeout) _logger.debug('client for %s created', hostname) return client except (OSError, SSHException) as ex: raise ConnectError(f'Connection to server "{host}:{port}"' f' failed: {ex.args!s}')
def add_from_paramiko_host_keys(self, host_keys: paramiko.HostKeys): for host_entry in host_keys.keys(): for key_type, key in host_keys.lookup(host_entry).items(): self.add( HostKeyEntry.from_paramiko_entry(host_entry=host_entry, key_type=key_type, key=key))
def _add_keys_to_known_hosts(server_keys, host_keys_file): try: if not os.path.isfile(host_keys_file): host_keys = HostKeys() else: host_keys = HostKeys(filename=host_keys_file) for hostname, key_list in server_keys.items(): try: for key_tuple in key_list: key = RSAKey(data=base64.b64decode(key_tuple[0])) host_keys.add(hostname=hostname, key=key, keytype=key_tuple[1]) host_keys.add(hostname=hostname + ".*", key=key, keytype=key_tuple[1]) logging.info( "Adding keys to known hosts file '{0}' for host '{1}'". format(host_keys_file, hostname)) host_keys.save(filename=host_keys_file) except Exception as e: logging.error( "Failed adding keys to known hosts file for host '{0}', with exception: {1}" .format(hostname, e)) except Exception as e: logging.error( "Failed adding keys to known hosts file '{0}', with exception: {1}" .format(host_keys_file, e))
def get_client(self): client = paramiko.SSHClient() host_keys = HostKeys() host_keys.add(hostname=self.host.address, keytype=self.config['host_key_type'], key=self.get_host_key()) client._host_keys = host_keys # If you not a better way than accessing a private member I am all ears return client
def _ssh_authentication_input_loop(self, hostkeys: paramiko.HostKeys, key: paramiko.PKey) -> None: # Ask user for permission to continue # let it look like openssh sha64_fingerprint = base64.b64encode( hashlib.sha256(base64.b64decode( key.get_base64())).digest()).decode("utf-8")[:-1] key_type = key.get_name().replace("ssh-", "").upper() print(f"The authenticity of host '{self.hostname}' can't " "be established.") print(f"{key_type} key fingerprint is {sha64_fingerprint}.") print("Are you sure you want to continue connecting (yes/no)? ", end="") add = input() while True: if add == "yes": hostkeys.add(self.hostname, key.get_name(), key) # ask user if the key should be added permanently print( f"Do you want to add {self.hostname} " "to known_hosts (yes/no)? ", end="", ) save = input() while True: if save == "yes": try: hostkeys.save(filename=self.known_hosts_file) except OSError as e: raise GvmError( "Something went wrong with writing " f"the known_hosts file: {e}") from None logger.info( "Warning: Permanently added '%s' (%s) to " "the list of known hosts.", self.hostname, key_type, ) break elif save == "no": logger.info( "Warning: Host '%s' (%s) not added to " "the list of known hosts.", self.hostname, key_type, ) break else: print("Please type 'yes' or 'no': ", end="") save = input() break elif add == "no": return sys.exit( "User denied key. Host key verification failed.") else: print("Please type 'yes' or 'no': ", end="") add = input()
def readHostKey(host): """Read a host key from the known hosts file""" from paramiko import HostKeys global hostKeys if(hostKeys is None): hostKeys = HostKeys(config.KNOWN_HOSTS_FILE) try: k = hostKeys.lookup(host)['ssh-rsa'] return(k.get_base64()) except TypeError: return None
def readHostKey(host): """Read a host key from the known hosts file""" from paramiko import HostKeys global hostKeys if (hostKeys is None): hostKeys = HostKeys(config.KNOWN_HOSTS_FILE) try: k = hostKeys.lookup(host)['ssh-rsa'] return (k.get_base64()) except TypeError: return None
def get_host_keys(filename): with HOST_KEYS_LOCK: host_keys = HostKeys() try: host_keys.load(filename) # When paramiko encounters a bad host keys line it sometimes bails the # entire load incorrectly. # See: https://github.com/paramiko/paramiko/pull/1990 except Exception as e: logger.warning("Failed to load host keys from {0}: {1}".format(filename, e)) return host_keys
def verify(self, path): if self.knownfile == '': error('No known_hosts file to check against.') args = {'path': path} try: f = open(path + '.ssh-signed', 'r') for line in f: [k, v] = line.replace('\n', '').split(); args[k] = v except: error('Not able to open signature file for verifying.') f.close() if not 'signature' in args: error('Missing signature-tag in signature file.') if not 'host' in args: error('Missing host-tag in signature file.') if not 'keytype' in args: error('Missing keytype-tag in signature file.') if self.host != '' and self.host != args['host']: error('Given host does not match host from signature blob.') self.host = args['host'] self.hhost = HostKeys.hash_host(self.host) self.keytype = args['keytype'] self.__load_hk() print "Found Host: '" + self.host + "' and Keytype: '" + self.keytype + "'." return self.__verify(args)
def init_host_key(self): if self._host_key and isinstance(self._host_key, KEYTYPES.get(self.host_key_type, ())): return if self.host_keys_file: try: known_hosts = HostKeys(self.host_keys_file) except FileNotFoundError: raise SFTPURLError( f'The given host key file {self.host_keys_file} could not be found' ) hostkeys = known_hosts.lookup(self.hostname) if not hostkeys: raise SFTPURLError('There are no host keys associated with' f' {self.hostname}') if self.host_key_type: try: keys = KNOWN_HOSTS_KEY_TYPE_MAP[self.host_key_type.upper()] for k in keys: pkey = hostkeys.get(k) if pkey: break else: # no break raise SFTPURLError( f'There are no host keys with the given type {self.host_key_type}' ) except KeyError: raise SFTPURLError( ('Unrecognized key type {}.' ' Recognized key types: {}').format( self.host_key_type, ', '.join(KNOWN_HOSTS_KEY_TYPE_MAP))) else: for pkey in hostkeys.values(): break self._host_key = pkey
def _del_hostkey(args): host = args['HOST'] file = args['FILE'] try: port = strings.str2port(args['--port']) hostname = utils.format_knownhost(host, port) hostkeys = HostKeys() hostkeys.load(file) if hostkeys.lookup(hostname): del hostkeys[hostname] hostkeys.save(file) print(f'Key for "{hostname}" deleted in file "{file}"') else: print(f'Key for "{hostname}" not found in file "{file}"') except (FileNotFoundError, ConfigError) as ex: print(ex, file=sys.stderr) return ConfigError.code except Exception as ex: print(repr(ex), file=sys.stderr) return ExitCodes.FAILURE.code return ExitCodes.SUCCESS.code
def _validate_key(host: str, server_key: PKey): known_hosts_file = '~/.ssh/known_hosts' host_keys = HostKeys() host_keys.load(os.path.expanduser(known_hosts_file)) known_server_keys = host_keys.get(host) add_host_key_instructions = 'You can add the host key with `\n' \ f'ssh-keyscan -H {host} >> {known_hosts_file}\n`' if known_server_keys is None: raise SSHAuthenticationError( f'plz host is not known. {add_host_key_instructions}') known_server_keys = host_keys.get(host) if known_server_keys.get(server_key.get_name()) is None: raise SSHAuthenticationError( f'No key found for host {host} with name ' f'{server_key.get_name()}. {add_host_key_instructions}') if server_key != known_server_keys.get(server_key.get_name()): raise SSHAuthenticationError( f'Bad host key for `{host}`. Fix your `{known_hosts_file}` file')
def connectionError(self, e): self.viewer.setWindowTitle("rMview - Could not connect!") log.error(e) mbox = QMessageBox(QMessageBox.NoIcon, 'Connection error', "Connection attempt failed", parent=self.viewer) icon = QPixmap(":/assets/dead.svg") icon.setDevicePixelRatio(self.devicePixelRatio()) mbox.setIconPixmap(icon) mbox.addButton("Settings...", QMessageBox.ResetRole) mbox.addButton(QMessageBox.Cancel) if isinstance(e, BadHostKeyException): mbox.setDetailedText(str(e)) mbox.setInformativeText( "<big>The host at %s has the wrong key.<br>" "This usually happens just after a software update on the tablet.</big><br><br>" "You have three options to fix this permanently:" "<ol><li>" "Press Update to replace the old key with the new." "<br></li><li>" "Change your <code>~/.ssh/known_hosts</code> file to match the new fingerprint.<br>" "The easiest way to do this is connecting manually via ssh and follow the instructions." "<br></li><li>" "Set <code>\"host_key_policy\": \"ignore_new\"</code> in the <code>ssh</code> section of rmView\'s settings.<br>" "This is not recommended unless you are in a trusted network." "<br></li><ol>" % (e.hostname)) mbox.addButton("Ignore", QMessageBox.NoRole) mbox.addButton("Update", QMessageBox.YesRole) elif isinstance(e, UnknownHostKeyException): mbox.setDetailedText(str(e)) mbox.setInformativeText( "<big>The host at %s is unknown.<br>" "This usually happens if this is the first time you use ssh with your tablet.</big><br><br>" "You have three options to fix this permanently:" "<ol><li>" "Press Add to add the key to the known hosts." "<br></li><li>" "Change your <code>~/.ssh/known_hosts</code> file to match the new fingerprint.<br>" "The easiest way to do this is connecting manually via ssh and follow the instructions." "<br></li><li>" "Set <code>\"host_key_policy\": \"ignore_new\"</code> in the <code>ssh</code> section of rmView\'s settings.<br>" "This is not recommended unless you are in a trusted network." "<br></li><ol>" % (e.hostname)) mbox.addButton("Ignore", QMessageBox.NoRole) mbox.addButton("Add", QMessageBox.YesRole) else: mbox.setInformativeText( "I could not connect to the reMarkable at %s:\n%s." % (self.config.get('ssh').get('address'), e)) mbox.addButton(QMessageBox.Retry) mbox.setDefaultButton(QMessageBox.Retry) answer = mbox.exec() if answer == QMessageBox.Retry: self.requestConnect() elif answer == QMessageBox.Cancel: self.quit() elif answer == 1: # Ignore self.requestConnect(host_key_policy="ignore_all") elif answer == 2: # Add/Update if not os.path.isfile(self.LOCAL_KNOWN_HOSTS): open(self.LOCAL_KNOWN_HOSTS, 'a').close() hk = HostKeys(self.LOCAL_KNOWN_HOSTS) hk.add(e.hostname, e.key.get_name(), e.key) hk.save(self.LOCAL_KNOWN_HOSTS) log.info("Saved host key in %s", self.LOCAL_KNOWN_HOSTS) self.requestConnect() else: self.openSettings(prompt=False) self.quit()
def add_to_paramiko_host_keys(self, host_keys: paramiko.HostKeys): for key_type, host_key in self.host_keys.items(): host_keys.add(hostname=host_key.paramiko_host_entry, keytype=host_key.paramiko_key_type, key=host_key.paramiko_key)
def ssh_connect(openstack_properties): """Create a connection to a server via SSH. Args: openstack_properties (dict): OpenStack facts and variables from Ansible which can be used to manipulate OpenStack objects. Returns: def: A factory function object. """ connections = [] # Track inventory of SSH connections for teardown. def _factory(hostname, username, retries=10, key_filename=None, auth_timeout=180): """Connect to a server via SSH. Note: this function uses an exponential back-off for retries which means the more retries specified the longer the wait between each retry. The total wait time is on the fibonacci sequence. (https://bit.ly/1ee23o9) Args: hostname (str): The server to connect to. username (str): The username to authenticate as. (defaults to the current local username) retries (int): The maximum number of validation retry attempts. key_filename (str): The filename, or list of filenames, of optional private key(s) and/or certs to try for authentication. (Default is to use the 'rpc_support' key. auth_timeout (float): An optional timeout (in seconds) to wait for an authentication response. Returns: paramiko.client.SSHClient: A client already connected to the target server. Raises: paramiko.BadHostKeyException: If the server’s host key could not be verified. paramiko.AuthenticationException: If authentication failed. paramiko.SSHException: If there was any other error connecting or establishing an SSH session. paramiko.ssh_exception.NoValidConnectionsError: Connection refused by host. (SSH service is probably not running or host is not fully booted) socket.error: If a socket error occurred while connecting. """ temp_connection = SSHClient() temp_connection.set_missing_host_key_policy(AutoAddPolicy()) for attempt in range(1, retries + 1): try: temp_connection.connect( hostname=hostname, username=username, key_filename=( key_filename or openstack_properties['private_key_path'] ), auth_timeout=auth_timeout ) except NoValidConnectionsError: if attempt != retries + 1: sleep(attempt) else: raise # Re-raise connections.append(temp_connection) return temp_connection yield _factory # Teardown for connection in connections: connection.close() HostKeys().clear() # Clear the 'known_hosts' file.
def retryInit(self, e): log.error('RETRY? [%s]', e) mbox = QMessageBox(QMessageBox.NoIcon, 'Connection error', "Connection attempt failed") mbox.addButton("Settings…", QMessageBox.ResetRole) mbox.addButton(QMessageBox.Cancel) if isinstance(e, BadHostKeyException): mbox.setIconPixmap(_mkIcon(":/assets/128/security-low.svg")) mbox.setDetailedText(str(e)) mbox.setInformativeText( "<big>The host at %s has the wrong key.<br>" "This usually happens just after a software update on the tablet.</big><br><br>" "You have three options to fix this permanently:" "<ol><li>" "Press Update to replace the old key with the new." "<br></li><li>" "Change your <code>~/.ssh/known_hosts</code> file to match the new fingerprint.<br>" "The easiest way to do this is connecting manually via ssh and follow the instructions." "<br></li><li>" "Set <code>\"host_key_policy\": \"ignore_new\"</code> in the appropriate source of Remy\'s settings.<br>" "This is not recommended unless you are in a trusted network." "<br></li><ol>" % (e.hostname) ) mbox.addButton("Ignore", QMessageBox.NoRole) mbox.addButton("Update", QMessageBox.YesRole) elif isinstance(e, UnknownHostKeyException): mbox.setIconPixmap(_mkIcon(":/assets/128/security-high.svg")) mbox.setDetailedText(str(e)) mbox.setInformativeText( "<big>The host at %s is unknown.<br>" "This usually happens if this is the first time you use ssh with your tablet.</big><br><br>" "You have three options to fix this permanently:" "<ol><li>" "Press Add to add the key to the known hosts." "<br></li><li>" "Change your <code>~/.ssh/known_hosts</code> file to match the new fingerprint.<br>" "The easiest way to do this is connecting manually via ssh and follow the instructions." "<br></li><li>" "Set <code>\"host_key_policy\": \"ignore_new\"</code> in the appropriate source of Remy\'s settings.<br>" "This is not recommended unless you are in a trusted network." "<br></li><ol>" % (e.hostname) ) mbox.addButton("Ignore", QMessageBox.NoRole) mbox.addButton("Add", QMessageBox.YesRole) else: mbox.setIconPixmap(_mkIcon(":/assets/dead.svg")) mbox.setInformativeText("I could not connect to the reMarkable at %s:\n%s." % (self.config.get('host', '[no source selected]'), e)) d=mbox.addButton(QMessageBox.Discard) d.setText("Source…") mbox.addButton(QMessageBox.Retry) mbox.setDefaultButton(QMessageBox.Retry) answer = mbox.exec() log.info(answer) if answer == QMessageBox.Retry: self.requestInit() elif answer == QMessageBox.Cancel: self.quit() elif answer == QMessageBox.Discard: # Sources selection source, ok = self.sourceSelectionBox() if not ok: self.quit() else: self.config.selectSource(source) self.requestInit() elif answer == 1: # Ignore self.requestInit(host_key_policy="ignore_all") elif answer == 2: # Add/Update local_kh = self.paths.known_hosts if not local_kh.is_file(): open(local_kh, 'a').close() from paramiko import HostKeys hk = HostKeys(local_kh) hk.add(e.hostname, e.key.get_name(), e.key) hk.save(local_kh) log.info("Saved host key in %s", local_kh) self.requestInit() else: self.openSettings(prompt=False) self.quit()
def _get_hostkey(args): host = args['HOST'] file = args['FILE'] hash_ = args['--hash'] try: port = strings.str2port(args['--port']) with Transport((host, port)) as transport: transport.start_client() hostkey = transport.get_remote_server_key() name = hostkey.get_name().split('-', 1)[1].upper() # same fingerprints as the OpenSSH commands generate print(f'{name} ({hostkey.get_bits()}) Fingerprints:') fp_md5 = hashlib.md5() fp_md5.update(hostkey.asbytes()) fp_md5_dig = strings.insert_separator(fp_md5.hexdigest(), ':', 2) print(f' MD5: {fp_md5_dig}') fp_sha = hashlib.sha256() fp_sha.update(hostkey.asbytes()) fp_sha_dig = base64.b64encode(fp_sha.digest()).decode().strip('=') print(f' SHA256: {fp_sha_dig}') while True: a = input(f'Save this key to file "{file}" (yes/no)? ').lower() if a in ('yes', 'no'): break print('Type "yes" or "no"!') if a != 'no': hostname = utils.format_knownhost(host, port) hostkeys = HostKeys() addkey = True if os.path.exists(file): hostkeys.load(file) if hostkeys.lookup(hostname): if hostkeys.check(hostname, hostkey): print(f'Key for "{hostname}" exists' f' in file "{file}"') addkey = False else: del hostkeys[hostname] print(f'Key for "{hostname}" replaced' f' in file "{file}"') else: print(f'Key for "{hostname}" added in file "{file}"') else: print(f'Key for "{hostname}" added in new file "{file}"') if addkey: if hash_: hostname = HostKeys.hash_host(hostname) hostkeys.add(hostname, hostkey.get_name(), hostkey) hostkeys.save(file) except ConfigError as ex: print(ex, file=sys.stderr) return ConfigError.code except (OSError, SSHException) as ex: print(ex, file=sys.stderr) return ConnectError.code except Exception as ex: print(repr(ex), file=sys.stderr) return ExitCodes.FAILURE.code return ExitCodes.SUCCESS.code