Пример #1
0
 def _init(self, retries=1):
     self.session = Session()
     if self.timeout:
         # libssh2 timeout is in ms
         self.session.set_timeout(self.timeout * 1000)
     try:
         self.session.handshake(self.sock)
     except Exception as ex:
         while retries < self.num_retries:
             return self._connect_init_retry(retries)
         msg = "Error connecting to host %s:%s - %s"
         logger.error(msg, self.host, self.port, ex)
         if isinstance(ex, SSH2Timeout):
             raise Timeout(msg, self.host, self.port, ex)
         raise
     try:
         self.auth()
     except Exception as ex:
         while retries < self.num_retries:
             return self._connect_init_retry(retries)
         msg = "Authentication error while connecting to %s:%s - %s"
         raise AuthenticationException(msg, self.host, self.port, ex)
     self.session.set_blocking(0)
     if self.keepalive_seconds:
         self.configure_keepalive()
         self._keepalive_greenlet = self.spawn_send_keepalive()
Пример #2
0
 def _connect(self):
     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     sock.connect((self._host, self._port))
     self._sess = Session()
     self._sess.handshake(sock)
     if self._auth == 'key':
         # self._pubkey_path must be generated from private key
         # s.userauth_publickey_fromfile(self._user, self._pubkey_path,
         #                               self._key_path, '')
         raise IrmaSFTPv2Error("Pub key authentication not implemented")
     else:
         self._sess.userauth_password(self._user, self._passwd)
     self._client = self._sess.sftp_init()
Пример #3
0
def sshconn(host, user, password, cmd):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, 22))
    session = Session()
    session.handshake(sock)
    session.userauth_password(user, password)

    channel = session.open_session()
    channel.execute(cmd)

    size, data = channel.read()
    while size > 0:
        print(data.decode())
        size, data = channel.read()
    channel.close()
    a = channel.get_exit_status()
    if a == 0:
        logger.info("Exit status: {0}".format(channel.get_exit_status()))
    else:
        logger.error("Exit status: {0} - verify the command".format(
            channel.get_exit_status()))
Пример #4
0
def main():
    args = parser.parse_args()
    if not os.path.isfile(args.privatekey):
        print("No such private key %s" % (args.privatekey, ))
        sys.exit(1)
    publickey = "%s.pub" % (args.privatekey, )
    if not os.path.isfile(publickey):
        print("Expected public key at %s, found none" % (publickey, ))
        sys.exit(1)

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((args.host, args.port))
    s = Session()
    s.handshake(sock)
    s.userauth_publickey_fromfile(args.user, publickey, args.privatekey,
                                  args.passphrase)
    chan = s.open_session()
    chan.execute(args.cmd)
    size, data = chan.read()
    while size > 0:
        print(data)
        size, data = chan.read()
Пример #5
0
def main():
    user = '******'
    host = '192.168.100.201'
    # publickey = os.path.expanduser('~\\.ssh\\id_rsa-sag-golddrive.pub')
    privatekey = os.path.expanduser('~\\.ssh\\id_rsa-sag-golddrive')
    if not os.path.isfile(privatekey):
        print("No such private key %s" % (privatekey,))
    if not os.path.isfile(privatekey):
        print("No such public key %s" % (publickey,))
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, 22))
    s = Session()
    s.handshake(sock)
    s.userauth_publickey_fromfile(user, privatekey)   
    sftp = s.sftp_init()
    now = datetime.now()
    print("Starting read for remote file...")
    with sftp.open('/tmp/file.bin', 
        LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR) as r, \
        open('file.bin','wb') as w:
        for size, data in r:
            w.write(data)
    print("Finished file read in %s" % (datetime.now() - now,))
