mirror of
https://github.com/borgbase/vorta
synced 2024-12-21 23:33:13 +00:00
Remove paramiko dependency (#1606)
Paramiko is a encryption key parsing library. It was used for determining which ssh keys are available on the system. This removes that fairly heavy dependency at replaces it with a very basic heuristic to determine ssh key file by their first line containing `-----BEGIN(\s\w+)? PRIVATE KEY-----`. * src/vorta/utils.py: Implement `is_ssh_private_key_file`. * src/vorta/utils.py (get_private_keys): Use `is_ssh_private_key_file` instead of paramiko. Enforce `077` permissions on key files. * src/vorta/views/ssh_dialog.py : Remove paramiko. * src/vorta/views/repo_tab.py (RepoTab.init_ssh): Show filename only in `sshComboBox`. * src/vorta/views/repo_add_dialog.py (AddRepoWindow.init_ssh_key): Show filename only in `sshComboBox`.
This commit is contained in:
parent
c4d16e250d
commit
1f1278270d
4 changed files with 33 additions and 45 deletions
|
@ -10,12 +10,8 @@
|
|||
import unicodedata
|
||||
from datetime import datetime as dt
|
||||
from functools import reduce
|
||||
from typing import Any, Callable, Iterable, Optional, Tuple, TypeVar
|
||||
from typing import Any, Callable, Iterable, List, Optional, Tuple, TypeVar
|
||||
import psutil
|
||||
from paramiko import SSHException
|
||||
from paramiko.ecdsakey import ECDSAKey
|
||||
from paramiko.ed25519key import Ed25519Key
|
||||
from paramiko.rsakey import RSAKey
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtCore import QFileInfo, QThread, pyqtSignal
|
||||
from PyQt5.QtWidgets import QApplication, QFileDialog, QSystemTrayIcon
|
||||
|
@ -179,9 +175,19 @@ def choose_file_dialog(parent, title, want_folder=True):
|
|||
return dialog
|
||||
|
||||
|
||||
def get_private_keys():
|
||||
def is_ssh_private_key_file(filepath: str) -> bool:
|
||||
"""Check if the file is a SSH key."""
|
||||
try:
|
||||
with open(filepath, 'r') as f:
|
||||
first_line = f.readline()
|
||||
pattern = r'^-----BEGIN(\s\w+)? PRIVATE KEY-----'
|
||||
return re.match(pattern, first_line) is not None
|
||||
except UnicodeDecodeError:
|
||||
return False
|
||||
|
||||
|
||||
def get_private_keys() -> List[str]:
|
||||
"""Find SSH keys in standard folder."""
|
||||
key_formats = [RSAKey, ECDSAKey, Ed25519Key]
|
||||
|
||||
ssh_folder = os.path.expanduser('~/.ssh')
|
||||
|
||||
|
@ -191,35 +197,25 @@ def get_private_keys():
|
|||
key_file = os.path.join(ssh_folder, key)
|
||||
if not os.path.isfile(key_file):
|
||||
continue
|
||||
for key_format in key_formats:
|
||||
try:
|
||||
parsed_key = key_format.from_private_key_file(key_file)
|
||||
key_details = {
|
||||
'filename': key,
|
||||
'format': parsed_key.get_name(),
|
||||
'bits': parsed_key.get_bits(),
|
||||
'fingerprint': parsed_key.get_fingerprint().hex(),
|
||||
}
|
||||
available_private_keys.append(key_details)
|
||||
except (
|
||||
SSHException,
|
||||
UnicodeDecodeError,
|
||||
IsADirectoryError,
|
||||
IndexError,
|
||||
ValueError,
|
||||
PermissionError,
|
||||
NotImplementedError,
|
||||
):
|
||||
logger.debug(
|
||||
f'Expected error parsing file in .ssh: {key} (You can safely ignore this)', exc_info=True
|
||||
)
|
||||
continue
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENXIO:
|
||||
# when key_file is a (ControlPath) socket
|
||||
continue
|
||||
# ignore config, known_hosts*, *.pub, etc.
|
||||
if key.endswith('.pub') or key.startswith('known_hosts') or key == 'config':
|
||||
continue
|
||||
try:
|
||||
if is_ssh_private_key_file(key_file):
|
||||
if os.stat(key_file).st_mode & 0o077 == 0:
|
||||
available_private_keys.append(key)
|
||||
else:
|
||||
raise
|
||||
logger.warning(f'Permissions for {key_file} are too open.')
|
||||
else:
|
||||
logger.debug(f'Not a private SSH key file: {key}')
|
||||
except PermissionError:
|
||||
logger.warning(f'Permission error while opening file: {key_file}', exc_info=True)
|
||||
continue
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENXIO:
|
||||
# when key_file is a (ControlPath) socket
|
||||
continue
|
||||
raise
|
||||
|
||||
return available_private_keys
|
||||
|
||||
|
|
|
@ -182,10 +182,7 @@ def init_encryption(self):
|
|||
def init_ssh_key(self):
|
||||
keys = get_private_keys()
|
||||
for key in keys:
|
||||
self.sshComboBox.addItem(
|
||||
f'{key["filename"]} ({key["format"]}:{key["fingerprint"]})',
|
||||
key['filename'],
|
||||
)
|
||||
self.sshComboBox.addItem(f'{key}', key)
|
||||
|
||||
def validate(self):
|
||||
"""Pre-flight check for valid input and borg binary."""
|
||||
|
|
|
@ -175,7 +175,7 @@ def init_ssh(self):
|
|||
self.sshComboBox.clear()
|
||||
self.sshComboBox.addItem(self.tr('Automatically choose SSH Key (default)'), None)
|
||||
for key in keys:
|
||||
self.sshComboBox.addItem(f'{key["filename"]} ({key["format"]})', key['filename'])
|
||||
self.sshComboBox.addItem(f'{key}', key)
|
||||
|
||||
def toggle_available_compression(self):
|
||||
use_zstd = borg_compat.check('ZSTD')
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import os
|
||||
from paramiko.ecdsakey import ECDSAKey
|
||||
from paramiko.ed25519key import Ed25519Key
|
||||
from paramiko.rsakey import RSAKey
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtCore import QProcess, Qt
|
||||
from PyQt5.QtWidgets import QApplication, QDialogButtonBox
|
||||
|
@ -10,8 +7,6 @@
|
|||
uifile = get_asset('UI/sshadd.ui')
|
||||
SSHAddUI, SSHAddBase = uic.loadUiType(uifile)
|
||||
|
||||
FORMAT_MAPPING = {'ed25519': Ed25519Key, 'rsa': RSAKey, 'ecdsa': ECDSAKey}
|
||||
|
||||
|
||||
class SSHAddWindow(SSHAddBase, SSHAddUI):
|
||||
def __init__(self):
|
||||
|
|
Loading…
Reference in a new issue