Source code for _proxy.ssh

# -*- coding: utf-8 -*-
SSH Proxy

Manage a remote host via SSH, using a Proxy Minion. This module doesn't have any
external dependencies, as it makes use of the native Salt internals used for
salt-ssh, therefore managing the remote machine by uploading a lightweight Salt
version on the target host, then invokes Salt functions over SSH (using the
``ssh`` binary installed on your computer or wherever this Proxy Minion runs).

.. note::

    To manage machines running Windows, you will need to install the
    ``saltwinshell`` library.


The configuration is aligned to the general Proxy Minion standards: put the
connection details and credentials under the ``proxy`` key in the Proxy config
or Pillar.

.. important:

    Local (i.e., per Proxy) option override the global configuration or CLI

    The IP address or the hostname of the remove machine to manage.

    Integer, the port number to use when establishing he connection
    (defaults to 22).

    The username required for authentication.

    The password used for authentication.

    Absolute path to the private SSH key used for authentication.

    The SSH private key password.

``timeout``: 30
    The SSH timeout. Defaults to 30 seconds.

``sudo``: ``False``
    Execute commands as sudo.

``tty``: ``False``
    Connect over tty.

    The username that should execute the commands as sudo.

    Enable remote port forwarding. Example: ````.
    Multiple remote port forwardings are supported, using comma-separated
    values, e.g., ``,``.

``identities_only``: ``False``
    Execute SSH with ``-o IdentitiesOnly=yes``. This option is intended for
    situations where ssh-agent offers many different identities and allow ssh
    to ignore those identities and use the only one specified in options.

``ignore_host_keys``: ``False``
    By default ssh host keys are honored and connections will ask for approval.
    Use this option to disable ``StrictHostKeyChecking``.

``no_host_keys``: ``False``
    Fully ignores ssh host keys which by default are honored and connections
    would ask for approval. Useful if the host key of a remote server has
    changed and would still error with ``ignore_host_keys``.

``winrm``: ``False``
    Flag that tells Salt to connect to a Windows machine. This option requires
    the ``saltwinshell`` to be installed.

Example Pillar:

.. code-block:: yaml

    proxytype: ssh
    user: test
    passwd: test
    port: 2022
from __future__ import absolute_import, print_function, unicode_literals

import json
import logging

import six
import salt.client.ssh
import salt.fileclient
import salt.exceptions
import salt.utils.path

__proxyenabled__ = ["ssh"]

log = logging.getLogger(__name__)

CONN = None

def _prep_conn(opts, fun, *args, **kwargs):
    Prepare the connection.
    opts["_ssh_version"] = salt.client.ssh.ssh_version()
    fsclient = salt.fileclient.FSClient(opts)
    # TODO: Have here more options to simplify the usage, through features like
    # auto-expand the path to the priv key, auto-discovery, etc.
    argv = [fun]
    argv.extend([salt.utils.json.dumps(arg) for arg in args])
                salt.utils.stringutils.to_str(key), salt.utils.json.dumps(val)
            for key, val in six.iteritems(kwargs)
    if not opts["proxy"].get("ssh_options"):
        opts["proxy"]["ssh_options"] = []
    if opts["proxy"].get("ignore_host_keys", False):
    if opts["proxy"].get("no_host_keys", False):
            ["StrictHostKeyChecking=no", "UserKnownHostsFile=/dev/null"]
    for cli_opt in ("identities_only", "priv", "priv_passwd"):
        if opts.get(cli_opt) and not opts["proxy"].get(cli_opt):
            opts["proxy"][cli_opt] = opts[cli_opt]
    ext_mods = salt.client.ssh.mod_data(fsclient)
    conn = salt.client.ssh.Single(
        opts, argv, opts["id"], fsclient=fsclient, mods=ext_mods, **opts["proxy"]
    conn.args = args
    conn.kwargs = kwargs
    thin_dir = conn.opts["thin_dir"]
    thin_dir = thin_dir.replace("proxy", "")
    conn.opts["thin_dir"] = thin_dir
    conn.thin_dir = thin_dir
    return conn

[docs]def init(opts): """ Init the SSH connection, and execute a simple call to ensure that the remote device is reachable, otherwise throw an error. """ global CONN, INITIALIZED if not salt.utils.path.which("ssh"): raise salt.exceptions.SaltSystemExit( code=-1, msg="No ssh binary found in path -- ssh must be installed for this Proxy module. Exiting.", ) CONN = _prep_conn(opts, "", "echo") INITIALIZED = True
[docs]def initialized(): """ Proxy initialized properly? """ return INITIALIZED
[docs]def module_executors(): """ Return the list of executors that should invoke the Salt functions. """ return ["ssh"]
[docs]def call(fun, *args, **kwargs): """ Call an arbitrary Salt function and return the output. """ global CONN, INITIALIZED if not CONN or not INITIALIZED: return opts = CONN.opts opts["output"] = "json" ssh_conn = _prep_conn(opts, fun, *args, **kwargs) ret = if ret[2] != 0: log.error("[%s] %s", opts["id"], ret[1]) return ret[0] thin_ret = json.loads(ret[0]) if "_error" in thin_ret["local"]: log.error(thin_ret["local"]["_error"]) if "stdout" in thin_ret["local"]: log.error(thin_ret["local"]["stdout"]) return thin_ret["local"]["return"]
[docs]def ping(): """ Execute "echo" on the remote host to ensure it's still accessible. """ global CONN, INITIALIZED if not CONN or not INITIALIZED: log.debug("Not connected, or not initialized") return False ret = log.debug(ret) return ret[2] == 0
[docs]def grains(): """ Invoke grains.items from the thin Salt on the remote machine, in order to return here the Grains. """ global GRAINS_CACHE if not GRAINS_CACHE: GRAINS_CACHE = call("grains.items") return GRAINS_CACHE
[docs]def shutdown(opts): """ Buh-bye... """ global CONN, INITIALIZED if CONN and INITIALIZED: del CONN INITIALIZED = False