Пример #6
0
class SSHClient(object):
    """ssh2-python (libssh2) based non-blocking SSH client."""

    IDENTITIES = (
        os.path.expanduser('~/.ssh/id_rsa'),
        os.path.expanduser('~/.ssh/id_dsa'),
        os.path.expanduser('~/.ssh/identity'),
        os.path.expanduser('~/.ssh/id_ecdsa'),
    )

    def __init__(self, host,
                 user=None, password=None, port=None,
                 pkey=None,
                 num_retries=DEFAULT_RETRIES,
                 retry_delay=RETRY_DELAY,
                 allow_agent=True, timeout=None,
                 forward_ssh_agent=False,
                 proxy_host=None,
                 _auth_thread_pool=True, keepalive_seconds=60):
        """:param host: Host name or IP to connect to.
        :type host: str
        :param user: User to connect as. Defaults to logged in user.
        :type user: str
        :param password: Password to use for password authentication.
        :type password: str
        :param port: SSH port to connect to. Defaults to SSH default (22)
        :type port: int
        :param pkey: Private key file path to use for authentication. Path must
          be either absolute path or relative to user home directory
          like ``~/<path>``.
        :type pkey: str
        :param num_retries: (Optional) Number of connection and authentication
          attempts before the client gives up. Defaults to 3.
        :type num_retries: int
        :param retry_delay: Number of seconds to wait between retries. Defaults
          to :py:class:`pssh.constants.RETRY_DELAY`
        :type retry_delay: int
        :param timeout: SSH session timeout setting in seconds. This controls
          timeout setting of authenticated SSH sessions.
        :type timeout: int
        :param allow_agent: (Optional) set to False to disable connecting to
          the system's SSH agent
        :type allow_agent: bool
        :param forward_ssh_agent: (Optional) Turn on SSH agent forwarding -
          equivalent to `ssh -A` from the `ssh` command line utility.
          Defaults to True if not set.
        :type forward_ssh_agent: bool
        :param proxy_host: Connection to host is via provided proxy host
          and client should use self.proxy_host for connection attempts.
        :type proxy_host: str
        :param keepalive_seconds: Interval of keep alive messages being sent to
          server. Set to ``0`` or ``False`` to disable.

        :raises: :py:class:`pssh.exceptions.PKeyFileError` on errors finding
          provided private key.
        """
        self.host = host
        self.user = user if user else None
        if self.user is None and not WIN_PLATFORM:
            self.user = pwd.getpwuid(os.geteuid()).pw_name
        elif self.user is None and WIN_PLATFORM:
            raise ValueError("Must provide user parameter on Windows")
        self.password = password
        self.port = port if port else 22
        self.num_retries = num_retries
        self.sock = None
        self.timeout = timeout
        self.retry_delay = retry_delay
        self.allow_agent = allow_agent
        self.forward_ssh_agent = forward_ssh_agent
        self._forward_requested = False
        self.session = None
        self.keepalive_seconds = keepalive_seconds
        self._keepalive_greenlet = None
        self._host = proxy_host if proxy_host else host
        self.pkey = _validate_pkey_path(pkey, self.host)
        self._connect(self._host, self.port)
        if _auth_thread_pool:
            THREAD_POOL.apply(self._init)
        else:
            self._init()

    def disconnect(self):
        """Disconnect session, close socket if needed."""
        if self.session is not None:
            try:
                self._eagain(self.session.disconnect)
            except Exception:
                pass
        if self.sock is not None and not self.sock.closed:
            self.sock.close()

    def __del__(self):
        self.disconnect()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.disconnect()

    def spawn_send_keepalive(self):
        """Spawns a new greenlet that sends keep alive messages every
        self.keepalive_seconds"""
        return spawn(self._send_keepalive)

    def _send_keepalive(self):
        while True:
            sleep(self._eagain(self.session.keepalive_send))

    def configure_keepalive(self):
        self.session.keepalive_config(False, self.keepalive_seconds)

    def _connect_init_retry(self, retries):
        retries += 1
        self.session = None
        if not self.sock.closed:
            self.sock.close()
        sleep(self.retry_delay)
        self._connect(self._host, self.port, retries=retries)
        return self._init(retries=retries)

    def _init(self, retries=1):
        self.session = Session()
        if self.timeout:
            # libssh2 timeout is in ms
            self.session.set_timeout(self.timeout * 1000)
        try:
            self.session.handshake(self.sock)
        except Exception as ex:
            while retries < self.num_retries:
                return self._connect_init_retry(retries)
            msg = "Error connecting to host %s:%s - %s"
            logger.error(msg, self.host, self.port, ex)
            if isinstance(ex, SSH2Timeout):
                raise Timeout(msg, self.host, self.port, ex)
            raise
        try:
            self.auth()
        except Exception as ex:
            while retries < self.num_retries:
                return self._connect_init_retry(retries)
            msg = "Authentication error while connecting to %s:%s - %s"
            raise AuthenticationException(msg, self.host, self.port, ex)
        self.session.set_blocking(0)
        if self.keepalive_seconds:
            self.configure_keepalive()
            self._keepalive_greenlet = self.spawn_send_keepalive()

    def _connect(self, host, port, retries=1):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        if self.timeout:
            self.sock.settimeout(self.timeout)
        logger.debug("Connecting to %s:%s", host, port)
        try:
            self.sock.connect((host, port))
        except sock_gaierror as ex:
            logger.error("Could not resolve host '%s' - retry %s/%s",
                         host, retries, self.num_retries)
            while retries < self.num_retries:
                sleep(self.retry_delay)
                return self._connect(host, port, retries=retries+1)
            raise UnknownHostException("Unknown host %s - %s - retry %s/%s",
                                       host, str(ex.args[1]), retries,
                                       self.num_retries)
        except sock_error as ex:
            logger.error("Error connecting to host '%s:%s' - retry %s/%s",
                         host, port, retries, self.num_retries)
            while retries < self.num_retries:
                sleep(self.retry_delay)
                return self._connect(host, port, retries=retries+1)
            error_type = ex.args[1] if len(ex.args) > 1 else ex.args[0]
            raise ConnectionErrorException(
                "Error connecting to host '%s:%s' - %s - retry %s/%s",
                host, port, str(error_type), retries,
                self.num_retries,)

    def _pkey_auth(self):
        self.session.userauth_publickey_fromfile(
            self.user,
            self.pkey,
            passphrase=self.password if self.password is not None else '')

    def _identity_auth(self):
        passphrase = self.password if self.password is not None else ''
        for identity_file in self.IDENTITIES:
            if not os.path.isfile(identity_file):
                continue
            logger.debug(
                "Trying to authenticate with identity file %s",
                identity_file)
            try:
                self.session.userauth_publickey_fromfile(
                    self.user,
                    identity_file,
                    passphrase=passphrase)
            except Exception:
                logger.debug("Authentication with identity file %s failed, "
                             "continuing with other identities",
                             identity_file)
                continue
            else:
                logger.debug("Authentication succeeded with identity file %s",
                             identity_file)
                return
        raise AuthenticationException("No authentication methods succeeded")

    def auth(self):
        if self.pkey is not None:
            logger.debug(
                "Proceeding with private key file authentication")
            return self._pkey_auth()
        if self.allow_agent:
            try:
                self.session.agent_auth(self.user)
            except Exception as ex:
                logger.debug("Agent auth failed with %s, "
                             "continuing with other authentication methods",
                             ex)
            else:
                logger.debug("Authentication with SSH Agent succeeded")
                return
        try:
            self._identity_auth()
        except AuthenticationException:
            if self.password is None:
                raise
            logger.debug("Private key auth failed, trying password")
            self._password_auth()

    def _password_auth(self):
        try:
            self.session.userauth_password(self.user, self.password)
        except Exception:
            raise AuthenticationException("Password authentication failed")

    def open_session(self):
        """Open new channel from session"""
        try:
            chan = self.session.open_session()
        except Exception as ex:
            raise SessionError(ex)
        while chan == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session)
            try:
                chan = self.session.open_session()
            except Exception as ex:
                raise SessionError(ex)
        # Multiple forward requests result in ChannelRequestDenied
        # errors, flag is used to avoid this.
        if self.forward_ssh_agent and not self._forward_requested:
            if not hasattr(chan, 'request_auth_agent'):
                warn("Requested SSH Agent forwarding but libssh2 version used "
                     "does not support it - ignoring")
                return chan
            self._eagain(chan.request_auth_agent)
            self._forward_requested = True
        return chan

    def execute(self, cmd, use_pty=False, channel=None):
        """Execute command on remote server.

        :param cmd: Command to execute.
        :type cmd: str
        :param use_pty: Whether or not to obtain a PTY on the channel.
        :type use_pty: bool
        :param channel: Use provided channel for execute rather than creating
          a new one.
        :type channel: :py:class:`ssh2.channel.Channel`
        """
        channel = self.open_session() if channel is None else channel
        if use_pty:
            self._eagain(channel.pty)
        logger.debug("Executing command '%s'" % cmd)
        self._eagain(channel.execute, cmd)
        return channel

    def read_stderr(self, channel, timeout=None):
        """Read standard error buffer from channel.

        :param channel: Channel to read output from.
        :type channel: :py:class:`ssh2.channel.Channel`
        """
        return _read_output(self.session, channel.read_stderr, timeout=timeout)

    def read_output(self, channel, timeout=None):
        """Read standard output buffer from channel.

        :param channel: Channel to read output from.
        :type channel: :py:class:`ssh2.channel.Channel`
        """
        return _read_output(self.session, channel.read, timeout=timeout)

    def _select_timeout(self, func, timeout):
        ret = func()
        while ret == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session, timeout=timeout)
            ret = func()
            if ret == LIBSSH2_ERROR_EAGAIN and timeout is not None:
                raise Timeout

    def wait_finished(self, channel, timeout=None):
        """Wait for EOF from channel and close channel.

        Used to wait for remote command completion and be able to gather
        exit code.

        :param channel: The channel to use.
        :type channel: :py:class:`ssh2.channel.Channel`
        """
        if channel is None:
            return
        # If .eof() returns EAGAIN after a select with a timeout, it means
        # it reached timeout without EOF and _select_timeout will raise
        # timeout exception causing the channel to appropriately
        # not be closed as the command is still running.
        self._select_timeout(channel.wait_eof, timeout)
        # Close channel to indicate no more commands will be sent over it
        self.close_channel(channel)

    def close_channel(self, channel):
        logger.debug("Closing channel")
        self._eagain(channel.close)

    def _eagain(self, func, *args, **kwargs):
        ret = func(*args, **kwargs)
        while ret == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session)
            ret = func(*args, **kwargs)
        return ret

    def read_output_buffer(self, output_buffer, prefix=None,
                           callback=None,
                           callback_args=None,
                           encoding='utf-8'):
        """Read from output buffers and log to ``host_logger``.

        :param output_buffer: Iterator containing buffer
        :type output_buffer: iterator
        :param prefix: String to prefix log output to ``host_logger`` with
        :type prefix: str
        :param callback: Function to call back once buffer is depleted:
        :type callback: function
        :param callback_args: Arguments for call back function
        :type callback_args: tuple
        """
        prefix = '' if prefix is None else prefix
        for line in output_buffer:
            output = line.decode(encoding)
            host_logger.info("[%s]%s\t%s", self.host, prefix, output)
            yield output
        if callback:
            callback(*callback_args)

    def run_command(self, command, sudo=False, user=None,
                    use_pty=False, shell=None,
                    encoding='utf-8', timeout=None):
        """Run remote command.

        :param command: Command to run.
        :type command: str
        :param sudo: Run command via sudo as super-user.
        :type sudo: bool
        :param user: Run command as user via sudo
        :type user: str
        :param use_pty: Whether or not to obtain a PTY on the channel.
        :type use_pty: bool
        :param shell: (Optional) Override shell to use to run command with.
          Defaults to login user's defined shell. Use the shell's command
          syntax, eg `shell='bash -c'` or `shell='zsh -c'`.
        :type shell: str
        :param encoding: Encoding to use for output. Must be valid
          `Python codec <https://docs.python.org/2.7/library/codecs.html>`_
        :type encoding: str

        :rtype: (channel, host, stdout, stderr, stdin) tuple.
        """
        # Fast path for no command substitution needed
        if not sudo and not user and not shell:
            _command = command
        else:
            _command = ''
            if sudo and not user:
                _command = 'sudo -S '
            elif user:
                _command = 'sudo -u %s -S ' % (user,)
            _shell = shell if shell else '$SHELL -c'
            _command += "%s '%s'" % (_shell, command,)
        channel = self.execute(_command, use_pty=use_pty)
        return channel, self.host, \
            self.read_output_buffer(
                self.read_output(channel, timeout=timeout),
                encoding=encoding), \
            self.read_output_buffer(
                self.read_stderr(channel, timeout=timeout), encoding=encoding,
                prefix='\t[err]'), channel

    def _make_sftp(self):
        """Make SFTP client from open transport"""
        try:
            sftp = self.session.sftp_init()
        except Exception as ex:
            raise SFTPError(ex)
        while sftp == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session)
            try:
                sftp = self.session.sftp_init()
            except Exception as ex:
                raise SFTPError(ex)
        return sftp

    def _mkdir(self, sftp, directory):
        """Make directory via SFTP channel

        :param sftp: SFTP client object
        :type sftp: :py:class:`ssh2.sftp.SFTP`
        :param directory: Remote directory to create
        :type directory: str

        :raises: :py:class:`pssh.exceptions.SFTPIOError` on SFTP IO errors
        """
        mode = LIBSSH2_SFTP_S_IRUSR | \
            LIBSSH2_SFTP_S_IWUSR | \
            LIBSSH2_SFTP_S_IXUSR | \
            LIBSSH2_SFTP_S_IRGRP | \
            LIBSSH2_SFTP_S_IROTH | \
            LIBSSH2_SFTP_S_IXGRP | \
            LIBSSH2_SFTP_S_IXOTH
        try:
            self._eagain(sftp.mkdir, directory, mode)
        except SFTPProtocolError as error:
            msg = "Error occured creating directory %s on host %s - %s"
            logger.error(msg, directory, self.host, error)
            raise SFTPIOError(msg, directory, self.host, error)
        logger.debug("Created remote directory %s", directory)

    def copy_file(self, local_file, remote_file, recurse=False,
                  sftp=None, _dir=None):
        """Copy local file to host via SFTP.

        :param local_file: Local filepath to copy to remote host
        :type local_file: str
        :param remote_file: Remote filepath on remote host to copy file to
        :type remote_file: str
        :param recurse: Whether or not to descend into directories recursively.
        :type recurse: bool

        :raises: :py:class:`ValueError` when a directory is supplied to
          ``local_file`` and ``recurse`` is not set
        :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation
          errors
        :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing
          via SFTP
        :raises: :py:class:`IOError` on local file IO errors
        :raises: :py:class:`OSError` on local OS errors like permission denied
        """
        sftp = self._make_sftp() if sftp is None else sftp
        if os.path.isdir(local_file) and recurse:
            return self._copy_dir(local_file, remote_file, sftp)
        elif os.path.isdir(local_file) and not recurse:
            raise ValueError("Recurse must be true if local_file is a "
                             "directory.")
        destination = self._remote_paths_split(remote_file)
        if destination is not None:
            try:
                self._eagain(sftp.stat, destination)
            except (SFTPHandleError, SFTPProtocolError):
                self.mkdir(sftp, destination)
        self.sftp_put(sftp, local_file, remote_file)
        logger.info("Copied local file %s to remote destination %s:%s",
                    local_file, self.host, remote_file)

    def _sftp_put(self, remote_fh, local_file):
        with open(local_file, 'rb', 2097152) as local_fh:
            for data in local_fh:
                eagain_write(remote_fh.write, data, self.session)

    def sftp_put(self, sftp, local_file, remote_file):
        mode = LIBSSH2_SFTP_S_IRUSR | \
               LIBSSH2_SFTP_S_IWUSR | \
               LIBSSH2_SFTP_S_IRGRP | \
               LIBSSH2_SFTP_S_IROTH
        f_flags = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC
        with self._sftp_openfh(
                sftp.open, remote_file, f_flags, mode) as remote_fh:
            try:
                self._sftp_put(remote_fh, local_file)
                # THREAD_POOL.apply(
                #     sftp_put, args=(self.session, remote_fh, local_file))
            except SFTPProtocolError as ex:
                msg = "Error writing to remote file %s - %s"
                logger.error(msg, remote_file, ex)
                raise SFTPIOError(msg, remote_file, ex)

    def mkdir(self, sftp, directory, _parent_path=None):
        """Make directory via SFTP channel.

        Parent paths in the directory are created if they do not exist.

        :param sftp: SFTP client object
        :type sftp: :py:class:`paramiko.sftp_client.SFTPClient`
        :param directory: Remote directory to create
        :type directory: str

        Catches and logs at error level remote IOErrors on creating directory.
        """
        try:
            _dir, sub_dirs = directory.split('/', 1)
        except ValueError:
            _dir = directory.split('/', 1)[0]
            sub_dirs = None
        if not _dir and directory.startswith('/'):
            try:
                _dir, sub_dirs = sub_dirs.split(os.path.sep, 1)
            except ValueError:
                return True
        if _parent_path is not None:
            _dir = '/'.join((_parent_path, _dir))
        try:
            self._eagain(sftp.stat, _dir)
        except (SFTPHandleError, SFTPProtocolError) as ex:
            logger.debug("Stat for %s failed with %s", _dir, ex)
            self._mkdir(sftp, _dir)
        if sub_dirs is not None:
            if directory.startswith('/'):
                _dir = ''.join(('/', _dir))
            return self.mkdir(sftp, sub_dirs, _parent_path=_dir)

    def _copy_dir(self, local_dir, remote_dir, sftp):
        """Call copy_file on every file in the specified directory, copying
        them to the specified remote directory."""
        file_list = os.listdir(local_dir)
        for file_name in file_list:
            local_path = os.path.join(local_dir, file_name)
            remote_path = '/'.join([remote_dir, file_name])
            self.copy_file(local_path, remote_path, recurse=True,
                           sftp=sftp)

    def copy_remote_file(self, remote_file, local_file, recurse=False,
                         sftp=None, encoding='utf-8'):
        """Copy remote file to local host via SFTP.

        :param remote_file: Remote filepath to copy from
        :type remote_file: str
        :param local_file: Local filepath where file(s) will be copied to
        :type local_file: str
        :param recurse: Whether or not to recursively copy directories
        :type recurse: bool
        :param encoding: Encoding to use for file paths.
        :type encoding: str

        :raises: :py:class:`ValueError` when a directory is supplied to
          ``local_file`` and ``recurse`` is not set
        :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation
          errors
        :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors reading
          from SFTP
        :raises: :py:class:`IOError` on local file IO errors
        :raises: :py:class:`OSError` on local OS errors like permission denied
        """
        sftp = self._make_sftp() if sftp is None else sftp
        try:
            self._eagain(sftp.stat, remote_file)
        except (SFTPHandleError, SFTPProtocolError):
            msg = "Remote file or directory %s does not exist"
            logger.error(msg, remote_file)
            raise SFTPIOError(msg, remote_file)
        try:
            dir_h = self._sftp_openfh(sftp.opendir, remote_file)
        except SFTPError:
            pass
        else:
            if not recurse:
                raise ValueError("Recurse must be true if remote_file is a "
                                 "directory.")
            file_list = self._sftp_readdir(dir_h)
            return self._copy_remote_dir(file_list, remote_file,
                                         local_file, sftp,
                                         encoding=encoding)
        destination = os.path.join(os.path.sep, os.path.sep.join(
            [_dir for _dir in local_file.split('/')
             if _dir][:-1]))
        self._make_local_dir(destination)
        self.sftp_get(sftp, remote_file, local_file)
        logger.info("Copied local file %s from remote destination %s:%s",
                    local_file, self.host, remote_file)

    def scp_recv(self, remote_file, local_file, recurse=False, sftp=None,
                 encoding='utf-8'):
        """Copy remote file to local host via SCP.

        Note - Remote directory listings are gather via SFTP when
        ``recurse`` is enabled - SCP lacks directory list support.
        Enabling recursion therefore involves creating an extra SFTP channel
        and requires SFTP support on the server.

        :param remote_file: Remote filepath to copy from
        :type remote_file: str
        :param local_file: Local filepath where file(s) will be copied to
        :type local_file: str
        :param recurse: Whether or not to recursively copy directories
        :type recurse: bool
        :param encoding: Encoding to use for file paths when recursion is
          enabled.
        :type encoding: str

        :raises: :py:class:`pssh.exceptions.SCPError` when a directory is
          supplied to ``local_file`` and ``recurse`` is not set.
        :raises: :py:class:`pssh.exceptions.SCPError` on errors copying file.
        :raises: :py:class:`IOError` on local file IO errors.
        :raises: :py:class:`OSError` on local OS errors like permission denied.
        """
        sftp = self._make_sftp() if (sftp is None and recurse) else sftp
        if recurse:
            try:
                self._eagain(sftp.stat, remote_file)
            except (SFTPHandleError, SFTPProtocolError):
                msg = "Remote file or directory %s does not exist"
                logger.error(msg, remote_file)
                raise SCPError(msg, remote_file)
            try:
                dir_h = self._sftp_openfh(sftp.opendir, remote_file)
            except SFTPError:
                pass
            else:
                file_list = self._sftp_readdir(dir_h)
                return self._scp_recv_dir(file_list, remote_file,
                                          local_file, sftp,
                                          encoding=encoding)
        destination = os.path.join(os.path.sep, os.path.sep.join(
            [_dir for _dir in local_file.split('/')
             if _dir][:-1]))
        self._make_local_dir(destination)
        self._scp_recv(remote_file, local_file)
        logger.info("SCP local file %s from remote destination %s:%s",
                    local_file, self.host, remote_file)

    def _scp_recv(self, remote_file, local_file):
        try:
            (file_chan, fileinfo) = self._eagain(
                self.session.scp_recv2, remote_file)
        except Exception as ex:
            msg = "Error copying file %s from host %s - %s"
            logger.error(msg, remote_file, self.host, ex)
            raise SCPError(msg, remote_file, self.host, ex)
        local_fh = open(local_file, 'wb')
        try:
            total = 0
            size, data = file_chan.read(size=fileinfo.st_size)
            while size == LIBSSH2_ERROR_EAGAIN:
                wait_select(self.session)
                size, data = file_chan.read(size=fileinfo.st_size)
            total += size
            local_fh.write(data)
            while total < fileinfo.st_size:
                size, data = file_chan.read(size=fileinfo.st_size - total)
                while size == LIBSSH2_ERROR_EAGAIN:
                    wait_select(self.session)
                    continue
                total += size
                local_fh.write(data)
            if total != fileinfo.st_size:
                msg = "Error copying data from remote file %s on host %s. " \
                      "Copied %s out of %s total bytes"
                raise SCPError(msg, remote_file, self.host, total,
                               fileinfo.st_size)
        finally:
            local_fh.close()

    def _scp_send_dir(self, local_dir, remote_dir, sftp):
        file_list = os.listdir(local_dir)
        for file_name in file_list:
            local_path = os.path.join(local_dir, file_name)
            remote_path = '/'.join([remote_dir, file_name])
            self.scp_send(local_path, remote_path, recurse=True,
                          sftp=sftp)

    def _scp_recv_dir(self, file_list, remote_dir, local_dir, sftp,
                      encoding='utf-8'):
        for file_name in file_list:
            file_name = file_name.decode(encoding)
            if file_name in ('.', '..'):
                continue
            remote_path = os.path.join(remote_dir, file_name)
            local_path = os.path.join(local_dir, file_name)
            logger.debug("Attempting recursive copy from %s:%s to %s",
                         self.host, remote_path, local_path)
            self.scp_recv(remote_path, local_path, sftp=sftp,
                          recurse=True)

    def scp_send(self, local_file, remote_file, recurse=False, sftp=None):
        """Copy local file to host via SCP.

        Note - Directories are created via SFTP when ``recurse`` is enabled -
        SCP lacks directory create support. Enabling recursion therefore
        involves creating an extra SFTP channel and requires SFTP support on the
        server.

        :param local_file: Local filepath to copy to remote host
        :type local_file: str
        :param remote_file: Remote filepath on remote host to copy file to
        :type remote_file: str
        :param recurse: Whether or not to descend into directories recursively.
        :type recurse: bool

        :raises: :py:class:`ValueError` when a directory is supplied to
          ``local_file`` and ``recurse`` is not set
        :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation
          errors
        :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing
          via SFTP
        :raises: :py:class:`IOError` on local file IO errors
        :raises: :py:class:`OSError` on local OS errors like permission denied
        """
        if os.path.isdir(local_file) and recurse:
            sftp = self._make_sftp() if sftp is None else sftp
            return self._scp_send_dir(local_file, remote_file, sftp)
        elif os.path.isdir(local_file) and not recurse:
            raise ValueError("Recurse must be True if local_file is a "
                             "directory.")
        destination = self._remote_paths_split(remote_file)
        if destination is not None:
            sftp = self._make_sftp() if sftp is None else sftp
            try:
                self._eagain(sftp.stat, destination)
            except (SFTPHandleError, SFTPProtocolError):
                self.mkdir(sftp, destination)
        self._scp_send(local_file, remote_file)
        logger.info("SCP local file %s to remote destination %s:%s",
                    local_file, self.host, remote_file)

    def _scp_send(self, local_file, remote_file):
        fileinfo = os.stat(local_file)
        try:
            chan = self._eagain(
                self.session.scp_send64,
                remote_file, fileinfo.st_mode & 0o777, fileinfo.st_size,
                fileinfo.st_mtime, fileinfo.st_atime)
        except Exception as ex:
            msg = "Error opening remote file %s for writing on host %s - %s"
            logger.error(msg, remote_file, self.host, ex)
            raise SCPError(msg, remote_file, self.host, ex)
        try:
            with open(local_file, 'rb', 2097152) as local_fh:
                for data in local_fh:
                    eagain_write(chan.write, data, self.session)
        except Exception as ex:
            msg = "Error writing to remote file %s on host %s - %s"
            logger.error(msg, remote_file, self.host, ex)
            raise SCPError(msg, remote_file, self.host, ex)

    def _sftp_readdir(self, dir_h):
        for size, buf, attrs in dir_h.readdir():
            for line in buf.splitlines():
                yield line

    def _sftp_openfh(self, open_func, remote_file, *args):
        try:
            fh = open_func(remote_file, *args)
        except Exception as ex:
            raise SFTPError(ex)
        while fh == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session, timeout=0.1)
            try:
                fh = open_func(remote_file, *args)
            except Exception as ex:
                raise SFTPError(ex)
        return fh

    def _sftp_get(self, remote_fh, local_file):
        with open(local_file, 'wb') as local_fh:
            for size, data in remote_fh:
                if size == LIBSSH2_ERROR_EAGAIN:
                    wait_select(self.session)
                    continue
                local_fh.write(data)

    def sftp_get(self, sftp, remote_file, local_file):
        with self._sftp_openfh(
                sftp.open, remote_file,
                LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR) as remote_fh:
            try:
                self._sftp_get(remote_fh, local_file)
                # Running SFTP in a thread requires a new session
                # as session handles or any handles created by a session
                # cannot be used simultaneously in multiple threads.
                # THREAD_POOL.apply(
                #     sftp_get, args=(self.session, remote_fh, local_file))
            except SFTPProtocolError as ex:
                msg = "Error reading from remote file %s - %s"
                logger.error(msg, remote_file, ex)
                raise SFTPIOError(msg, remote_file, ex)

    def _copy_remote_dir(self, file_list, remote_dir, local_dir, sftp,
                         encoding='utf-8'):
        for file_name in file_list:
            file_name = file_name.decode(encoding)
            if file_name in ('.', '..'):
                continue
            remote_path = os.path.join(remote_dir, file_name)
            local_path = os.path.join(local_dir, file_name)
            self.copy_remote_file(remote_path, local_path, sftp=sftp,
                                  recurse=True)

    def _make_local_dir(self, dirpath):
        if os.path.exists(dirpath):
            return
        try:
            os.makedirs(dirpath)
        except OSError:
            logger.error("Unable to create local directory structure for "
                         "directory %s", dirpath)
            raise

    def _remote_paths_split(self, file_path):
        _sep = file_path.rfind('/')
        if _sep > 0:
            return file_path[:_sep]
        return
Пример #7
0
def login_fun(user="******", password="******", port=22, host='192.168.1.1'):

    #we name aour output file using currency data and hour
    data_obj = datetime.datetime.now()
    data_obj = str(data_obj)
    data_obj = data_obj.replace("-", "_")
    data_obj = data_obj.replace(" ", "_")
    data_obj = data_obj.replace(":", "_")

    print(data_obj[0:16])
    nazwa = "output_all_1" + data_obj[0:16] + ".txt"
    os.system(f"touch {nazwa}")
    directory = os.getcwd()

    filepath = directory + "/" + nazwa

    #we create secound file which get singal above cut_of_signal which we load from keyboard
    nazwa_v2 = "extreme_signal" + data_obj[0:16] + ".txt"
    filepath_v2 = directory + "/" + nazwa_v2

    f = open(filepath, "a")
    main_list = []
    #iteration over all hosts
    for hostname in host:
        #print(type(hostname))
        #print(hostname)
        #print(port)
        #print(type(port))

        #I checked port if is open, if open return return 0, above I first search with nmap open ports
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        if (sock.connect_ex((hostname, port)) == 0):
            print(f"Port {port} is open ")

            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect((hostname, port))

            #we are create our sesssion
            session = Session()  # initializing sesion
            session.handshake(sock)
            #try authorize by login and password which we read from keyboard
            try:
                session.userauth_password(user, password)
            except:
                print("Incorretc login or password")
                continue

            channel = session.open_session()
            # start the interactive shell in the channnel
            channel.shell()
            # channel.write("vcgencmd measure_temp\n")
            # channel.write("clear\n")

            #we search our signal in Ubiquity file
            channel.write("mca-status | grep signal\n")
            channel.write("mca-status | grep chain\n")
            time.sleep(1)

            size, data = channel.read()
            my_list = []
            my_list.append(hostname)
            parametr = data.decode()
            parametr = parametr.split('\r\n')
            parametr.pop(-1)
            my_list.extend(parametr)

            #write to file our output form Ubiquity
            #print(my_list)
            zapis = data.decode()
            f.write(str(my_list))
            f.write("\n")
            main_list.append(my_list)
            channel.close()
            print("Exit status: {0}".format(channel.get_exit_status()))
        else:
            print("Port is close")
            continue
    return main_list, filepath_v2
Пример #8
0
emailMsg = MIMEText('Failed to reach destination')
emailMsg['Subject'] = "Failed to reach host"
emailMsg['From'], emailMsg['To'] = emailAddr

# SSH Host Credentials
host = "HOSTNAME OR IP"
user = "******"
port = 22
password = "******"

# Intialize IPV4 socket stream
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Initate connection
sock.connect((host, port))
session = Session()
session.handshake(sock)
session.userauth_password(user, password)
channel = session.open_session()

# Execute command, limit numner of packets to X.
channel.execute("ping 192.168.1.222 -c 5")

size, data = channel.read()

while size > 0:
    if "Destination Host Unreachable" in data.decode():
        print("Host unreachable")
        SendEmail()
        break
    else:
