Example #1
0
USER = '******'
PASSWORD = '******'
TIMEOUT = 30
PORT = 22
try:
    ssh.connect(
        host=HOST,
        user=USER,
        password=PASSWORD,
        timeout=TIMEOUT,
        port=PORT,
    )
except LibsshSessionException as ssh_exc:
    print(f'Failed to connect to {HOST}:{PORT} over SSH: {ssh_exc!s}')

print(f'{ssh.is_connected=}')

remote_file = '/etc/hosts'
local_file = '/tmp/hosts'
sftp = ssh.sftp()
sftp.get(remote_file, local_file)
sftp.close()

remote_file = '/etc/hosts'
local_file = '/tmp/hosts'
sftp = ssh.sftp()
sftp.put(remote_file, local_file)
sftp.close()

ssh.close()
Example #2
0
class Connection(ConnectionBase):
    """SSH based connections with Paramiko"""

    transport = "ansible.netcommon.libssh"
    _log_channel = None

    def _cache_key(self):
        return "%s__%s__" % (
            self._play_context.remote_addr,
            self._play_context.remote_user,
        )

    def _connect(self):
        cache_key = self._cache_key()
        if cache_key in SSH_CONNECTION_CACHE:
            self.ssh = SSH_CONNECTION_CACHE[cache_key]
        else:
            self.ssh = SSH_CONNECTION_CACHE[
                cache_key] = self._connect_uncached()
        return self

    def _set_log_channel(self, name):
        self._log_channel = name

    def _get_proxy_command(self, port=22):
        proxy_command = None
        # Parse ansible_ssh_common_args, specifically looking for ProxyCommand
        ssh_args = [
            getattr(self._play_context, "ssh_extra_args", "") or "",
            getattr(self._play_context, "ssh_common_args", "") or "",
            getattr(self._play_context, "ssh_args", "") or "",
        ]

        if ssh_args is not None:
            args = self._split_ssh_args(" ".join(ssh_args))
            for i, arg in enumerate(args):
                if arg.lower() == "proxycommand":
                    # _split_ssh_args split ProxyCommand from the command itself
                    proxy_command = args[i + 1]
                else:
                    # ProxyCommand and the command itself are a single string
                    match = SETTINGS_REGEX.match(arg)
                    if match:
                        if match.group(1).lower() == "proxycommand":
                            proxy_command = match.group(2)

                if proxy_command:
                    break

        proxy_command = proxy_command or self.get_option("proxy_command")

        if proxy_command:
            replacers = {
                "%h": self._play_context.remote_addr,
                "%p": port,
                "%r": self._play_context.remote_user,
            }
            for find, replace in replacers.items():
                proxy_command = proxy_command.replace(find, str(replace))

        return proxy_command

    def _connect_uncached(self):
        """activates the connection object"""

        if not HAS_PYLIBSSH:
            raise AnsibleError(missing_required_lib("ansible-pylibssh"))

        ssh_connect_kwargs = {}

        remote_user = self.get_option("remote_user")
        remote_addr = self.get_option("remote_addr")
        port = self._play_context.port or 22
        display.vvv(
            "ESTABLISH LIBSSH CONNECTION FOR USER: %s on PORT %s TO %s" %
            (remote_user, port, remote_addr),
            host=remote_addr,
        )

        self.ssh = Session()

        if self._play_context.verbosity > 3:
            self.ssh.set_log_level(logging.INFO)

        self.keyfile = os.path.expanduser("~/.ssh/known_hosts")

        proxy_command = self._get_proxy_command(port)

        try:
            private_key = None
            if self._play_context.private_key_file:
                with open(
                        os.path.expanduser(
                            self._play_context.private_key_file)) as fp:
                    b_content = fp.read()
                    private_key = to_bytes(b_content,
                                           errors="surrogate_or_strict")

            if proxy_command:
                ssh_connect_kwargs["proxycommand"] = proxy_command

            self.ssh.set_missing_host_key_policy(
                MyAddPolicy(self._new_stdin, self))

            self.ssh.connect(
                host=remote_addr.lower(),
                user=remote_user,
                look_for_keys=self.get_option("look_for_keys"),
                host_key_checking=self.get_option("host_key_checking"),
                password=self.get_option("password"),
                private_key=private_key,
                timeout=self._play_context.timeout,
                port=port,
                **ssh_connect_kwargs)
        except LibsshSessionException as e:
            msg = "ssh connection failed: " + to_text(e)
            raise AnsibleConnectionFailure(msg)
        except Exception as e:
            raise AnsibleConnectionFailure(to_text(e))

        display.vvv("ssh connection is OK: " + str(self.ssh))
        return self.ssh

    def exec_command(self, cmd, in_data=None, sudoable=True):
        """run a command on the remote host"""

        super(Connection, self).exec_command(cmd,
                                             in_data=in_data,
                                             sudoable=sudoable)

        if in_data:
            raise AnsibleError(
                "Internal Error: this module does not support optimized module pipelining"
            )

        bufsize = 4096

        try:
            self.chan = self.ssh.new_channel()
        except Exception as e:
            text_e = to_text(e)
            msg = "Failed to open session"
            if text_e:
                msg += ": %s" % text_e
            raise AnsibleConnectionFailure(to_native(msg))

        # sudo usually requires a PTY (cf. requiretty option), therefore
        # we give it one by default (pty=True in ansible.cfg), and we try
        # to initialise from the calling environment when sudoable is enabled
        if self.get_option("pty") and sudoable:
            self.chan.request_shell()

        display.vvv("EXEC %s" % cmd, host=self._play_context.remote_addr)

        cmd = to_bytes(cmd, errors="surrogate_or_strict")

        result = None
        no_prompt_out = b""
        no_prompt_err = b""
        become_output = b""
        out = b""
        err = b""

        try:
            if self.become and self.become.expect_prompt():
                passprompt = False
                become_sucess = False
                self.chan.sendall(cmd)

                while not (become_sucess or passprompt):
                    display.debug("Waiting for Privilege Escalation input")
                    self.chan.poll(timeout=self._play_context.timeout)
                    chunk = self.chan.recv(bufsize)
                    display.debug("chunk is: %s" % chunk)

                    if not chunk:
                        if b"unknown user" in become_output:
                            n_become_user = to_native(
                                self.become.get_option(
                                    "become_user",
                                    playcontext=self._play_context,
                                ))
                            raise AnsibleError("user %s does not exist" %
                                               n_become_user)
                        else:
                            break
                            # raise AnsibleError('ssh connection closed waiting for password prompt')
                    become_output += chunk
                    # need to check every line because we might get lectured
                    # and we might get the middle of a line in a chunk
                    for line in become_output.splitlines(True):
                        if self.become.check_success(line):
                            become_sucess = True
                            break
                        if self.become.check_password_prompt(line):
                            passprompt = True
                            break
                if passprompt:
                    if self.become:
                        become_pass = self.become.get_option(
                            "become_pass", playcontext=self._play_context)
                        self.chan.sendall(
                            to_bytes(become_pass, errors="surrogate_or_strict")
                            + b"\n")
                    else:
                        raise AnsibleError(
                            "A password is required but none was supplied")
                else:
                    no_prompt_out += become_output
                    no_prompt_err += become_output
            else:
                result = self.chan.exec_command(
                    to_text(cmd, errors="surrogate_or_strict"))
        except socket.timeout:
            raise AnsibleError(
                "ssh timed out waiting for privilege escalation.\n" +
                become_output)

        if result:
            rc = result.returncode
            out = result.stdout
            err = result.stderr
        else:
            rc = self.chan.get_channel_exit_status()
        return rc, out, err

    def put_file(self, in_path, out_path, proto="sftp"):
        """transfer a file from local to remote"""

        super(Connection, self).put_file(in_path, out_path)

        display.vvv(
            "PUT %s TO %s" % (in_path, out_path),
            host=self._play_context.remote_addr,
        )

        if not os.path.exists(to_bytes(in_path, errors="surrogate_or_strict")):
            raise AnsibleFileNotFound("file or module does not exist: %s" %
                                      in_path)

        if proto == "sftp":
            try:
                self.sftp = self.ssh.sftp()
            except Exception as e:
                raise AnsibleError("failed to open a SFTP connection (%s)" % e)

            try:
                self.sftp.put(
                    to_bytes(in_path, errors="surrogate_or_strict"),
                    to_bytes(out_path, errors="surrogate_or_strict"),
                )
            except IOError:
                raise AnsibleError("failed to transfer file to %s" % out_path)
        elif proto == "scp":
            scp = self.ssh.scp()
            try:
                scp.put(in_path, out_path)
            except LibsshSCPException as exc:
                raise AnsibleError("Error transferring file to %s: %s" %
                                   (out_path, to_text(exc)))
        else:
            raise AnsibleError(
                "Don't know how to transfer file over protocol %s" % proto)

    def _connect_sftp(self):
        cache_key = "%s__%s__" % (
            self._play_context.remote_addr,
            self._play_context.remote_user,
        )
        if cache_key in SFTP_CONNECTION_CACHE:
            return SFTP_CONNECTION_CACHE[cache_key]
        else:
            result = SFTP_CONNECTION_CACHE[cache_key] = self._connect(
            ).ssh.sftp()
            return result

    def fetch_file(self, in_path, out_path, proto="sftp"):
        """save a remote file to the specified path"""

        super(Connection, self).fetch_file(in_path, out_path)

        display.vvv(
            "FETCH %s TO %s" % (in_path, out_path),
            host=self._play_context.remote_addr,
        )

        if proto == "sftp":
            try:
                self.sftp = self._connect_sftp()
            except Exception as e:
                raise AnsibleError("failed to open a SFTP connection (%s)" %
                                   to_native(e))

            try:
                self.sftp.get(
                    to_bytes(in_path, errors="surrogate_or_strict"),
                    to_bytes(out_path, errors="surrogate_or_strict"),
                )
            except IOError:
                raise AnsibleError("failed to transfer file from %s" % in_path)
        elif proto == "scp":
            scp = self.ssh.scp()
            try:
                scp.get(out_path, in_path)
            except LibsshSCPException as exc:
                raise AnsibleError("Error transferring file from %s: %s" %
                                   (out_path, to_text(exc)))
        else:
            raise AnsibleError(
                "Don't know how to transfer file over protocol %s" % proto)

    def reset(self):
        self.close()
        self._connect()

    def close(self):
        """terminate the connection"""

        cache_key = self._cache_key()
        SSH_CONNECTION_CACHE.pop(cache_key, None)
        SFTP_CONNECTION_CACHE.pop(cache_key, None)

        if hasattr(self, "sftp"):
            if self.sftp is not None:
                self.sftp.close()

        if hasattr(self, "chan"):
            if self.chan is not None:
                self.chan.close()

        self.ssh.close()
        self._connected = False