Пример #1
0
class SSH(AbstractCommunicator):
    CONFIGURATION_KEYS = ("host_key", "host_key_type", "username",
                          "private_key", "private_key_type")
    SUPPORTED_HOST_TYPES = ("Darwin", "Linux_AMD64", "Windows")
    IDENTIFIER = "SSH"

    PRIVATE_KEY_FORMAT_MAPPINGS = {
        'DSS': paramiko.DSSKey.from_private_key,
        'RSA': paramiko.RSAKey.from_private_key,
        'ECDSA': paramiko.ECDSAKey.from_private_key,
        'Ed25519': paramiko.Ed25519Key.from_private_key
    }

    HOST_KEY_FORMAT_MAPPINGS = {
        'ssh-dss': paramiko.DSSKey,
        'ssh-rsa': paramiko.RSAKey,
        'ssh-ed25519': paramiko.Ed25519Key
    }
    for key in ECDSAKey.supported_key_format_identifiers():
        HOST_KEY_FORMAT_MAPPINGS[key] = paramiko.ECDSAKey

    def __init__(self, host):
        super().__init__(host)
        self.username = self.config["username"]

    def get_host_key(self) -> PKey:
        # Decide what kind of key we're looking at and create an object
        # to hold it accordingly.
        key_type = self.host.config['host_key_type']
        if key_type not in self.HOST_KEY_FORMAT_MAPPINGS:
            raise SSHError(f"Unable to handle key of type {key_type}")

        try:
            key = self.host.config['host_key'].encode('utf-8')
            host_key = self.HOST_KEY_FORMAT_MAPPINGS[key_type](
                data=decodebytes(key))
        except binascii.Error as e:
            raise InvalidHostKey(repr(self.host), e)
        return host_key

    def get_private_key(self):
        # Decide what kind of key we're looking at and create an object
        # to hold it accordingly.
        key_type = self.config['private_key_type']
        if key_type not in self.PRIVATE_KEY_FORMAT_MAPPINGS:
            raise SSHError(f"Unable to handle key of type {key_type}")

        try:
            key = self.config['private_key']
            private_key = self.PRIVATE_KEY_FORMAT_MAPPINGS[key_type](
                StringIO(key))
        except binascii.Error as e:
            raise SSHError(f"Private key error {repr(self.host)}", e)
        return private_key

    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 execute_command(self, command: str) -> CommandResponse:
        client = self.get_client()
        try:
            client.connect(self.host.address,
                           username=self.config["username"],
                           pkey=self.get_private_key(),
                           timeout=settings.SSH_CONNECT_TIMEOUT)
            stdin, stdout, stderr = client.exec_command(
                command=command, timeout=settings.SSH_EXEC_TIMEOUT)
            return_code = stdout.channel.recv_exit_status()
            stdout_str = stdout.read().decode('UTF-8')
            stderr_str = stderr.read().decode('UTF-8')
            if return_code != 0:
                logger.info(
                    f"host={self.username}@{self.host.address} rc={return_code} command={command} stdout={stdout_str} stderr={stderr_str}"
                )
        except paramiko.SSHException as e:
            logger.exception(
                f"Error: host={self.username}@{self.host.address}, command={command}"
            )
            raise SSHError(
                f"Ran into problems connecting to {self.username}@{self.host.address}: {e}"
            )
        finally:
            client.close()

        return CommandResponse(return_code, stdout_str, stderr_str)

    def is_host_reachable(self) -> bool:
        try:
            if self.host.type == "Windows":
                response = self.execute_command('date /t')
            else:
                response = self.execute_command('true')
        except Exception:
            return False
        return True
Пример #2
0
from ...command_util import GenericUserError
from .. import URLConfig
from ..archive import ensure_archive

from . import Uploader
KEYTYPES = {
    'RSA': RSAKey,
    'ECDSA': ECDSAKey,
    'DSA': DSSKey,
    'DSS': DSSKey,
    'ED25519': Ed25519Key
}

KNOWN_HOSTS_KEY_TYPE_MAP = {
    'RSA': ('ssh-rsa', ),
    'ECDSA': tuple(ECDSAKey.supported_key_format_identifiers()),
    'DSA': ('ssh-dss', ),
    'DSS': ('ssh-dss', ),
    'ED25519': ('ssh-ed25519', )
}


class DumbSFTPUploader(Uploader):
    '''
    A dumb SFTP uploader that just sends a bundle to a remote directory.

    The server has to decide what to do with the bundle (e.g., putting it where it can be
    downloaded)
    '''
    def __init__(self, upload_url):
        '''