Пример #9
0
class SSHClient(object):
    """ssh2-python (libssh2) based non-blocking SSH client."""

    IDENTITIES = [
        os.path.expanduser('~/.ssh/id_rsa'),
        os.path.expanduser('~/.ssh/id_dsa'),
        os.path.expanduser('~/.ssh/identity')
    ]

    def __init__(self, host,
                 user=None, password=None, port=None,
                 pkey=None,
                 num_retries=DEFAULT_RETRIES,
                 retry_delay=RETRY_DELAY,
                 allow_agent=True, timeout=None,
                 forward_ssh_agent=True,
                 proxy_host=None,
                 _auth_thread_pool=True):
        """:param host: Host name or IP to connect to.
        :type host: str
        :param user: User to connect as. Defaults to logged in user.
        :type user: str
        :param password: Password to use for password authentication.
        :type password: str
        :param port: SSH port to connect to. Defaults to SSH default (22)
        :type port: int
        :param pkey: Private key file path to use for authentication.
          Note that the public key file
          pair *must* also exist in the same location with name ``<pkey>.pub``
        :type pkey: str
        :param num_retries: (Optional) Number of connection and authentication
          attempts before the client gives up. Defaults to 3.
        :type num_retries: int
        :param retry_delay: Number of seconds to wait between retries. Defaults
          to :py:class:`pssh.constants.RETRY_DELAY`
        :type retry_delay: int
        :param timeout: SSH session timeout setting in seconds. This controls
          timeout setting of authenticated SSH sessions.
        :type timeout: int
        :param allow_agent: (Optional) set to False to disable connecting to
          the system's SSH agent
        :type allow_agent: bool
        :param forward_ssh_agent: (Optional) Turn on SSH agent forwarding -
          equivalent to `ssh -A` from the `ssh` command line utility.
          Defaults to True if not set.
        :type forward_ssh_agent: bool
        :param proxy_host: Connection to host is via provided proxy host
          and client should use self.proxy_host for connection attempts.
        :type proxy_host: str
        """
        self.host = host
        self.user = user if user else None
        if self.user is None and not WIN_PLATFORM:
            self.user = pwd.getpwuid(os.geteuid()).pw_name
        elif self.user is None and WIN_PLATFORM:
            raise ValueError("Must provide user parameter on Windows")
        self.password = password
        self.port = port if port else 22
        self.pkey = pkey
        self.num_retries = num_retries
        self.sock = None
        self.timeout = timeout * 1000 if timeout else None
        self.retry_delay = retry_delay
        self.allow_agent = allow_agent
        self.forward_ssh_agent = forward_ssh_agent
        self._forward_requested = False
        self.session = None
        self._host = proxy_host if proxy_host else host
        self._connect(self._host, self.port)
        if _auth_thread_pool:
            THREAD_POOL.apply(self._init)
        else:
            self._init()

    def disconnect(self):
        """Disconnect session, close socket if needed."""
        if self.session is not None:
            try:
                self._eagain(self.session.disconnect)
            except Exception:
                pass
        if not self.sock.closed:
            self.sock.close()

    def __del__(self):
        self.disconnect()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.disconnect()

    def _connect_init_retry(self, retries):
        retries += 1
        self.session = None
        if not self.sock.closed:
            self.sock.close()
        sleep(self.retry_delay)
        self._connect(self._host, self.port, retries=retries)
        return self._init(retries=retries)

    def _init(self, retries=1):
        self.session = Session()
        if self.timeout:
            self.session.set_timeout(self.timeout)
        try:
            self.session.handshake(self.sock)
        except Exception as ex:
            while retries < self.num_retries:
                return self._connect_init_retry(retries)
            msg = "Error connecting to host %s:%s - %s"
            logger.error(msg, self.host, self.port, ex)
            if isinstance(ex, SSH2Timeout):
                raise Timeout(msg, self.host, self.port, ex)
            raise
        try:
            self.auth()
        except Exception as ex:
            while retries < self.num_retries:
                return self._connect_init_retry(retries)
            msg = "Authentication error while connecting to %s:%s - %s"
            raise AuthenticationException(msg, self.host, self.port, ex)
        self.session.set_blocking(0)

    def _connect(self, host, port, retries=1):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.settimeout(self.timeout)
        logger.debug("Connecting to %s:%s", host, port)
        try:
            self.sock.connect((host, port))
        except sock_gaierror as ex:
            logger.error("Could not resolve host '%s' - retry %s/%s",
                         host, retries, self.num_retries)
            while retries < self.num_retries:
                sleep(self.retry_delay)
                return self._connect(host, port, retries=retries+1)
            raise UnknownHostException("Unknown host %s - %s - retry %s/%s",
                                       host, str(ex.args[1]), retries,
                                       self.num_retries)
        except sock_error as ex:
            logger.error("Error connecting to host '%s:%s' - retry %s/%s",
                         host, port, retries, self.num_retries)
            while retries < self.num_retries:
                sleep(self.retry_delay)
                return self._connect(host, port, retries=retries+1)
            error_type = ex.args[1] if len(ex.args) > 1 else ex.args[0]
            raise ConnectionErrorException(
                "Error connecting to host '%s:%s' - %s - retry %s/%s",
                host, port, str(error_type), retries,
                self.num_retries,)

    def _pkey_auth(self):
        pub_file = "%s.pub" % self.pkey
        logger.debug("Attempting authentication with public key %s for user %s",
                     pub_file, self.user)
        self.session.userauth_publickey_fromfile(
            self.user,
            pub_file,
            self.pkey,
            self.password if self.password is not None else '')

    def _identity_auth(self):
        for identity_file in self.IDENTITIES:
            if not os.path.isfile(identity_file):
                continue
            pub_file = "%s.pub" % (identity_file)
            logger.debug(
                "Trying to authenticate with identity file %s",
                identity_file)
            try:
                self.session.userauth_publickey_fromfile(
                    self.user,
                    pub_file,
                    identity_file,
                    self.password if self.password is not None else '')
            except Exception:
                logger.debug("Authentication with identity file %s failed, "
                             "continuing with other identities",
                             identity_file)
                continue
            else:
                logger.debug("Authentication succeeded with identity file %s",
                             identity_file)
                return
        raise AuthenticationException("No authentication methods succeeded")

    def auth(self):
        if self.pkey is not None:
            logger.debug(
                "Proceeding with public key file authentication")
            return self._pkey_auth()
        if self.allow_agent:
            try:
                self.session.agent_auth(self.user)
            except Exception as ex:
                logger.debug("Agent auth failed with %s, "
                             "continuing with other authentication methods",
                             ex)
            else:
                logger.debug("Authentication with SSH Agent succeeded")
                return
        try:
            self._identity_auth()
        except AuthenticationException:
            if self.password is None:
                raise
            logger.debug("Public key auth failed, trying password")
            self._password_auth()

    def _password_auth(self):
        if self.session.userauth_password(self.user, self.password) != 0:
            raise AuthenticationException("Password authentication failed")

    def open_session(self):
        """Open new channel from session"""
        try:
            chan = self.session.open_session()
        except Exception as ex:
            raise SessionError(ex)
        while chan == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session)
            try:
                chan = self.session.open_session()
            except Exception as ex:
                raise SessionError(ex)
        # Multiple forward requests result in ChannelRequestDenied
        # errors, flag is used to avoid this.
        if self.forward_ssh_agent and not self._forward_requested:
            self._eagain(chan.request_auth_agent)
            self._forward_requested = True
        return chan

    def execute(self, cmd, use_pty=False, channel=None):
        """Execute command on remote server.

        :param cmd: Command to execute.
        :type cmd: str
        :param use_pty: Whether or not to obtain a PTY on the channel.
        :type use_pty: bool
        :param channel: Use provided channel for execute rather than creating
          a new one.
        :type channel: :py:class:`ssh2.channel.Channel`
        """
        channel = self.open_session() if channel is None else channel
        if use_pty:
            self._eagain(channel.pty)
        logger.debug("Executing command '%s'" % cmd)
        self._eagain(channel.execute, cmd)
        return channel

    def read_stderr(self, channel, timeout=None):
        """Read standard error buffer from channel.

        :param channel: Channel to read output from.
        :type channel: :py:class:`ssh2.channel.Channel`
        """
        return _read_output(self.session, channel.read_stderr, timeout=timeout)

    def read_output(self, channel, timeout=None):
        """Read standard output buffer from channel.

        :param channel: Channel to read output from.
        :type channel: :py:class:`ssh2.channel.Channel`
        """
        return _read_output(self.session, channel.read, timeout=timeout)

    def _select_timeout(self, func, timeout):
        ret = func()
        while ret == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session, timeout=timeout)
            ret = func()
            if ret == LIBSSH2_ERROR_EAGAIN and timeout is not None:
                raise Timeout

    def wait_finished(self, channel, timeout=None):
        """Wait for EOF from channel and close channel.

        Used to wait for remote command completion and be able to gather
        exit code.

        :param channel: The channel to use.
        :type channel: :py:class:`ssh2.channel.Channel`
        """
        if channel is None:
            return
        # If .eof() returns EAGAIN after a select with a timeout, it means
        # it reached timeout without EOF and _select_timeout will raise
        # timeout exception causing the channel to appropriately
        # not be closed as the command is still running.
        self._select_timeout(channel.wait_eof, timeout)
        # Close channel to indicate no more commands will be sent over it
        self.close_channel(channel)

    def close_channel(self, channel):
        logger.debug("Closing channel")
        self._eagain(channel.close)

    def _eagain(self, func, *args, **kwargs):
        ret = func(*args, **kwargs)
        while ret == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session)
            ret = func(*args, **kwargs)
        return ret

    def read_output_buffer(self, output_buffer, prefix=None,
                           callback=None,
                           callback_args=None,
                           encoding='utf-8'):
        """Read from output buffers and log to ``host_logger``.

        :param output_buffer: Iterator containing buffer
        :type output_buffer: iterator
        :param prefix: String to prefix log output to ``host_logger`` with
        :type prefix: str
        :param callback: Function to call back once buffer is depleted:
        :type callback: function
        :param callback_args: Arguments for call back function
        :type callback_args: tuple
        """
        prefix = '' if prefix is None else prefix
        for line in output_buffer:
            output = line.decode(encoding)
            host_logger.info("[%s]%s\t%s", self.host, prefix, output)
            yield output
        if callback:
            callback(*callback_args)

    def run_command(self, command, sudo=False, user=None,
                    use_pty=False, shell=None,
                    encoding='utf-8', timeout=None):
        """Run remote command.

        :param command: Command to run.
        :type command: str
        :param sudo: Run command via sudo as super-user.
        :type sudo: bool
        :param user: Run command as user via sudo
        :type user: str
        :param use_pty: Whether or not to obtain a PTY on the channel.
        :type use_pty: bool
        :param shell: (Optional) Override shell to use to run command with.
          Defaults to login user's defined shell. Use the shell's command
          syntax, eg `shell='bash -c'` or `shell='zsh -c'`.
        :type shell: str
        :param encoding: Encoding to use for output. Must be valid
          `Python codec <https://docs.python.org/2.7/library/codecs.html>`_
        :type encoding: str
        """
        # Fast path for no command substitution needed
        if not sudo and not user and not shell:
            _command = command
        else:
            _command = ''
            if sudo and not user:
                _command = 'sudo -S '
            elif user:
                _command = 'sudo -u %s -S ' % (user,)
            _shell = shell if shell else '$SHELL -c'
            _command += "%s '%s'" % (_shell, command,)
        channel = self.execute(_command, use_pty=use_pty)
        return channel, self.host, \
            self.read_output_buffer(
                self.read_output(channel, timeout=timeout),
                encoding=encoding), \
            self.read_output_buffer(
                self.read_stderr(channel, timeout=timeout), encoding=encoding,
                prefix='\t[err]'), channel

    def _make_sftp(self):
        """Make SFTP client from open transport"""
        try:
            sftp = self.session.sftp_init()
        except Exception as ex:
            raise SFTPError(ex)
        while sftp == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session)
            try:
                sftp = self.session.sftp_init()
            except Exception as ex:
                raise SFTPError(ex)
        return sftp

    def _mkdir(self, sftp, directory):
        """Make directory via SFTP channel

        :param sftp: SFTP client object
        :type sftp: :py:class:`ssh2.sftp.SFTP`
        :param directory: Remote directory to create
        :type directory: str

        :raises: :py:class:`pssh.exceptions.SFTPIOError` on SFTP IO errors
        """
        mode = LIBSSH2_SFTP_S_IRUSR | \
            LIBSSH2_SFTP_S_IWUSR | \
            LIBSSH2_SFTP_S_IXUSR | \
            LIBSSH2_SFTP_S_IRGRP | \
            LIBSSH2_SFTP_S_IROTH | \
            LIBSSH2_SFTP_S_IXGRP | \
            LIBSSH2_SFTP_S_IXOTH
        try:
            self._eagain(sftp.mkdir, directory, mode)
        except SFTPProtocolError as error:
            msg = "Error occured creating directory %s on host %s - %s"
            logger.error(msg, directory, self.host, error)
            raise SFTPIOError(msg, directory, self.host, error)
        logger.debug("Created remote directory %s", directory)

    def copy_file(self, local_file, remote_file, recurse=False,
                  sftp=None, _dir=None):
        """Copy local file to host via SFTP.

        :param local_file: Local filepath to copy to remote host
        :type local_file: str
        :param remote_file: Remote filepath on remote host to copy file to
        :type remote_file: str
        :param recurse: Whether or not to descend into directories recursively.
        :type recurse: bool

        :raises: :py:class:`ValueError` when a directory is supplied to
          ``local_file`` and ``recurse`` is not set
        :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation
          errors
        :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing
          via SFTP
        :raises: :py:class:`IOError` on local file IO errors
        :raises: :py:class:`OSError` on local OS errors like permission denied
        """
        sftp = self._make_sftp() if sftp is None else sftp
        if os.path.isdir(local_file) and recurse:
            return self._copy_dir(local_file, remote_file, sftp)
        elif os.path.isdir(local_file) and not recurse:
            raise ValueError("Recurse must be true if local_file is a "
                             "directory.")
        destination = self._remote_paths_split(remote_file)
        if destination is not None:
            try:
                self._eagain(sftp.stat, destination)
            except (SFTPHandleError, SFTPProtocolError):
                self.mkdir(sftp, destination)
        self.sftp_put(sftp, local_file, remote_file)
        logger.info("Copied local file %s to remote destination %s:%s",
                    local_file, self.host, remote_file)

    def _sftp_put(self, remote_fh, local_file):
        with open(local_file, 'rb') as local_fh:
            for data in local_fh:
                self._eagain(remote_fh.write, data)

    def sftp_put(self, sftp, local_file, remote_file):
        mode = LIBSSH2_SFTP_S_IRUSR | \
               LIBSSH2_SFTP_S_IWUSR | \
               LIBSSH2_SFTP_S_IRGRP | \
               LIBSSH2_SFTP_S_IROTH
        f_flags = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC
        with self._sftp_openfh(
                sftp.open, remote_file, f_flags, mode) as remote_fh:
            try:
                self._sftp_put(remote_fh, local_file)
                # THREAD_POOL.apply(
                #     sftp_put, args=(self.session, remote_fh, local_file))
            except SFTPProtocolError as ex:
                msg = "Error writing to remote file %s - %s"
                logger.error(msg, remote_file, ex)
                raise SFTPIOError(msg, remote_file, ex)

    def mkdir(self, sftp, directory, _parent_path=None):
        """Make directory via SFTP channel.

        Parent paths in the directory are created if they do not exist.

        :param sftp: SFTP client object
        :type sftp: :py:class:`paramiko.sftp_client.SFTPClient`
        :param directory: Remote directory to create
        :type directory: str

        Catches and logs at error level remote IOErrors on creating directory.
        """
        try:
            _dir, sub_dirs = directory.split('/', 1)
        except ValueError:
            _dir = directory.split('/', 1)[0]
            sub_dirs = None
        if not _dir and directory.startswith('/'):
            try:
                _dir, sub_dirs = sub_dirs.split(os.path.sep, 1)
            except ValueError:
                return True
        if _parent_path is not None:
            _dir = '/'.join((_parent_path, _dir))
        try:
            self._eagain(sftp.stat, _dir)
        except (SFTPHandleError, SFTPProtocolError) as ex:
            logger.debug("Stat for %s failed with %s", _dir, ex)
            self._mkdir(sftp, _dir)
        if sub_dirs is not None:
            if directory.startswith('/'):
                _dir = ''.join(('/', _dir))
            return self.mkdir(sftp, sub_dirs, _parent_path=_dir)

    def _copy_dir(self, local_dir, remote_dir, sftp):
        """Call copy_file on every file in the specified directory, copying
        them to the specified remote directory."""
        file_list = os.listdir(local_dir)
        for file_name in file_list:
            local_path = os.path.join(local_dir, file_name)
            remote_path = '/'.join([remote_dir, file_name])
            self.copy_file(local_path, remote_path, recurse=True,
                           sftp=sftp)

    def copy_remote_file(self, remote_file, local_file, recurse=False,
                         sftp=None, encoding='utf-8'):
        """Copy remote file to local host via SFTP.

        :param remote_file: Remote filepath to copy from
        :type remote_file: str
        :param local_file: Local filepath where file(s) will be copied to
        :type local_file: str
        :param recurse: Whether or not to recursively copy directories
        :type recurse: bool
        :param encoding: Encoding to use for file paths.
        :type encoding: str

        :raises: :py:class:`ValueError` when a directory is supplied to
          ``local_file`` and ``recurse`` is not set
        :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation
          errors
        :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors reading
          from SFTP
        :raises: :py:class:`IOError` on local file IO errors
        :raises: :py:class:`OSError` on local OS errors like permission denied
        """
        sftp = self._make_sftp() if sftp is None else sftp
        try:
            self._eagain(sftp.stat, remote_file)
        except (SFTPHandleError, SFTPProtocolError):
            msg = "Remote file or directory %s does not exist"
            logger.error(msg, remote_file)
            raise SFTPIOError(msg, remote_file)
        try:
            dir_h = self._sftp_openfh(sftp.opendir, remote_file)
        except SFTPError:
            pass
        else:
            if not recurse:
                raise ValueError("Recurse must be true if remote_file is a "
                                 "directory.")
            file_list = self._sftp_readdir(dir_h)
            return self._copy_remote_dir(file_list, remote_file,
                                         local_file, sftp,
                                         encoding=encoding)
        destination = os.path.join(os.path.sep, os.path.sep.join(
            [_dir for _dir in local_file.split('/')
             if _dir][:-1]))
        self._make_local_dir(destination)
        self.sftp_get(sftp, remote_file, local_file)
        logger.info("Copied local file %s from remote destination %s:%s",
                    local_file, self.host, remote_file)

    def scp_recv(self, remote_file, local_file, recurse=False, sftp=None,
                 encoding='utf-8'):
        """Copy remote file to local host via SCP.

        Note - Remote directory listings are gather via SFTP when
        ``recurse`` is enabled - SCP lacks directory list support.
        Enabling recursion therefore involves creating an extra SFTP channel
        and requires SFTP support on the server.

        :param remote_file: Remote filepath to copy from
        :type remote_file: str
        :param local_file: Local filepath where file(s) will be copied to
        :type local_file: str
        :param recurse: Whether or not to recursively copy directories
        :type recurse: bool
        :param encoding: Encoding to use for file paths when recursion is
          enabled.
        :type encoding: str

        :raises: :py:class:`pssh.exceptions.SCPError` when a directory is
          supplied to ``local_file`` and ``recurse`` is not set.
        :raises: :py:class:`pssh.exceptions.SCPError` on errors copying file.
        :raises: :py:class:`IOError` on local file IO errors.
        :raises: :py:class:`OSError` on local OS errors like permission denied.
        """
        sftp = self._make_sftp() if (sftp is None and recurse) else sftp
        if recurse:
            try:
                self._eagain(sftp.stat, remote_file)
            except (SFTPHandleError, SFTPProtocolError):
                msg = "Remote file or directory %s does not exist"
                logger.error(msg, remote_file)
                raise SCPError(msg, remote_file)
            try:
                dir_h = self._sftp_openfh(sftp.opendir, remote_file)
            except SFTPError:
                pass
            else:
                file_list = self._sftp_readdir(dir_h)
                return self._scp_recv_dir(file_list, remote_file,
                                          local_file, sftp,
                                          encoding=encoding)
        destination = os.path.join(os.path.sep, os.path.sep.join(
            [_dir for _dir in local_file.split('/')
             if _dir][:-1]))
        self._make_local_dir(destination)
        self._scp_recv(remote_file, local_file)
        logger.info("SCP local file %s from remote destination %s:%s",
                    local_file, self.host, remote_file)

    def _scp_recv(self, remote_file, local_file):
        try:
            (file_chan, fileinfo) = self._eagain(
                self.session.scp_recv2, remote_file)
        except Exception as ex:
            msg = "Error copying file %s from host %s - %s"
            logger.error(msg, remote_file, self.host, ex)
            raise SCPError(msg, remote_file, self.host, ex)
        local_fh = open(local_file, 'wb')
        try:
            total = 0
            size, data = file_chan.read(size=fileinfo.st_size)
            while size == LIBSSH2_ERROR_EAGAIN:
                wait_select(self.session)
                size, data = file_chan.read(size=fileinfo.st_size)
            total += size
            local_fh.write(data)
            while total < fileinfo.st_size:
                size, data = file_chan.read(size=fileinfo.st_size - total)
                while size == LIBSSH2_ERROR_EAGAIN:
                    wait_select(self.session)
                    continue
                total += size
                local_fh.write(data)
            if total != fileinfo.st_size:
                msg = "Error copying data from remote file %s on host %s. " \
                      "Copied %s out of %s total bytes"
                raise SCPError(msg, remote_file, self.host, total,
                               fileinfo.st_size)
        finally:
            local_fh.close()

    def _scp_send_dir(self, local_dir, remote_dir, sftp):
        file_list = os.listdir(local_dir)
        for file_name in file_list:
            local_path = os.path.join(local_dir, file_name)
            remote_path = '/'.join([remote_dir, file_name])
            self.scp_send(local_path, remote_path, recurse=True,
                          sftp=sftp)

    def _scp_recv_dir(self, file_list, remote_dir, local_dir, sftp,
                      encoding='utf-8'):
        for file_name in file_list:
            file_name = file_name.decode(encoding)
            if file_name in ('.', '..'):
                continue
            remote_path = os.path.join(remote_dir, file_name)
            local_path = os.path.join(local_dir, file_name)
            logger.debug("Attempting recursive copy from %s:%s to %s",
                         self.host, remote_path, local_path)
            self.scp_recv(remote_path, local_path, sftp=sftp,
                          recurse=True)

    def scp_send(self, local_file, remote_file, recurse=False, sftp=None):
        """Copy local file to host via SCP.

        Note - Directories are created via SFTP when ``recurse`` is enabled -
        SCP lacks directory create support. Enabling recursion therefore
        involves creating an extra SFTP channel and requires SFTP support on the
        server.

        :param local_file: Local filepath to copy to remote host
        :type local_file: str
        :param remote_file: Remote filepath on remote host to copy file to
        :type remote_file: str
        :param recurse: Whether or not to descend into directories recursively.
        :type recurse: bool

        :raises: :py:class:`ValueError` when a directory is supplied to
          ``local_file`` and ``recurse`` is not set
        :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation
          errors
        :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing
          via SFTP
        :raises: :py:class:`IOError` on local file IO errors
        :raises: :py:class:`OSError` on local OS errors like permission denied
        """
        if os.path.isdir(local_file) and recurse:
            sftp = self._make_sftp() if sftp is None else sftp
            return self._scp_send_dir(local_file, remote_file, sftp)
        elif os.path.isdir(local_file) and not recurse:
            raise ValueError("Recurse must be True if local_file is a "
                             "directory.")
        destination = self._remote_paths_split(remote_file)
        if destination is not None:
            sftp = self._make_sftp() if sftp is None else sftp
            try:
                self._eagain(sftp.stat, destination)
            except (SFTPHandleError, SFTPProtocolError):
                self.mkdir(sftp, destination)
        self._scp_send(local_file, remote_file)
        logger.info("SCP local file %s to remote destination %s:%s",
                    local_file, self.host, remote_file)

    def _scp_send(self, local_file, remote_file):
        fileinfo = os.stat(local_file)
        try:
            chan = self._eagain(
                self.session.scp_send64,
                remote_file, fileinfo.st_mode & 777, fileinfo.st_size,
                fileinfo.st_mtime, fileinfo.st_atime)
        except Exception as ex:
            msg = "Error opening remote file %s for writing on host %s - %s"
            logger.error(msg, remote_file, self.host, ex)
            raise SCPError(msg, remote_file, self.host, ex)
        try:
            with open(local_file, 'rb') as local_fh:
                for data in local_fh:
                    chan.write(data)
        except Exception as ex:
            msg = "Error writing to remote file %s on host %s - %s"
            logger.error(msg, remote_file, self.host, ex)
            raise SCPError(msg, remote_file, self.host, ex)

    def _sftp_readdir(self, dir_h):
        for size, buf, attrs in dir_h.readdir():
            for line in buf.splitlines():
                yield line

    def _sftp_openfh(self, open_func, remote_file, *args):
        try:
            fh = open_func(remote_file, *args)
        except Exception as ex:
            raise SFTPError(ex)
        while fh == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session, timeout=0.1)
            try:
                fh = open_func(remote_file, *args)
            except Exception as ex:
                raise SFTPError(ex)
        return fh

    def _sftp_get(self, remote_fh, local_file):
        with open(local_file, 'wb') as local_fh:
            for size, data in remote_fh:
                if size == LIBSSH2_ERROR_EAGAIN:
                    wait_select(self.session)
                    continue
                local_fh.write(data)

    def sftp_get(self, sftp, remote_file, local_file):
        with self._sftp_openfh(
                sftp.open, remote_file,
                LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR) as remote_fh:
            try:
                self._sftp_get(remote_fh, local_file)
                # Running SFTP in a thread requires a new session
                # as session handles or any handles created by a session
                # cannot be used simultaneously in multiple threads.
                # THREAD_POOL.apply(
                #     sftp_get, args=(self.session, remote_fh, local_file))
            except SFTPProtocolError as ex:
                msg = "Error reading from remote file %s - %s"
                logger.error(msg, remote_file, ex)
                raise SFTPIOError(msg, remote_file, ex)

    def _copy_remote_dir(self, file_list, remote_dir, local_dir, sftp,
                         encoding='utf-8'):
        for file_name in file_list:
            file_name = file_name.decode(encoding)
            if file_name in ('.', '..'):
                continue
            remote_path = os.path.join(remote_dir, file_name)
            local_path = os.path.join(local_dir, file_name)
            self.copy_remote_file(remote_path, local_path, sftp=sftp,
                                  recurse=True)

    def _make_local_dir(self, dirpath):
        if os.path.exists(dirpath):
            return
        try:
            os.makedirs(dirpath)
        except OSError:
            logger.error("Unable to create local directory structure for "
                         "directory %s", dirpath)
            raise

    def _remote_paths_split(self, file_path):
        _sep = file_path.rfind('/')
        if _sep > 0:
            return file_path[:_sep]
        return
Пример #10
0
    def connect(self, host, port, username, password):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((host, port))
        try:
            ssh_session = Session()
            try:
                ssh_session.set_timeout(self.timeoutMs)
                ssh_session.handshake(sock)
                ssh_session.userauth_password(username, password)
                self._sftp_session = ssh_session.sftp_init()
                # set non_blocking_mode
                ssh_session.set_blocking(False)
                self._socket = sock
                self._ssh_session = ssh_session
            except:
                self._close_session(ssh_session)
                raise
        except:
            self._close_socket(sock)
            raise

        return self
Пример #11
0
class SSHClient(BaseSSHClient):
    """ssh2-python (libssh2) based non-blocking SSH client."""

    def __init__(self, host,
                 user=None, password=None, port=None,
                 pkey=None,
                 num_retries=DEFAULT_RETRIES,
                 retry_delay=RETRY_DELAY,
                 allow_agent=True, timeout=None,
                 forward_ssh_agent=False,
                 proxy_host=None,
                 _auth_thread_pool=True, keepalive_seconds=60,
                 identity_auth=True,):
        """:param host: Host name or IP to connect to.
        :type host: str
        :param user: User to connect as. Defaults to logged in user.
        :type user: str
        :param password: Password to use for password authentication.
        :type password: str
        :param port: SSH port to connect to. Defaults to SSH default (22)
        :type port: int
        :param pkey: Private key file path to use for authentication. Path must
          be either absolute path or relative to user home directory
          like ``~/<path>``.
        :type pkey: str
        :param num_retries: (Optional) Number of connection and authentication
          attempts before the client gives up. Defaults to 3.
        :type num_retries: int
        :param retry_delay: Number of seconds to wait between retries. Defaults
          to :py:class:`pssh.constants.RETRY_DELAY`
        :type retry_delay: int
        :param timeout: SSH session timeout setting in seconds. This controls
          timeout setting of authenticated SSH sessions.
        :type timeout: int
        :param allow_agent: (Optional) set to False to disable connecting to
          the system's SSH agent
        :type allow_agent: bool
        :param identity_auth: (Optional) set to False to disable attempting to
          authenticate with default identity files from
          `pssh.clients.base_ssh_client.BaseSSHClient.IDENTITIES`
        :type identity_auth: bool
        :param forward_ssh_agent: (Optional) Turn on SSH agent forwarding -
          equivalent to `ssh -A` from the `ssh` command line utility.
          Defaults to True if not set.
        :type forward_ssh_agent: bool
        :param proxy_host: Connection to host is via provided proxy host
          and client should use self.proxy_host for connection attempts.
        :type proxy_host: str
        :param keepalive_seconds: Interval of keep alive messages being sent to
          server. Set to ``0`` or ``False`` to disable.

        :raises: :py:class:`pssh.exceptions.PKeyFileError` on errors finding
          provided private key.
        """
        self.forward_ssh_agent = forward_ssh_agent
        self._forward_requested = False
        self.keepalive_seconds = keepalive_seconds
        self._keepalive_greenlet = None
        super(SSHClient, self).__init__(
            host, user=user, password=password, port=port, pkey=pkey,
            num_retries=num_retries, retry_delay=retry_delay,
            allow_agent=allow_agent, _auth_thread_pool=_auth_thread_pool,
            timeout=timeout,
            proxy_host=proxy_host, identity_auth=identity_auth)

    def disconnect(self):
        """Disconnect session, close socket if needed."""
        logger.debug("Disconnecting client for host %s", self.host)
        self._keepalive_greenlet = None
        if self.session is not None:
            try:
                self._eagain(self.session.disconnect)
            except Exception:
                pass
            self.session = None
        self.sock = None

    def spawn_send_keepalive(self):
        """Spawns a new greenlet that sends keep alive messages every
        self.keepalive_seconds"""
        return spawn(self._send_keepalive)

    def _send_keepalive(self):
        while True:
            sleep(self._eagain(self.session.keepalive_send))

    def configure_keepalive(self):
        self.session.keepalive_config(False, self.keepalive_seconds)

    def _init(self, retries=1):
        self.session = Session()
        if self.timeout:
            # libssh2 timeout is in ms
            self.session.set_timeout(self.timeout * 1000)
        try:
            self.session.handshake(self.sock)
        except Exception as ex:
            while retries < self.num_retries:
                return self._connect_init_retry(retries)
            msg = "Error connecting to host %s:%s - %s"
            logger.error(msg, self.host, self.port, ex)
            if isinstance(ex, SSH2Timeout):
                raise Timeout(msg, self.host, self.port, ex)
            ex.host = self.host
            ex.port = self.port
            raise
        try:
            self.auth()
        except Exception as ex:
            while retries < self.num_retries:
                return self._connect_init_retry(retries)
            msg = "Authentication error while connecting to %s:%s - %s"
            raise AuthenticationException(msg, self.host, self.port, ex)
        self.session.set_blocking(0)
        if self.keepalive_seconds:
            self.configure_keepalive()
            self._keepalive_greenlet = self.spawn_send_keepalive()

    def auth(self):
        if self.pkey is not None:
            logger.debug(
                "Proceeding with private key file authentication")
            return self._pkey_auth(password=self.password)
        if self.allow_agent:
            try:
                self.session.agent_auth(self.user)
            except (AgentAuthenticationError, AgentConnectionError, AgentGetIdentityError,
                    AgentListIdentitiesError) as ex:
                logger.debug("Agent auth failed with %s"
                             "continuing with other authentication methods", ex)
            except Exception as ex:
                logger.error("Unknown error during agent authentication - %s", ex)
            else:
                logger.debug("Authentication with SSH Agent succeeded")
                return
        try:
            self._identity_auth()
        except AuthenticationException:
            if self.password is None:
                raise
            logger.debug("Private key auth failed, trying password")
            self._password_auth()

    def _pkey_auth(self, password=None):
        self.session.userauth_publickey_fromfile(
            self.user,
            self.pkey,
            passphrase=password if password is not None else '')

    def _password_auth(self):
        try:
            self.session.userauth_password(self.user, self.password)
        except Exception:
            raise AuthenticationException("Password authentication failed")

    def open_session(self):
        """Open new channel from session"""
        try:
            chan = self.session.open_session()
        except Exception as ex:
            raise SessionError(ex)
        while chan == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session)
            try:
                chan = self.session.open_session()
            except Exception as ex:
                raise SessionError(ex)
        # Multiple forward requests result in ChannelRequestDenied
        # errors, flag is used to avoid this.
        if self.forward_ssh_agent and not self._forward_requested:
            if not hasattr(chan, 'request_auth_agent'):
                warn("Requested SSH Agent forwarding but libssh2 version used "
                     "does not support it - ignoring")
                return chan
            self._eagain(chan.request_auth_agent)
            self._forward_requested = True
        return chan

    def execute(self, cmd, use_pty=False, channel=None):
        """Execute command on remote server.

        :param cmd: Command to execute.
        :type cmd: str
        :param use_pty: Whether or not to obtain a PTY on the channel.
        :type use_pty: bool
        :param channel: Use provided channel for execute rather than creating
          a new one.
        :type channel: :py:class:`ssh2.channel.Channel`
        """
        channel = self.open_session() if channel is None else channel
        if use_pty:
            self._eagain(channel.pty)
        logger.debug("Executing command '%s'", cmd)
        self._eagain(channel.execute, cmd)
        return channel

    def read_stderr(self, channel, timeout=None):
        """Read standard error buffer from channel.
        Returns a generator of line by line output.

        :param channel: Channel to read output from.
        :type channel: :py:class:`ssh2.channel.Channel`
        :rtype: generator
        """
        return _read_output(self.session, channel.read_stderr, timeout=timeout)

    def read_output(self, channel, timeout=None):
        """Read standard output buffer from channel.
        Returns a generator of line by line output.

        :param channel: Channel to read output from.
        :type channel: :py:class:`ssh2.channel.Channel`
        :rtype: generator
        """
        return _read_output(self.session, channel.read, timeout=timeout)

    def wait_finished(self, channel, timeout=None):
        """Wait for EOF from channel and close channel.

        Used to wait for remote command completion and be able to gather
        exit code.

        :param channel: The channel to use.
        :type channel: :py:class:`ssh2.channel.Channel`
        """
        if channel is None:
            return
        # If wait_eof() returns EAGAIN after a select with a timeout, it means
        # it reached timeout without EOF and _select_timeout will raise
        # timeout exception causing the channel to appropriately
        # not be closed as the command is still running.
        self._eagain(channel.wait_eof)
        # Close channel to indicate no more commands will be sent over it
        self.close_channel(channel)

    def close_channel(self, channel):
        logger.debug("Closing channel")
        self._eagain(channel.close)

    def _eagain(self, func, *args, **kwargs):
        ret = func(*args, **kwargs)
        while ret == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session)
            ret = func(*args, **kwargs)
        return ret

    def _make_sftp(self):
        """Make SFTP client from open transport"""
        try:
            sftp = self.session.sftp_init()
        except Exception as ex:
            raise SFTPError(ex)
        while sftp == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session)
            try:
                sftp = self.session.sftp_init()
            except Exception as ex:
                raise SFTPError(ex)
        return sftp

    def _mkdir(self, sftp, directory):
        """Make directory via SFTP channel

        :param sftp: SFTP client object
        :type sftp: :py:class:`ssh2.sftp.SFTP`
        :param directory: Remote directory to create
        :type directory: str

        :raises: :py:class:`pssh.exceptions.SFTPIOError` on SFTP IO errors
        """
        mode = LIBSSH2_SFTP_S_IRUSR | \
            LIBSSH2_SFTP_S_IWUSR | \
            LIBSSH2_SFTP_S_IXUSR | \
            LIBSSH2_SFTP_S_IRGRP | \
            LIBSSH2_SFTP_S_IROTH | \
            LIBSSH2_SFTP_S_IXGRP | \
            LIBSSH2_SFTP_S_IXOTH
        try:
            self._eagain(sftp.mkdir, directory, mode)
        except SFTPProtocolError as error:
            msg = "Error occured creating directory %s on host %s - %s"
            logger.error(msg, directory, self.host, error)
            raise SFTPIOError(msg, directory, self.host, error)
        logger.debug("Created remote directory %s", directory)

    def copy_file(self, local_file, remote_file, recurse=False, sftp=None):
        """Copy local file to host via SFTP.

        :param local_file: Local filepath to copy to remote host
        :type local_file: str
        :param remote_file: Remote filepath on remote host to copy file to
        :type remote_file: str
        :param recurse: Whether or not to descend into directories recursively.
        :type recurse: bool

        :raises: :py:class:`ValueError` when a directory is supplied to
          ``local_file`` and ``recurse`` is not set
        :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation
          errors
        :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing
          via SFTP
        :raises: :py:class:`IOError` on local file IO errors
        :raises: :py:class:`OSError` on local OS errors like permission denied
        """
        sftp = self._make_sftp() if sftp is None else sftp
        if os.path.isdir(local_file) and recurse:
            return self._copy_dir(local_file, remote_file, sftp)
        elif os.path.isdir(local_file) and not recurse:
            raise ValueError("Recurse must be true if local_file is a "
                             "directory.")
        destination = self._remote_paths_split(remote_file)
        if destination is not None:
            try:
                self._eagain(sftp.stat, destination)
            except (SFTPHandleError, SFTPProtocolError):
                self.mkdir(sftp, destination)
        self.sftp_put(sftp, local_file, remote_file)
        logger.info("Copied local file %s to remote destination %s:%s",
                    local_file, self.host, remote_file)

    def _sftp_put(self, remote_fh, local_file):
        with open(local_file, 'rb', 2097152) as local_fh:
            for data in local_fh:
                eagain_write(remote_fh.write, data, self.session)

    def sftp_put(self, sftp, local_file, remote_file):
        mode = LIBSSH2_SFTP_S_IRUSR | \
               LIBSSH2_SFTP_S_IWUSR | \
               LIBSSH2_SFTP_S_IRGRP | \
               LIBSSH2_SFTP_S_IROTH
        f_flags = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC
        with self._sftp_openfh(
                sftp.open, remote_file, f_flags, mode) as remote_fh:
            try:
                self._sftp_put(remote_fh, local_file)
                # THREAD_POOL.apply(
                #     sftp_put, args=(self.session, remote_fh, local_file))
            except SFTPProtocolError as ex:
                msg = "Error writing to remote file %s - %s"
                logger.error(msg, remote_file, ex)
                raise SFTPIOError(msg, remote_file, ex)

    def mkdir(self, sftp, directory):
        """Make directory via SFTP channel.

        Parent paths in the directory are created if they do not exist.

        :param sftp: SFTP client object
        :type sftp: :py:class:`paramiko.sftp_client.SFTPClient`
        :param directory: Remote directory to create
        :type directory: str

        Catches and logs at error level remote IOErrors on creating directory.
        """
        _paths_to_create = deque()
        for d in directory.split('/'):
            if not d:
                continue
            _paths_to_create.append(d)
        cwd = '' if directory.startswith('/') else '.'
        while _paths_to_create:
            cur_dir = _paths_to_create.popleft()
            cwd = '/'.join([cwd, cur_dir])
            try:
                self._eagain(sftp.stat, cwd)
            except (SFTPHandleError, SFTPProtocolError) as ex:
                logger.debug("Stat for %s failed with %s", cwd, ex)
                self._mkdir(sftp, cwd)

    def copy_remote_file(self, remote_file, local_file, recurse=False,
                         sftp=None, encoding='utf-8'):
        """Copy remote file to local host via SFTP.

        :param remote_file: Remote filepath to copy from
        :type remote_file: str
        :param local_file: Local filepath where file(s) will be copied to
        :type local_file: str
        :param recurse: Whether or not to recursively copy directories
        :type recurse: bool
        :param encoding: Encoding to use for file paths.
        :type encoding: str

        :raises: :py:class:`ValueError` when a directory is supplied to
          ``local_file`` and ``recurse`` is not set
        :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation
          errors
        :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors reading
          from SFTP
        :raises: :py:class:`IOError` on local file IO errors
        :raises: :py:class:`OSError` on local OS errors like permission denied
        """
        sftp = self._make_sftp() if sftp is None else sftp
        try:
            self._eagain(sftp.stat, remote_file)
        except (SFTPHandleError, SFTPProtocolError):
            msg = "Remote file or directory %s does not exist"
            logger.error(msg, remote_file)
            raise SFTPIOError(msg, remote_file)
        try:
            dir_h = self._sftp_openfh(sftp.opendir, remote_file)
        except SFTPError:
            pass
        else:
            if not recurse:
                raise ValueError("Recurse must be true if remote_file is a "
                                 "directory.")
            file_list = self._sftp_readdir(dir_h)
            return self._copy_remote_dir(file_list, remote_file,
                                         local_file, sftp,
                                         encoding=encoding)
        destination = os.path.join(os.path.sep, os.path.sep.join(
            [_dir for _dir in local_file.split('/')
             if _dir][:-1]))
        self._make_local_dir(destination)
        self.sftp_get(sftp, remote_file, local_file)
        logger.info("Copied local file %s from remote destination %s:%s",
                    local_file, self.host, remote_file)

    def scp_recv(self, remote_file, local_file, recurse=False, sftp=None,
                 encoding='utf-8'):
        """Copy remote file to local host via SCP.

        Note - Remote directory listings are gathered via SFTP when
        ``recurse`` is enabled - SCP lacks directory list support.
        Enabling recursion therefore involves creating an extra SFTP channel
        and requires SFTP support on the server.

        :param remote_file: Remote filepath to copy from
        :type remote_file: str
        :param local_file: Local filepath where file(s) will be copied to
        :type local_file: str
        :param recurse: Whether or not to recursively copy directories
        :type recurse: bool
        :param encoding: Encoding to use for file paths when recursion is
          enabled.
        :type encoding: str

        :raises: :py:class:`pssh.exceptions.SCPError` when a directory is
          supplied to ``local_file`` and ``recurse`` is not set.
        :raises: :py:class:`pssh.exceptions.SCPError` on errors copying file.
        :raises: :py:class:`IOError` on local file IO errors.
        :raises: :py:class:`OSError` on local OS errors like permission denied.
        """
        if recurse:
            sftp = self._make_sftp() if sftp is None else sftp
            try:
                self._eagain(sftp.stat, remote_file)
            except (SFTPHandleError, SFTPProtocolError):
                msg = "Remote file or directory %s does not exist"
                logger.error(msg, remote_file)
                raise SCPError(msg, remote_file)
            try:
                dir_h = self._sftp_openfh(sftp.opendir, remote_file)
            except SFTPError:
                pass
            else:
                try:
                    os.makedirs(local_file)
                except OSError:
                    pass
                file_list = self._sftp_readdir(dir_h)
                return self._scp_recv_dir(file_list, remote_file,
                                          local_file, sftp,
                                          encoding=encoding)
        elif local_file.endswith('/'):
            remote_filename = remote_file.rsplit('/')[-1]
            local_file += remote_filename
        destination = os.path.join(os.path.sep, os.path.sep.join(
            [_dir for _dir in local_file.split('/')
             if _dir][:-1]))
        self._make_local_dir(destination)
        self._scp_recv(remote_file, local_file)
        logger.info("SCP local file %s from remote destination %s:%s",
                    local_file, self.host, remote_file)

    def _scp_recv(self, remote_file, local_file):
        try:
            (file_chan, fileinfo) = self._eagain(
                self.session.scp_recv2, remote_file)
        except Exception as ex:
            msg = "Error copying file %s from host %s - %s"
            logger.error(msg, remote_file, self.host, ex)
            raise SCPError(msg, remote_file, self.host, ex)
        local_fh = open(local_file, 'wb')
        try:
            total = 0
            while total < fileinfo.st_size:
                size, data = file_chan.read(size=fileinfo.st_size - total)
                if size == LIBSSH2_ERROR_EAGAIN:
                    wait_select(self.session)
                    continue
                total += size
                local_fh.write(data)
            if total != fileinfo.st_size:
                msg = "Error copying data from remote file %s on host %s. " \
                      "Copied %s out of %s total bytes"
                raise SCPError(msg, remote_file, self.host, total,
                               fileinfo.st_size)
        finally:
            local_fh.close()

    def scp_send(self, local_file, remote_file, recurse=False, sftp=None):
        """Copy local file to host via SCP.

        Note - Directories are created via SFTP when ``recurse`` is enabled -
        SCP lacks directory create support. Enabling recursion therefore
        involves creating an extra SFTP channel and requires SFTP support on the
        server.

        :param local_file: Local filepath to copy to remote host
        :type local_file: str
        :param remote_file: Remote filepath on remote host to copy file to
        :type remote_file: str
        :param recurse: Whether or not to descend into directories recursively.
        :type recurse: bool

        :raises: :py:class:`ValueError` when a directory is supplied to
          ``local_file`` and ``recurse`` is not set
        :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation
          errors
        :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing
          via SFTP
        :raises: :py:class:`IOError` on local file IO errors
        :raises: :py:class:`OSError` on local OS errors like permission denied
        """
        if os.path.isdir(local_file) and recurse:
            sftp = self._make_sftp() if sftp is None else sftp
            return self._scp_send_dir(local_file, remote_file, sftp)
        elif os.path.isdir(local_file) and not recurse:
            raise ValueError("Recurse must be True if local_file is a "
                             "directory.")
        if recurse:
            destination = self._remote_paths_split(remote_file)
            if destination is not None:
                sftp = self._make_sftp() if sftp is None else sftp
                try:
                    self._eagain(sftp.stat, destination)
                except (SFTPHandleError, SFTPProtocolError):
                    self.mkdir(sftp, destination)
        elif remote_file.endswith('/'):
            local_filename = local_file.rsplit('/')[-1]
            remote_file += local_filename
        self._scp_send(local_file, remote_file)
        logger.info("SCP local file %s to remote destination %s:%s",
                    local_file, self.host, remote_file)

    def _scp_send(self, local_file, remote_file):
        fileinfo = os.stat(local_file)
        try:
            chan = self._eagain(
                self.session.scp_send64,
                remote_file, fileinfo.st_mode & 0o777, fileinfo.st_size,
                fileinfo.st_mtime, fileinfo.st_atime)
        except Exception as ex:
            msg = "Error opening remote file %s for writing on host %s - %s"
            logger.error(msg, remote_file, self.host, ex)
            raise SCPError(msg, remote_file, self.host, ex)
        try:
            with open(local_file, 'rb', 2097152) as local_fh:
                for data in local_fh:
                    eagain_write(chan.write, data, self.session)
        except Exception as ex:
            msg = "Error writing to remote file %s on host %s - %s"
            logger.error(msg, remote_file, self.host, ex)
            raise SCPError(msg, remote_file, self.host, ex)

    def _sftp_openfh(self, open_func, remote_file, *args):
        try:
            fh = open_func(remote_file, *args)
        except Exception as ex:
            raise SFTPError(ex)
        while fh == LIBSSH2_ERROR_EAGAIN:
            wait_select(self.session, timeout=0.1)
            try:
                fh = open_func(remote_file, *args)
            except Exception as ex:
                raise SFTPError(ex)
        return fh

    def _sftp_get(self, remote_fh, local_file):
        with open(local_file, 'wb') as local_fh:
            for size, data in remote_fh:
                if size == LIBSSH2_ERROR_EAGAIN:
                    wait_select(self.session)
                    continue
                local_fh.write(data)

    def sftp_get(self, sftp, remote_file, local_file):
        with self._sftp_openfh(
                sftp.open, remote_file,
                LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR) as remote_fh:
            try:
                self._sftp_get(remote_fh, local_file)
                # Running SFTP in a thread requires a new session
                # as session handles or any handles created by a session
                # cannot be used simultaneously in multiple threads.
                # THREAD_POOL.apply(
                #     sftp_get, args=(self.session, remote_fh, local_file))
            except SFTPProtocolError as ex:
                msg = "Error reading from remote file %s - %s"
                logger.error(msg, remote_file, ex)
                raise SFTPIOError(msg, remote_file, ex)

    def get_exit_status(self, channel):
        if not channel.eof():
            return
        return channel.get_exit_status()

    def finished(self, channel):
        """Checks if remote command has finished - has server sent client
        EOF.

        :rtype: bool
        """
        if channel is None:
            return
        return channel.eof()
Пример #12
0
class SSHStream(BaseStream):
    default_port = 22
    SSH_KEY_PREFIX = config.path.ssh_key_prefix

    _key_cache = cachetools.TTLCache(100, ttl=60)

    def __init__(self, cli: CLI):
        super().__init__(cli)
        self.script = cli.script  # @todo: Remove
        self.session = None
        self.channel = None
        self.credentials = cli.script.credentials

    def __del__(self):
        self.channel = None
        self.session = None

    @classmethod
    @cachetools.cachedmethod(operator.attrgetter("_key_cache"),
                             lock=lambda _: key_lock)
    def get_publickey(cls,
                      pool: str) -> Tuple[Optional[bytes], Optional[bytes]]:
        """
        Return public, private key pair
        :return: bytes, bytes or None, None
        """
        logger.debug("Getting keys for pool %s", pool)
        pub_path = os.path.join(config.path.ssh_key_prefix, pool, "id_rsa.pub")
        priv_path = os.path.join(config.path.ssh_key_prefix, pool, "id_rsa")
        if not os.path.exists(pub_path):
            logger.debug("No public key for pool %s (%s)", pool, pub_path)
            return None, None
        if not os.path.exists(priv_path):
            logger.debug("No private key for pool %s (%s)", pool, priv_path)
            return None, None
        with open(pub_path, "rb") as fpub, open(priv_path, "rb") as fpriv:
            return fpub.read(), fpriv.read()

    async def startup(self):
        """
        SSH session startup
        """
        user = self.credentials["user"]
        if user is None:
            user = ""
        self.logger.debug("Startup ssh session for user '%s'", user)
        self.session = Session()
        try:
            self.session.handshake(self.socket)
            host_hash = smart_bytes(
                self.session.hostkey_hash(LIBSSH2_HOSTKEY_HASH_SHA1))
            hex_hash = smart_text(codecs.encode(host_hash, "hex"))
            self.logger.debug("Connected. Host fingerprint is %s", hex_hash)
            auth_methods = self.session.userauth_list(user)
            if not auth_methods:
                self.logger.info(
                    "No supported authentication methods. Failed to log in")
                raise CLIAuthFailed("Failed to log in")
            self.logger.debug("Supported authentication methods: %s",
                              ", ".join(auth_methods))
            # Try to authenticate
            authenticated = False
            for method in auth_methods:
                ah = getattr(self, "auth_%s" % method.replace("-", ""), None)
                if ah:
                    metrics["ssh_auth", ("method", method)] += 1
                    authenticated |= ah()
                    if authenticated:
                        metrics["ssh_auth_success", ("method", method)] += 1
                        break
                    metrics["ssh_auth_fail", ("method", method)] += 1
            if not authenticated:
                self.logger.error("Failed to authenticate user '%s'", user)
                raise CLIAuthFailed("Failed to log in")
            self.logger.debug("User is authenticated")
            self.logger.debug("Open channel")
            self.channel = self.session.open_session()
            self.channel.pty("xterm")
            self.channel.shell()
            self.session.keepalive_config(False, 60)
            self.session.set_blocking(False)
        except SSH2Error as e:
            self.logger.info("SSH Error: %s", e)
            raise CLISSHProtocolError("SSH Error: %s" % e)

    async def read(self, n: int) -> bytes:
        while True:
            try:
                await self.wait_for_read()
                metrics["ssh_reads"] += 1
                code, data = self.channel.read(n)
                if code == 0:
                    if self.channel.eof():
                        self.logger.info("SSH session reset")
                        self.close()
                        return b""
                    metrics["ssh_reads_blocked"] += 1
                    continue
                elif code > 0:
                    n = len(data)
                    metrics["ssh_read_bytes"] += n
                    return data
                elif code == LIBSSH2_ERROR_EAGAIN:
                    metrics["ssh_reads_blocked"] += 1
                    continue
                metrics["ssh_errors", ("code", code)] += 1
                raise CLISSHProtocolError("SSH Error code %s" % code)
            except SSH2Error as e:
                raise CLISSHProtocolError("SSH Error: %s" % e)

    async def write(self, data: bytes):
        metrics["ssh_writes"] += 1
        while data:
            await self.wait_for_write()
            try:
                _, sent = self.channel.write(data)
                metrics["ssh_write_bytes"] += sent
                data = data[sent:]
            except SSH2Error as e:
                raise CLISSHProtocolError("SSH Error: %s" % e)

    def close(self, exc_info=False):
        if self.channel:
            self.logger.debug("Closing channel")
            try:
                self.channel.close()
            except SSH2Error as e:
                self.logger.debug("Cannot close channel clearly: %s", e)
            # The causes of memory leak
            # self.channel = None
        if self.session:
            self.logger.debug("Closing ssh session")
            self.session = None
        super().close()

    def get_user(self) -> str:
        """
        Get current user
        """
        return self.script.credentials["user"] or ""

    def get_password(self) -> str:
        """
        Get current user's password
        """
        return self.script.credentials["password"] or ""

    def auth_publickey(self) -> bool:
        """
        Public key authentication
        """
        self.logger.debug("Trying publickey authentication")
        pub_key, priv_key = self.get_publickey(self.script.pool)
        if not pub_key or not priv_key:
            self.logger.debug("No keys for pool. Skipping")
            return False
        user = self.get_user()
        try:
            self.session.userauth_publickey_frommemory(user, priv_key, "",
                                                       pub_key)
            return True
        except SSH2Error:
            msg = self.session.last_error()
            self.logger.debug("Failed: %s", msg)
            return False

    def auth_keyboardinteractive(self):
        """
        Keyboard-interactive authentication. Send username and password
        """
        self.logger.debug("Trying keyboard-interactive")
        if not hasattr(self.session, "userauth_keyboardinteractive"):
            self.logger.debug(
                "keyboard-interactive is not supported by ssh library. Skipping"
            )
            return False
        try:
            self.session.userauth_keyboardinteractive(self.get_user(),
                                                      self.get_password())
            self.logger.debug("Success")
            return True
        except SSH2Error:
            msg = self.session.last_error()
            self.logger.debug("Failed: %s", msg)
            return False

    def auth_password(self):
        """
        Password authentication. Send username and password
        """
        self.logger.debug("Trying password authentication")
        try:
            self.session.userauth_password(self.get_user(),
                                           self.get_password())
            self.logger.debug("Success")
            return True
        except SSH2Error:
            msg = self.session.last_error()
            self.logger.debug("Failed: %s", msg)
            return False
Пример #13
0
 def _init_session(self):
     self.session = Session()
     if self.timeout:
         self.session.set_timeout(self.timeout * 1000)
     self.session.handshake(self.sock)
Пример #14
0
class SSH2Transport(Transport):
    def __init__(
        self,
        host: str,
        port: int = -1,
        auth_username: str = "",
        auth_private_key: str = "",
        auth_password: str = "",
        auth_strict_key: bool = True,
        timeout_socket: int = 5,
        timeout_transport: int = 5,
        timeout_exit: bool = True,
        ssh_config_file: str = "",
        ssh_known_hosts_file: str = "",
    ) -> None:
        """
        SSH2Transport Object

        Inherit from Transport ABC
        SSH2Transport <- Transport (ABC)

        Args:
            host: host ip/name to connect to
            port: port to connect to
            auth_username: username for authentication
            auth_private_key: path to private key for authentication
            auth_password: password for authentication
            auth_strict_key: True/False to enforce strict key checking (default is True)
            timeout_socket: timeout for establishing socket in seconds
            timeout_transport: timeout for ssh2 transport in seconds
            timeout_exit: True/False close transport if timeout encountered
            ssh_config_file: string to path for ssh config file
            ssh_known_hosts_file: string to path for ssh known hosts file

        Returns:
            N/A  # noqa: DAR202

        Raises:
            N/A

        """
        cfg_port, cfg_user, cfg_private_key = self._process_ssh_config(host, ssh_config_file)

        if port == -1:
            port = cfg_port or 22

        super().__init__(
            host,
            port,
            timeout_socket,
            timeout_transport,
            timeout_exit,
        )

        self.auth_username: str = auth_username or cfg_user
        self.auth_private_key: str = auth_private_key or cfg_private_key
        self.auth_password: str = auth_password
        self.auth_strict_key: bool = auth_strict_key
        self.ssh_known_hosts_file: str = ssh_known_hosts_file

        self.session: Session = None
        self.channel: Channel = None

        self.socket = Socket(host=self.host, port=self.port, timeout=self.timeout_socket)

    @staticmethod
    def _process_ssh_config(host: str, ssh_config_file: str) -> Tuple[Optional[int], str, str]:
        """
        Method to parse ssh config file

        In the future this may move to be a "helper" function as it should be very similar between
        paramiko and and ssh2-python... for now it can be a static method as there may be varying
        supported args between the two transport drivers.

        Args:
            host: host to lookup in ssh config file
            ssh_config_file: string path to ssh config file; passed down from `Scrape`, or the
                `NetworkDriver` or subclasses of it, in most cases.

        Returns:
            Tuple: port to use for ssh, username to use for ssh, identity file (private key) to
                use for ssh auth

        Raises:
            N/A

        """
        ssh = SSHConfig(ssh_config_file)
        host_config = ssh.lookup(host)
        return host_config.port, host_config.user or "", host_config.identity_file or ""

    def open(self) -> None:
        """
        Parent method to open session, authenticate and acquire shell

        Args:
            N/A

        Returns:
            N/A  # noqa: DAR202

        Raises:
            Exception: if socket handshake fails
            ScrapliAuthenticationFailed: if all authentication means fail

        """
        if not self.socket.socket_isalive():
            self.socket.socket_open()

        self.session = Session()
        self.set_timeout(self.timeout_transport)
        try:
            self.session.handshake(self.socket.sock)
        except Exception as exc:
            LOG.critical(
                f"Failed to complete handshake with host {self.host}; " f"Exception: {exc}"
            )
            raise exc

        if self.auth_strict_key:
            LOG.debug(f"Attempting to validate {self.host} public key")
            self._verify_key()

        LOG.debug(f"Session to host {self.host} opened")
        self._authenticate()
        if not self._isauthenticated():
            msg = f"Authentication to host {self.host} failed"
            LOG.critical(msg)
            raise ScrapliAuthenticationFailed(msg)

        self._open_channel()

    def _verify_key(self) -> None:
        """
        Verify target host public key, raise exception if invalid/unknown

        Args:
            N/A

        Returns:
            N/A  # noqa: DAR202

        Raises:
            KeyVerificationFailed: if public key verification fails

        """
        known_hosts = SSHKnownHosts(self.ssh_known_hosts_file)

        if self.host not in known_hosts.hosts.keys():
            raise KeyVerificationFailed(f"{self.host} not in known_hosts!")

        remote_server_key_info = self.session.hostkey()
        encoded_remote_server_key = remote_server_key_info[0]
        raw_remote_public_key = base64.encodebytes(encoded_remote_server_key)
        remote_public_key = raw_remote_public_key.replace(b"\n", b"").decode()

        if known_hosts.hosts[self.host]["public_key"] != remote_public_key:
            raise KeyVerificationFailed(
                f"{self.host} in known_hosts but public key does not match!"
            )

    def _authenticate(self) -> None:
        """
        Parent method to try all means of authentication

        Args:
            N/A

        Returns:
            N/A  # noqa: DAR202

        Raises:
            ScrapliAuthenticationFailed: if auth fails

        """
        if self.auth_private_key:
            self._authenticate_public_key()
            if self._isauthenticated():
                LOG.debug(f"Authenticated to host {self.host} with public key auth")
                return
            if not self.auth_password or not self.auth_username:
                msg = (
                    f"Failed to authenticate to host {self.host} with private key "
                    f"`{self.auth_private_key}`. Unable to continue authentication, "
                    "missing username, password, or both."
                )
                LOG.critical(msg)
                raise ScrapliAuthenticationFailed(msg)

        self._authenticate_password()
        if self._isauthenticated():
            LOG.debug(f"Authenticated to host {self.host} with password")
            return
        self._authenticate_keyboard_interactive()
        if self._isauthenticated():
            LOG.debug(f"Authenticated to host {self.host} with keyboard interactive")

    def _authenticate_public_key(self) -> None:
        """
        Attempt to authenticate with public key authentication

        Args:
            N/A

        Returns:
            N/A  # noqa: DAR202

        Raises:
            N/A

        """
        try:
            self.session.userauth_publickey_fromfile(
                self.auth_username, self.auth_private_key.encode()
            )
        except AuthenticationError as exc:
            LOG.critical(
                f"Public key authentication with host {self.host} failed. Exception: {exc}."
            )
        except SSH2Error as exc:
            LOG.critical(
                "Unknown error occurred during public key authentication with host "
                f"{self.host}; Exception: {exc}"
            )

    def _authenticate_password(self) -> None:
        """
        Attempt to authenticate with password authentication

        Args:
            N/A

        Returns:
            N/A  # noqa: DAR202

        Raises:
            Exception: if unknown (i.e. not auth failed) exception occurs

        """
        try:
            self.session.userauth_password(self.auth_username, self.auth_password)
        except AuthenticationError:
            LOG.critical(
                f"Password authentication with host {self.host} failed. Exception: "
                f"`AuthenticationError`."
            )
        except Exception as exc:
            LOG.critical(
                "Unknown error occurred during password authentication with host "
                f"{self.host}; Exception: {exc}"
            )
            raise exc

    def _authenticate_keyboard_interactive(self) -> None:
        """
        Attempt to authenticate with keyboard interactive authentication

        Args:
            N/A

        Returns:
            N/A  # noqa: DAR202

        Raises:
            Exception: if unknown (i.e. not auth failed) exception occurs

        """
        try:
            self.session.userauth_keyboardinteractive(  # pylint: disable=C0415
                self.auth_username, self.auth_password
            )
        except AttributeError as exc:
            LOG.critical(
                "Keyboard interactive authentication not supported in your ssh2-python version. "
                f"Exception: {exc}"
            )
        except AuthenticationError:
            LOG.critical(
                f"Keyboard interactive authentication with host {self.host} failed. "
                f"Exception: `AuthenticationError`."
            )
        except Exception as exc:
            LOG.critical(
                "Unknown error occurred during keyboard interactive authentication with host "
                f"{self.host}; Exception: {exc}"
            )
            raise exc

    def _isauthenticated(self) -> bool:
        """
        Check if session is authenticated

        Args:
            N/A

        Returns:
            bool: True if authenticated, else False

        Raises:
            N/A

        """
        authenticated: bool = self.session.userauth_authenticated()
        return authenticated

    def _open_channel(self) -> None:
        """
        Open channel, acquire pty, request interactive shell

        Args:
            N/A

        Returns:
            N/A  # noqa: DAR202

        Raises:
            N/A

        """
        self.channel = self.session.open_session()
        self.channel.pty()
        self.channel.shell()
        LOG.debug(f"Channel to host {self.host} opened")

    def close(self) -> None:
        """
        Close session and socket

        Args:
            N/A

        Returns:
            N/A  # noqa: DAR202

        Raises:
            N/A

        """
        self.channel.close()
        LOG.debug(f"Channel to host {self.host} closed")
        self.socket.socket_close()

    def isalive(self) -> bool:
        """
        Check if socket is alive and session is authenticated

        Args:
            N/A

        Returns:
            bool: True if socket is alive and session authenticated, else False

        Raises:
            N/A

        """
        if self.socket.socket_isalive() and not self.channel.eof() and self._isauthenticated():
            return True
        return False

    def read(self) -> bytes:
        """
        Read data from the channel

        Args:
            N/A

        Returns:
            bytes: bytes output as read from channel

        Raises:
            N/A

        """
        output: bytes
        _, output = self.channel.read(65535)
        return output

    def write(self, channel_input: str) -> None:
        """
        Write data to the channel

        Args:
            channel_input: string to send to channel

        Returns:
            N/A  # noqa: DAR202

        Raises:
            N/A

        """
        self.channel.write(channel_input)

    def set_timeout(self, timeout: int) -> None:
        """
        Set session timeout

        Args:
            timeout: timeout in seconds

        Returns:
            N/A  # noqa: DAR202

        Raises:
            N/A

        """
        # ssh2-python expects timeout in milliseconds
        self.session.set_timeout(timeout * 1000)
Пример #15
0
class IrmaSFTPv2(FTPInterface):
    """Irma SFTPv2 handler

    This class handles the connection with a sftp server
    functions for interacting with it.
    """

    _Exception = IrmaSFTPv2Error

    # ==================================
    #  Constructor and Destructor stuff
    # ==================================

    def __init__(self, host, port, auth, key_path, user, passwd,
                 dst_user=None, upload_path='uploads', hash_check=False,
                 autoconnect=True):
        self._sess = None
        self._client = None
        super().__init__(host, port, auth, key_path, user, passwd, dst_user,
                         upload_path, hash_check, autoconnect)

    def connected(self):
        return self._sess is not None

    # ============================
    #  Overridden private methods
    # ============================

    def _connect(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((self._host, self._port))
        self._sess = Session()
        self._sess.handshake(sock)
        if self._auth == 'key':
            # self._pubkey_path must be generated from private key
            # s.userauth_publickey_fromfile(self._user, self._pubkey_path,
            #                               self._key_path, '')
            raise IrmaSFTPv2Error("Pub key authentication not implemented")
        else:
            self._sess.userauth_password(self._user, self._passwd)
        self._client = self._sess.sftp_init()

    def _disconnect(self, *, force=False):
        self._client = None
        if not force:
            self._sess.disconnect()
        self._sess = None

    def _upload(self, remote, fobj):
        mode = LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | \
            LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH
        opt = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE
        with self._client.open(remote, opt, mode) as rfh:
            for chunk in iter(lambda: fobj.read(1024*1024), b""):
                rfh.write(chunk)

    def _download(self, remote, fobj):
        with self._client.open(remote, 0, 0) as rfh:
            for size, data in rfh:
                fobj.write(data)

    def _ls(self, remote):
        with self._client.opendir(remote) as rfh:
            paths = (p[1].decode('utf-8') for p in rfh.readdir())
            return [p for p in paths if p not in ['.', '..']]

    def _is_file(self, remote):
        return not self._is_dir(remote)

    def _is_dir(self, remote):
        st = self._client.stat(remote)
        return stat.S_ISDIR(st.st_mode)

    def _rm(self, remote):
        self._client.unlink(remote)

    def _rmdir(self, remote):
        self._client.rmdir(remote)

    def _mkdir(self, remote):
        mode = LIBSSH2_SFTP_S_IRUSR | \
               LIBSSH2_SFTP_S_IWUSR | \
               LIBSSH2_SFTP_S_IXUSR
        self._client.mkdir(remote, mode)

    def _mv(self, oldremote, newremote):
        self._client.rename(oldremote, newremote)
import smtplib
import socket

from ssh2.session import Session

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Connecting to Machine...")
s.connect(("172.18.51.115", 22))

session = Session()
session.handshake(s)
session.userauth_password('*****', '****')

try:
    # Creating a session
    channel = session.open_session()
    channel.execute('ls')
    size, data = channel.read()
    while size > 0:
        print(data.decode())
        size, data = channel.read()
    channel.close()

    if channel.get_exit_status() == 0:
        print("Connection established")
        s1 = smtplib.SMTP('smtp.gmail.com', 587)

        s1 = smtplib.SMTP_SSL("smtp.gmail.com", 465)
        s1.ehlo()

        s1.starttls()
Пример #17
0
    # asyncssh
    # Source: https://asyncssh.readthedocs.io/en/latest/
    import asyncssh

    async with asyncssh.connect('ssh.example.com' username="******", password="******") as conn:
        result = await conn.run(command)

    # ssh2-python
    # Source: https://pypi.org/project/ssh2-python/
    from ssh2.session import Session
    import socket

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(('ssh.example.com', 22))

    session = Session()
    session.handshake(sock)

    session.userauth_password("username", "password")

    channel = session.open_session()
    channel.execute(command)
    channel.wait_eof()
    channel.close()
    channel.wait_closed()

    # twisted.conch
    # Source: https://twistedmatrix.com/documents/current/conch/examples/index.html
    # https://stackoverflow.com/questions/22196373/sshcommandclientendpoint-twisted-how-to-execute-more-than-one-commands
    # https://twistedmatrix.com/documents/current/conch/howto/conch_client.html
    from twisted.conch.endpoints import SSHCommandClientEndpoint
Пример #18
0
 def test_internal_methods(self):
     self.session = Session()
     self.session.flag(LIBSSH2_FLAG_COMPRESS, True)
     self.session.handshake(self.sock)
     response = self.session.methods(LIBSSH2_METHOD_COMP_CS)
     assert response[0] in COMP_BTYES
Пример #19
0
	def upload(self, screenshot, name):
		self.loadSettings()
		#Save to a temporary file
		timestamp = time.time()
		try:
			tmpFilename = QDesktopServices.storageLocation(QDesktopServices.TempLocation) + "/" + ScreenCloud.formatFilename(str(timestamp))
		except AttributeError:
			from PythonQt.QtCore import QStandardPaths #fix for Qt5
			tmpFilename = QStandardPaths.writableLocation(QStandardPaths.TempLocation) + "/" + ScreenCloud.formatFilename(str(timestamp))
		screenshot.save(QFile(tmpFilename), ScreenCloud.getScreenshotFormat())
		#Connect to server
		try:
			sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			sock.connect((self.host, self.port))
			session = Session()
			session.handshake(sock)
		except Exception as e:
			ScreenCloud.setError(e.message)
			return False
		if self.authMethod == "Password":
			try:
				session.userauth_password(self.username, self.password)
			except ssh2.exceptions.AuthenticationError:
				ScreenCloud.setError("Authentication failed (password)")
				return False
		else:
			try:
				session.userauth_publickey_fromfile(self.username, self.keyfile, passphrase=self.passphrase)
			except ssh2.exceptions.AuthenticationError:
				ScreenCloud.setError("Authentication failed (key)")
				return False
			except Exception as e:
				ScreenCloud.setError("Unknown error: " + e.message)
				return False
		sftp = session.sftp_init()
		mode = LIBSSH2_SFTP_S_IRUSR | \
			LIBSSH2_SFTP_S_IWUSR | \
			LIBSSH2_SFTP_S_IRGRP | \
			LIBSSH2_SFTP_S_IROTH
		f_flags = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE
		try:
			try:
				sftp.opendir(self.folder)
			except ssh2.exceptions.SFTPError:
				sftp.mkdir(self.folder, mode | LIBSSH2_SFTP_S_IXUSR)
			(filepath, filename) = os.path.split(ScreenCloud.formatFilename(name))
			if len(filepath):
				for folder in filepath.split("/"):
					try:
						sftp.mkdir(folder)
					except IOError:
						pass
			source = tmpFilename
			destination = self.folder + "/" + ScreenCloud.formatFilename(filename)
			with open(source, 'rb') as local_fh, sftp.open(destination, f_flags, mode) as remote_fh:
				for data in local_fh:
					remote_fh.write(data)
		except IOError:
			ScreenCloud.setError("Failed to write " + self.folder + "/" + ScreenCloud.formatFilename(name) + ". Check permissions.")
			return False
		sock.close()
		if self.url:
			ScreenCloud.setUrl(self.url + ScreenCloud.formatFilename(name))
		return True
Пример #20
0
def exec_ssh_cmd(cmd, host):
    username = '******'

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, 22))

    session = Session()
    session.handshake(sock)
    session.agent_auth(username)

    session = Session()
    session.userauth_publickey_fromfile(username, '~/.ssh/keys/exoscale')
    channel = session.open_session()
    channel.execute(cmd)

    size, data = channel.read()

    while size > 0:
        print(data)
        size, data = channel.read()
    channel.close()
    print("Exit status: %s" % channel.get_exit_status())
Пример #21
0
import os
import socket

from ssh2.session import Session
from ssh2.utils import version

# Connection settings
host = 'localhost'
user = os.getlogin()

# Make socket, connect
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, 22))

# Initialise
session = Session()
session.handshake(sock)

# List available authentication methods
print(session.userauth_list(user))

# Convenience function for agent based authentication
session.agent_auth(user)

# Agent capabilities
agent = session.agent_init()
agent.connect()
identities = agent.get_identities()
print(identities)
print(identities[0].magic)
del agent
Пример #22
0
def show_entry_fields():
    #   sys.stderr.write("\x1b[2J\x1b[H")
    U = Username.get()  # USERNAME
    P = Password.get()  # PASSWORD
    H = Hostname.get()  # DEVICE HOSTNAME
    L = log.get()  # EVENT LOG OR ACS LOG
    D = device.get()  # CISCO HUAWEI(9306)
    #   T=typ.get()# INFO CRITICAL TACCACCT DEBUG
    M = mon.get()  # MONTH
    Day = day.get()  # DAY
    K = Keywords.get()  #keywords
    T = ''
    ############################################################################### LOGIN
    host = '79.128.180.20'
    H = H.replace(' ', '')
    if len(H) == 0:
        print('\nEnter a Hostname...\n')
        return
    if len(K) > 0:
        K = '|' + K.replace(' ', ' |')

    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((host, 22))  #host/port
        #start session
        session = Session()
        session.handshake(sock)
        session.userauth_password(U, P)
        channel = session.open_session()
        print('Login Successful!!!\n')
    except:
        print('WRONG CREDENTIALS\n')
        return

    channel.shell()

    channel.write('host ' + H + '\n')
    size, data = channel.read()
    ip = re.findall(r'(\d{2}.\d{1}\d?\d?.\d{1}\d?\d?.\d{1}\d?\d?)', str(data))
    print(data.decode())
    if 'not found:' in data.decode():
        return

############################################################################### PATHFINDER
    H = ' ' + H + ' '
    if L == "Machine Logs":
        command = K
        if D == 'Cisco':
            #           if T == 'Tacacct':#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
            #               print('Wrong input--> Type')
            #               return

            print('>>>', 'cd /netlog/cisco')
            channel.write('cd /netlog/cisco\n')
        elif not D == 'Cisco':
            T = 'info'
            #           if not T== 'Info':#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
            #               print('Wrong input--> Type')
            #               return

            print('>>>', 'cd /netlog/misco')
            channel.write('cd /netlog/misco\n')

        print('>>>', 'ls -lt')
        channel.write('ls -lt\n')
#################################################################################################

    elif L == "ACS (Tacaccs)":
        #       check the condidions that do not work with
        #GET IP FROM HOSTNAME

        HH = H
        if D == 'Cisco':
            H = " '" + ip[0] + ",' "
            command = r''' | awk -F"atheacs|CmdSet=|RequestLatency|User=|Port=" '/CmdAV/ {print$1$5$3}'| egrep -v "acctman|scriptman" | grep CmdAV | more'''
            #           if not T == 'Tacacct':#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
            #               print('Wrong input--> Type')
            #               return
            T = 'tacacct'
            print('>>>', 'cd /netlog/acslogs')
            channel.write('cd /netlog/acslogs\n')

        elif D == 'Huawei 9306':
            H = " '" + ip[0] + ",' "
            command = r''' | awk -F"atheacs|CmdSet=|RequestLatency|User=|Authen-Method=|Protocol" '/CmdAV/ {print$1$6$3}' | egrep -v "acctman|scriptman" | grep CmdAV | more'''
            #           if not T == 'Tacacct':#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
            #               print('Wrong input--> Type')
            #               return
            T = 'tacacct'
            print('>>>', 'cd /netlog/acslogs')
            channel.write('cd /netlog/acslogs\n')

        elif D == 'Huawei':
            command = r''' | awk -F":" '/command/ {printf"%s-%s-%s %s -> %s %s \n",$1,$2,$6,$7,$8,$9}' | more'''
            #           if not T == 'Info':#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
            #               print('Wrong input--> Type')
            #               return
            T = 'info'
            print('>>>', 'cd /netlog/misco')
            channel.write('cd /netlog/misco\n')

        print('>>>', 'ls -lt')
        channel.write('ls -lt\n')

#
################################################################################ GIVE COMMAND AND EXPORT
#       READ FROM SCREEN AND CREATE STRING NAMED TEXT
    text = ''
    count = 0
    buffer = 1024
    while True:
        size, data = channel.read(buffer)
        text += str(data.decode())
        count += int(size)
        if size < buffer: break
#   print(text)

    text = re.sub(' +', ' ', text).strip()  #remove blanks
    df = pd.DataFrame([x.split(' ') for x in text.split('\n')]).dropna()
    df = df[[3, 5, 6, 7, 8]]
    df = df.rename(columns={
        3: "Type",
        5: "Month",
        6: "Day",
        7: "Time",
        8: "File_name"
    })

    try:
        df2 = df.loc[(df['Month'] == M).reset_index(drop=True)
                     & (df['Day'] == Day) &
                     (df["File_name"].str.contains(T.lower()))].reset_index(
                         drop=True)  #month date and type ||
    except:
        print('\nNo records for', M, Day, '-->', T + '.log')
        return

    ij = 0
    if len(df2) > 1:
        print('\nSelect file\n', df2)

        while True:
            ij = input("Please enter a number: ")
            try:
                if ij == '':
                    print('Canceled..\n\n')
                    return
                elif int(ij) >= 0 and int(ij) <= (len(df2) - 1):
                    break

            except:
                print('\nInvalid input. Please select a number between 0 and',
                      len(df2) - 1)

    print('Opening: ', df2.at[int(ij), 'File_name'])
    string = df2.at[int(ij), 'File_name']

    if '.gz' in string:
        c1 = 'zgrep'
    else:
        c1 = 'grep'

    final_command = c1 + H + string + command
    print('\n>>>', final_command)

    channel.write(final_command + '\n')
    channel.write('date\n')

    text = ''
    count = 0
    buffer = 4096
    while True:
        size, data = channel.read(buffer)
        text += str(data.decode())
        count += int(size)
        print('reading:', count)
        if size < buffer:
            print('reading complete..')
            break
    text = text.replace('CmdAV=', '')
    text = text.replace('CmdArgAV=', '')
    text = text.replace('<cr>', '')

    text_file = open("out.txt", "w")
    text_file.write(text)
    text_file.close()
    os.startfile("out.txt")
Пример #23
0
class SSHIOStream(IOStream):
    SSH_KEY_PREFIX = config.path.ssh_key_prefix

    _key_cache = cachetools.TTLCache(100, ttl=60)

    def __init__(self, sock, cli, *args, **kwargs):
        super(SSHIOStream, self).__init__(sock, *args, **kwargs)
        self.cli = cli
        self.script = self.cli.script
        self.logger = cli.logger
        self.session = None
        self.channel = None

    def __del__(self):
        self.channel = None
        self.session = None

    @classmethod
    @cachetools.cachedmethod(operator.attrgetter("_key_cache"), lock=lambda _: key_lock)
    def get_publickey(cls, pool):
        """
        Return public, private key pair
        :return: bytes, bytes or None, None
        """
        logger.debug("Getting keys for pool %s", pool)
        pub_path = os.path.join(config.path.ssh_key_prefix, pool, "id_rsa.pub")
        priv_path = os.path.join(config.path.ssh_key_prefix, pool, "id_rsa")
        if not os.path.exists(pub_path):
            logger.debug("No public key for pool %s (%s)", pool, pub_path)
            return None, None
        if not os.path.exists(priv_path):
            logger.debug("No private key for pool %s (%s)", pool, priv_path)
            return None, None
        with open(pub_path) as fpub, open(priv_path) as fpriv:
            return fpub.read(), fpriv.read()

    @tornado.gen.coroutine
    def startup(self):
        """
        SSH session startup
        """
        user = self.script.credentials["user"]
        if user is None:
            user = ""
        self.logger.debug("Startup ssh session for user '%s'", user)
        self.session = Session()
        try:
            self.session.handshake(self.socket)
            host_hash = smart_bytes(self.session.hostkey_hash(LIBSSH2_HOSTKEY_HASH_SHA1))
            hex_hash = smart_text(codecs.encode(host_hash, "hex"))
            self.logger.debug("Connected. Host fingerprint is %s", hex_hash)
            auth_methods = self.session.userauth_list(user)
            if not auth_methods:
                self.logger.info("No supported authentication methods. Failed to log in")
                raise CLIAuthFailed("Failed to log in")
            self.logger.debug("Supported authentication methods: %s", ", ".join(auth_methods))
            # Try to authenticate
            authenticated = False
            for method in auth_methods:
                ah = getattr(self, "auth_%s" % method.replace("-", ""), None)
                if ah:
                    metrics["ssh_auth", ("method", method)] += 1
                    authenticated |= ah()
                    if authenticated:
                        metrics["ssh_auth_success", ("method", method)] += 1
                        break
                    metrics["ssh_auth_fail", ("method", method)] += 1
            if not authenticated:
                self.logger.error("Failed to authenticate user '%s'", user)
                raise CLIAuthFailed("Failed to log in")
            self.logger.debug("User is authenticated")
            self.logger.debug("Open channel")
            self.channel = self.session.open_session()
            self.channel.pty("xterm")
            self.channel.shell()
            self.session.keepalive_config(False, 60)
            self.session.set_blocking(False)
        except SSH2Error as e:
            self.logger.info("SSH Error: %s", e)
            raise CLISSHProtocolError("SSH Error: %s" % e)

    def read_from_fd(self):
        try:
            metrics["ssh_reads"] += 1
            code, data = self.channel.read(self.read_chunk_size)
            if code == 0:
                if self.channel.eof():
                    self.logger.info("SSH session reset")
                    self.close()
                metrics["ssh_reads_blocked"] += 1
                return None
            elif code > 0:
                metrics["ssh_read_bytes"] += len(data)
                return data
            elif code == LIBSSH2_ERROR_EAGAIN:
                metrics["ssh_reads_blocked"] += 1
                return None  # Blocking call
            metrics["ssh_errors", ("code", code)] += 1
            raise CLISSHProtocolError("SSH Error code %s" % code)
        except SSH2Error as e:
            raise CLISSHProtocolError("SSH Error: %s" % e)

    def write_to_fd(self, data):
        # ssh2 doesn't accept memoryview
        metrics["ssh_writes"] += 1
        if isinstance(data, memoryview):
            data = data.tobytes()
        try:
            _, written = self.channel.write(data)
            metrics["ssh_write_bytes"] += written
            return written
        except SSH2Error as e:
            raise CLISSHProtocolError("SSH Error: %s" % e)

    def close(self, exc_info=False):
        if not self.closed():
            if self.channel:
                self.logger.debug("Closing channel")
                try:
                    self.channel.close()
                except SSH2Error as e:
                    self.logger.debug("Cannot close channel clearly: %s", e)
                self.channel = None
            if self.session:
                self.logger.debug("Closing ssh session")
                self.session = None
        super(SSHIOStream, self).close(exc_info=exc_info)

    def get_user(self):
        # type: () -> str
        """
        Get current user
        """
        return self.script.credentials["user"] or ""

    def get_password(self):
        # type: () -> str
        """
        Get current user's password
        """
        return self.script.credentials["password"] or ""

    def auth_publickey(self):
        """
        Public key authentication
        """
        self.logger.debug("Trying publickey authentication")
        pub_key, priv_key = self.get_publickey(self.script.pool)
        if not pub_key or not priv_key:
            self.logger.debug("No keys for pool. Skipping")
            return False
        user = self.get_user()
        try:
            self.session.userauth_publickey_frommemory(user, priv_key, "", pub_key)
            return True
        except SSH2Error:
            msg = self.session.last_error()
            self.logger.debug("Failed: %s", msg)
            return False

    def auth_keyboardinteractive(self):
        """
        Keyboard-interactive authentication. Send username and password
        """
        self.logger.debug("Trying keyboard-interactive")
        if not hasattr(self.session, "userauth_keyboardinteractive"):
            self.logger.debug("keyboard-interactive is not supported by ssh library. Skipping")
            return False
        try:
            self.session.userauth_keyboardinteractive(self.get_user(), self.get_password())
            self.logger.debug("Success")
            return True
        except SSH2Error:
            msg = self.session.last_error()
            self.logger.debug("Failed: %s", msg)
            return False

    def auth_password(self):
        """
        Password authentication. Send username and password
        """
        self.logger.debug("Trying password authentication")
        try:
            self.session.userauth_password(self.get_user(), self.get_password())
            self.logger.debug("Success")
            return True
        except SSH2Error:
            msg = self.session.last_error()
            self.logger.debug("Failed: %s", msg)
            return False
from ssh2.session import Session
from ssh2.session import LIBSSH2_HOSTKEY_HASH_SHA1, LIBSSH2_HOSTKEY_TYPE_RSA
from ssh2.knownhost import LIBSSH2_KNOWNHOST_TYPE_PLAIN, \
    LIBSSH2_KNOWNHOST_KEYENC_RAW, LIBSSH2_KNOWNHOST_KEY_SSHRSA

# Connection settings
host = 'localhost'
user = os.getlogin()
known_hosts = os.sep.join([os.path.expanduser('~'), '.ssh', 'known_hosts'])

# Make socket, connect
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, 22))

# Initialise
session = Session()
session.handshake(sock)

host_key, key_type = session.hostkey()

server_key_type = LIBSSH2_KNOWNHOST_KEY_SSHRSA \
                  if key_type == LIBSSH2_HOSTKEY_TYPE_RSA \
                     else LIBSSH2_KNOWNHOST_KEY_SSHDSS

kh = session.knownhost_init()
_read_hosts = kh.readfile(known_hosts)
print("Read %s hosts from known hosts file at %s" % (_read_hosts, known_hosts))

# Verification
type_mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | \
            LIBSSH2_KNOWNHOST_KEYENC_RAW | \
Пример #25
0
from ssh2.session import Session
from ssh2.channel import Channel
s = Session()
c = Channel(s)
Пример #26
0
class IrmaSFTPv2(FTPInterface):
    """Irma SFTPv2 handler

    This class handles the connection with a sftp server
    functions for interacting with it.
    """

    _Exception = IrmaSFTPv2Error

    # ==================================
    #  Constructor and Destructor stuff
    # ==================================

    def __init__(self,
                 host,
                 port,
                 auth,
                 key_path,
                 user,
                 passwd,
                 dst_user=None,
                 upload_path='uploads',
                 hash_check=False,
                 autoconnect=True):
        self._sess = None
        self._client = None
        super().__init__(host, port, auth, key_path, user, passwd, dst_user,
                         upload_path, hash_check, autoconnect)

    def connected(self):
        return self._sess is not None

    # ============================
    #  Overridden private methods
    # ============================

    def _connect(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((self._host, self._port))
        self._sess = Session()
        self._sess.handshake(sock)
        if self._auth == 'key':
            # self._pubkey_path must be generated from private key
            # s.userauth_publickey_fromfile(self._user, self._pubkey_path,
            #                               self._key_path, '')
            raise IrmaSFTPv2Error("Pub key authentication not implemented")
        else:
            self._sess.userauth_password(self._user, self._passwd)
        self._client = self._sess.sftp_init()

    def _disconnect(self, *, force=False):
        self._client = None
        if not force:
            self._sess.disconnect()
        self._sess = None

    def _upload(self, remote, fobj):
        mode = LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | \
            LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH
        opt = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE
        with self._client.open(remote, opt, mode) as rfh:
            for chunk in iter(lambda: fobj.read(1024 * 1024), b""):
                rfh.write(chunk)

    def _download(self, remote, fobj):
        with self._client.open(remote, 0, 0) as rfh:
            for size, data in rfh:
                fobj.write(data)

    def _ls(self, remote):
        with self._client.opendir(remote) as rfh:
            paths = (p[1].decode('utf-8') for p in rfh.readdir())
            return [p for p in paths if p not in ['.', '..']]

    def _is_file(self, remote):
        return not self._is_dir(remote)

    def _is_dir(self, remote):
        st = self._client.stat(remote)
        return stat.S_ISDIR(st.st_mode)

    def _rm(self, remote):
        self._client.unlink(remote)

    def _rmdir(self, remote):
        self._client.rmdir(remote)

    def _mkdir(self, remote):
        mode = LIBSSH2_SFTP_S_IRUSR | \
               LIBSSH2_SFTP_S_IWUSR | \
               LIBSSH2_SFTP_S_IXUSR
        self._client.mkdir(remote, mode)

    def _mv(self, oldremote, newremote):
        self._client.rename(oldremote, newremote)
from __future__ import print_function

import os
import socket

from ssh2.session import Session

host = '192.168.0.16'
user = os.getlogin()

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, 22))

session = Session()
session.handshake(sock)
session.userauth_password(os.getenv('remoteUser'), os.getenv('remotePassword'))

channel = session.open_session()
channel.execute('echo walrus | sudo -S reboot')
size, data = channel.read()
while size > 0:
    print(data)
    size, data = channel.read()
channel.close()
print("Exit status: %s" % channel.get_exit_status())
Пример #28
0
class ssh:
    """
    format of the config file(eg. ~/.emuhelper/server.conf):
    [server]
    ip = xx.xx.xx.xx
    user = xxx
    serverdir = /xxx/xxx/xxx/
    password = xxx
    """
    def __init__(self):
        self.ip = None
        self.user = None
        self.password = None

    def get_info(self, conf):
        config = configparser.ConfigParser()
        config.read(conf)
        self.ip = config.get("server", "ip")
        self.user = config.get("server", "user")
        self.password = config.get("server", "password")
        self.serverdir = config.get("server", "serverdir")

    def login(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect((self.ip, 22))

        self.session = Session()
        self.session.handshake(self.sock)
        self.session.userauth_password(self.user, self.password)

    def submit(self, workdir, jobfile, server="pbs"):
        """
        server: pbs or llhpc
        """
        channel = self.session.open_session()
        if server == "llhpc":
            channel.execute("cd %s; yhbatch %s" %
                            (os.path.join(self.serverdir, workdir), jobfile))
        elif server == "pbs":
            channel.execute("cd %s; qsub %s" %
                            (os.path.join(self.serverdir, workdir), jobfile))
        print("\n\n")
        print("=========================================\n")
        print("     information from remote server\n")
        print("=========================================\n")
        all_data = b''
        size, data = channel.read()
        while size > 0:
            #print(data.decode())
            all_data = all_data + data
            size, data = channel.read()
        channel.close()
        print(all_data.decode())
        print("Exit status: %s" % channel.get_exit_status())

    def execute(self, cmd):
        channel = self.session.open_session()
        channel.execute(cmd)
        print("\n\n")
        print("=========================================\n")
        print("     information from remote server\n")
        print("=========================================\n")
        all_data = b''
        size, data = channel.read()
        while size > 0:
            #print(data.decode())
            all_data = all_data + data
            size, data = channel.read()
        channel.close()
        print(all_data.decode())
        print("Exit status: %s" % channel.get_exit_status())

    def execute_silent(self, cmd):
        channel = self.session.open_session()
        channel.execute(cmd)
        all_data = b''
        size, data = channel.read()
        while size > 0:
            all_data = all_data + data
            size, data = channel.read()
        channel.close()
        return all_data
Пример #29
0
    def _establish_ssh_session(self):
        # Connect to remote host.
        try:
            sock = socket.create_connection(
                (str(self._ssh_host), self._ssh_port))
        except Exception:
            log.error("Cannot connect to host '%s' (%s, %d).", self.name,
                      self._ssh_host, self._ssh_port)
            raise

        # SSH handshake.
        ssh_session = Session()
        ssh_session.handshake(sock)

        # Verify host key. Accept keys from previously unknown hosts on first connection.
        hosts = ssh_session.knownhost_init()
        testbed_root = os.path.dirname(os.path.abspath(inspect.stack()[-1][1]))
        known_hosts_path = os.path.join(testbed_root, KNOWN_HOSTS_FILE)
        try:
            hosts.readfile(known_hosts_path)
        except ssh2.exceptions.KnownHostReadFileError:
            pass  # ignore, file is created/overwritten later

        host_key, key_type = ssh_session.hostkey()
        server_type = None
        if key_type == LIBSSH2_HOSTKEY_TYPE_RSA:
            server_type = LIBSSH2_KNOWNHOST_KEY_SSHRSA
        else:
            server_type = LIBSSH2_KNOWNHOST_KEY_SSHDSS
        type_mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | server_type

        try:
            hosts.checkp(
                str(self._ssh_host).encode('utf-8'), self._ssh_port, host_key,
                type_mask)
        except ssh2.exceptions.KnownHostCheckNotFoundError:
            log.warn("Host key of '%s' (%s, %d) added to known hosts.",
                     self.name, self._ssh_host, self._ssh_port)
            hosts.addc(
                str(self._ssh_host).encode('utf-8'), host_key, type_mask)
            hosts.writefile(known_hosts_path)
        except ssh2.exceptions.KnownHostCheckMisMatchError:
            log.error("Host key of '%s' (%s, %d) does not match known key.",
                      self.name, self._ssh_host, self._ssh_port)
            raise

        # Authenticate at remote host.
        try:
            if self._identity_file is None:
                ssh_session.agent_auth(self._username)
            else:
                ssh_session.userauth_publickey_fromfile(
                    self._username, self._identity_file)
        except Exception:
            log.error("Authentication at host '%s' (%s, %d) failed.",
                      self.name, self._ssh_host, self._ssh_port)
            ssh_session.disconnect()
            raise

        return ssh_session
Пример #30
0
    def _connect(self):
        """create a connection for this thread"""
        self._abort = False
        size_bk = 0
        if self.parent_ui:
            size_bk += self.parent_ui.progress["maximum"]
            self.parent_ui.progress.configure(mode="indeterminate",
                                              maximum=100)
            self.parent_ui.progress.start()
        if self._mode == "SFTP":
            try:
                sock = socket(AF_INET, SOCK_STREAM)
                sock.settimeout(10)
                sock.connect((self._host, self._port))
                cli = Session()
                cli.set_timeout(10000)
                cli.handshake(sock)

                cli.userauth_password(self._name, self._passwd)
                cli.set_timeout(0)
                self._connection = cli.sftp_init()

            except Timeout:
                self._abort = True
                messagebox.showerror("Connection Error",
                                     "Connection timeout on login.")
            except AuthenticationError as e:
                self._abort = True
                messagebox.showerror("Authentication Error",
                                     "Wrong login credentials.")
            except Exception as e:
                print(type(e), e.args, str(e))
                self._abort = True
                messagebox.showerror(
                    "Connection Error",
                    "Could not establish a connection.\n%s" % e)
            finally:
                if self.parent_ui:
                    self.parent_ui.progress.stop()
                    self.parent_ui.progress.configure(value=0,
                                                      mode="determinate",
                                                      maximum=size_bk)

        else:  # FTP
            try:
                ftp = FTP()
                ftp.encoding = self._enc
                ftp.connect(self._host, self._port, 10)
                ftp.login(self._name, self._passwd)

                self._connection = ftp
            except Exception as e:
                self._abort = True
                messagebox.showerror("Connection Error", str(e))
            finally:
                if self.parent_ui:
                    self.parent_ui.progress.stop()
                    self.parent_ui.progress.configure(value=0,
                                                      mode="determinate",
                                                      maximum=size_bk)
Пример #31
0
 def create_session(self):
     self.session = Session()
     self.session.set_timeout(0.5)
     self.session.handshake(self.socket)
     return self.session.userauth_password(self.username, self.password)
Пример #32
0
    def linux_host_runshell(self, commands):
        assert type(commands) is list and len(
            commands
        ) != 0, "[Assert Error] Input commands is not a list or it's empty!"
        auth_method = 'password'

        # Make socket, connect
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((self.host_ip, self.port))

        # Initialise
        session = Session()
        session.handshake(sock)
        userauth_list = session.userauth_list(self.username)
        self.LOG.print_log(
            'DEBUG', "In host {}, user {} authentication list is: {}.".format(
                self.host_ip, self.username, userauth_list))
        assert auth_method in userauth_list, "[Assert Error] The user in linux host must support {} authentication!".format(
            auth_method)
        session.userauth_password(self.username, self.password)

        # Channel initialise, exec and wait for end
        channel = session.open_session()
        channel.shell()

        for command in commands:
            self.LOG.print_log('INFO', "Run cmd: {}".format(command))
            channel.write(command + '\n')

        # check session is in non-blocking mode
        blocking_mode = session.get_blocking()
        self.LOG.print_log(
            'INFO',
            'Now the session is in blocking mode ? {}'.format(blocking_mode))
        if blocking_mode:
            self.LOG.print_log('INFO', "Set session blocking mode to False...")
            session.set_blocking(False)
            blocking_mode = session.get_blocking()
            self.LOG.print_log(
                'INFO', 'Now the session is in blocking mode ? {}'.format(
                    blocking_mode))

        # Get output
        round_num = 0
        timeout = 40
        begin_time = time.time()

        output = b''
        while True:
            if len(output) == 0 and time.time() - begin_time > timeout * 3:
                self.LOG.print_log(
                    'INFO', 'Nothing is received in {} seconds, exit.'.format(
                        timeout * 3))
                break
            elif len(output) > 0 and time.time() - begin_time > timeout:
                self.LOG.print_log(
                    'INFO',
                    'No data is received in {} seconds, exit.'.format(timeout))
                break

            size, data = channel.read()
            if size > 0:
                self.LOG.print_log(
                    'DEBUG',
                    "round_number {} Got new data:\n".format(round_num, data))
                self.LOG.print_plain_log('DEBUG', data)
                output += data
                begin_time = time.time()
            else:
                self.LOG.print_log(
                    'DEBUG', "round_number {} No new data.".format(round_num))
            time.sleep(0.5)
            round_num += 1

        channel.close()

        output = output.decode("utf-8").strip()
        return_code = channel.get_exit_status()

        self.LOG.print_log('INFO', "output is:\n")
        self.LOG.print_plain_log('INFO', output)
        self.LOG.print_log('INFO', "exit status is:{}.".format(return_code))

        return output, return_code
Пример #33
0
class SSH2NetSessionSSH2:
    def __init__(self, p_self):
        """
        Initialize SSH2NetSessionSSH2 Object

        This is the default underlying "driver" for ssh2net. This has been pulled out of the
        "base" SSH2NetSession class to provide a mechanism for supporting both "ssh2-python" and
        "paramiko".

        Args:
            p_self: SSH2Net object

        Returns:
            N/A  # noqa

        Raises:
            N/A  # noqa

        """
        self.__dict__ = p_self.__dict__
        self._session_alive = p_self._session_alive
        self._session_open = p_self._session_open
        self._channel_alive = p_self._channel_alive

    def _session_open_connect(self) -> None:
        """
        Perform session handshake

        Args:
            N/A  # noqa

        Returns:
            N/A  # noqa

        Raises:
            Exception: catch all for unknown exceptions during session handshake

        """
        self.session = Session()
        if self.session_timeout:
            self.session.set_timeout(self.session_timeout)
        try:
            self.session.handshake(self.sock)
        except Exception as exc:
            logging.critical(
                f"Failed to complete handshake with host {self.host}; " f"Exception: {exc}"
            )
            raise exc

    def _session_public_key_auth(self) -> None:
        """
        Perform public key based auth on SSH2NetSession

        Args:
            N/A  # noqa

        Returns:
            N/A  # noqa

        Raises:
            Exception: catch all for unhandled exceptions

        """
        try:
            self.session.userauth_publickey_fromfile(self.auth_user, self.auth_public_key)
        except AuthenticationError:
            logging.critical(f"Public key authentication with host {self.host} failed. ")
        except Exception as exc:
            logging.critical(
                "Unknown error occurred during public key authentication with host "
                f"{self.host}; Exception: {exc}"
            )
            raise exc

    def _session_password_auth(self) -> None:
        """
        Perform password based auth on SSH2NetSession

        Args:
            N/A  # noqa

        Returns:
            N/A  # noqa

        Raises:
            AuthenticationFailed: if authentication fails
            Exception: catch all for unknown other exceptions

        """
        try:
            self.session.userauth_password(self.auth_user, self.auth_password)
        except AuthenticationError as exc:
            logging.critical(
                f"Password authentication with host {self.host} failed. Exception: {exc}."
                f"\n\tTrying keyboard interactive auth..."
            )
            try:
                self.session.userauth_keyboardinteractive(self.auth_user, self.auth_password)
            except AuthenticationError as exc:
                logging.critical(
                    f"Keyboard interactive authentication with host {self.host} failed. "
                    f"Exception: {exc}."
                )
                raise AuthenticationFailed
            except Exception as exc:
                logging.critical(
                    "Unknown error occurred during keyboard interactive authentication with host "
                    f"{self.host}; Exception: {exc}"
                )
                raise exc
        except Exception as exc:
            logging.critical(
                "Unknown error occurred during password authentication with host "
                f"{self.host}; Exception: {exc}"
            )
            raise exc

    def _channel_open_driver(self) -> None:
        """
        Open channel

        Args:
            N/A  # noqa

        Returns:
            N/A  # noqa

        Raises:
            N/A  # noqa

        """
        self.channel = self.session.open_session()
        self.channel.pty()
        logging.debug(f"Channel to host {self.host} opened")

    def _channel_invoke_shell(self) -> None:
        """
        Invoke shell on channel

        Args:
            N/A  # noqa

        Returns:
            N/A  # noqa

        Raises:
            N/A  # noqa

        """
        self._shell = True
        self.channel.shell()
Пример #34
0
    def runcmd(self, sp_ip, command, background=False, stdin_str=None):
        assert type(command) is str and command != "" and not command.isspace(), \
            "[Assert Error] Input command is invalid!"
        if stdin_str:
            assert background is False, "[Assert Error] Input background and stdin_str, you can only select one of them."
            assert type(
                stdin_str
            ) is str, "[Assert Error] Input stdin_str must be a string!"

        auth_method = 'publickey'

        # Make socket, connect
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((sp_ip, self.port))

        # Initialise
        session = Session()
        session.handshake(sock)
        userauth_list = session.userauth_list(self.username)
        self.LOG.print_log(
            'DEBUG', "In host {}, user {} authentication list is: {}.".format(
                sp_ip, self.username, userauth_list))
        assert auth_method in userauth_list, "[Assert Error] The user in storage must support {} authentication!".format(
            auth_method)
        session.userauth_publickey_fromfile(self.username,
                                            self.private_key_file)

        # Channel initialise, exec and wait for end
        self.LOG.print_log('INFO', "Run cmd: {}.".format(command))
        channel = session.open_session()
        # channel.execute(command)
        # channel.wait_eof()
        # channel.close()
        # channel.wait_closed()

        if background:
            channel.execute(command)
            time.sleep(5)
            channel.close()
        elif stdin_str:
            channel.execute(command)
            time.sleep(5)
            channel.write(stdin_str)
            channel.wait_eof()
            channel.close()
            channel.wait_closed()
        else:
            channel.execute(command)
            channel.wait_eof()
            channel.close()
            channel.wait_closed()

        # Get output
        output = b''
        size, data = channel.read()
        while size > 0:
            output += data
            size, data = channel.read()
        output = output.decode("utf-8").strip()

        # Get exit status
        return_code = channel.get_exit_status()

        self.LOG.print_log('INFO', "Command {} - output is:\n".format(command))
        self.LOG.print_plain_log('INFO', output)
        self.LOG.print_log(
            'INFO',
            "Command {} - exit status is:{}.".format(command, return_code))

        return output, return_code