Remote password handling
diff --git a/cfg_checker/cfg_check.py b/cfg_checker/cfg_check.py
index 34a3102..b46ca60 100644
--- a/cfg_checker/cfg_check.py
+++ b/cfg_checker/cfg_check.py
@@ -8,7 +8,7 @@
import reporter
-from cfg_checker.common.exception import ConfigException
+from cfg_checker.common.exception import CheckerException, ConfigException
from cfg_checker.common import utils, const
from cfg_checker.common import config, logger, logger_cli, pkg_dir
from cfg_checker.clients import salt
@@ -220,6 +220,12 @@
'--file',
help="HTML filename to save report"
)
+ parser.add_argument(
+ '-s',
+ '--sudo',
+ action='store_true', default=True,
+ help="Use sudo for getting salt creds"
+ )
subparsers = parser.add_subparsers(dest='command')
# packages
pkg_parser = subparsers.add_parser(
@@ -290,6 +296,9 @@
logger_cli.info("\nPlease, check arguments")
return
+ # Pass externally configured values
+ config.ssh_uses_sudo = args.sudo
+
# Handle options
if args.debug:
logger_cli.setLevel(DEBUG)
@@ -323,7 +332,7 @@
if __name__ == '__main__':
try:
config_check_entrypoint()
- except ConfigException as e:
+ except CheckerException as e:
logger_cli.error("\nERROR: {}".format(
e.message
))
diff --git a/cfg_checker/common/exception.py b/cfg_checker/common/exception.py
index bc7cefe..157c466 100644
--- a/cfg_checker/common/exception.py
+++ b/cfg_checker/common/exception.py
@@ -19,3 +19,15 @@
def __init__(self, message, *args, **kwargs):
super(ConfigException, self).__init__(message, *args, **kwargs)
self.message = "Configuration error: {}".format(message)
+
+
+class SaltException(CheckerException):
+ def __init__(self, message, *args, **kwargs):
+ super(SaltException, self).__init__(message, *args, **kwargs)
+ self.message = "Salt error: {}".format(message)
+
+
+class InvalidReturnException(CheckerException):
+ def __init__(self, message, *args, **kwargs):
+ super(InvalidReturnException, self).__init__(message, *args, **kwargs)
+ self.message = "Unexpected return value: {}".format(message)
diff --git a/cfg_checker/common/other.py b/cfg_checker/common/other.py
index 97030bb..1ff5adf 100644
--- a/cfg_checker/common/other.py
+++ b/cfg_checker/common/other.py
@@ -1,5 +1,6 @@
import os
import re
+import subprocess
from cfg_checker.common.const import all_roles_map
@@ -11,6 +12,15 @@
pkg_dir = os.path.abspath(pkg_dir)
+def shell(command):
+ _ps = subprocess.Popen(
+ command.split(),
+ stdout=subprocess.PIPE
+ ).communicate()[0].decode()
+
+ return _ps
+
+
class Utils(object):
@staticmethod
def validate_name(fqdn, message=False):
diff --git a/cfg_checker/common/salt_utils.py b/cfg_checker/common/salt_utils.py
index ba1233b..1a9d1da 100644
--- a/cfg_checker/common/salt_utils.py
+++ b/cfg_checker/common/salt_utils.py
@@ -1,11 +1,66 @@
"""
Module to handle interaction with salt
"""
+import json
import os
import requests
import time
-from cfg_checker.common import logger, config
+from cfg_checker.common import logger, logger_cli, config
+from cfg_checker.common.other import shell
+from cfg_checker.common.exception import SaltException, InvalidReturnException
+
+
+def _extract_password(_raw):
+ if not isinstance(_raw, unicode):
+ raise InvalidReturnException(_raw)
+ else:
+ try:
+ _json = json.loads(_raw)
+ except ValueError as e:
+ raise SaltException(
+ "Return value is not a json: '{}'".format(_raw)
+ )
+
+ return _json["local"]
+
+
+def get_remote_env_password():
+ """Uses ssh call with configured options to get password from salt master
+
+ :return: password string
+ """
+ _salt_cmd = "salt-call --out=json pillar.get _param:salt_api_password"
+ _ssh_cmd = ["ssh"]
+ # Build SSH cmd
+ if config.ssh_key:
+ _ssh_cmd.append("-i " + config.ssh_key)
+ if config.ssh_user:
+ _ssh_cmd.append(config.ssh_user+'@'+config.ssh_host)
+ else:
+ _ssh_cmd.append(config.ssh_host)
+ if config.ssh_uses_sudo:
+ _ssh_cmd.append("sudo")
+
+ _ssh_cmd.append(_salt_cmd)
+ _ssh_cmd = " ".join(_ssh_cmd)
+ logger_cli.debug("### Calling salt: '{}'".format(_ssh_cmd))
+ _result = shell(_ssh_cmd)
+ if len(_result) < 1:
+ raise InvalidReturnException("Empty value returned for '{}".format(
+ _ssh_cmd
+ ))
+ else:
+ return _extract_password(_result)
+
+def get_local_password():
+ """Calls salt locally to get password from the pillar
+
+ :return: password string
+ """
+ _cmd = "salt-call --out=json pillar.get _param:salt_api_password"
+ _result = shell(_cmd)
+ return _extract_password(_result)
def list_to_target_string(node_list, separator):
@@ -49,7 +104,7 @@
data = {}
_path = os.path.join(self.uri, path)
if path == 'login':
- _data = str(data).replace(config.salt_pass, "*****")
+ _data = str(data).replace(self._pass, "*****")
else:
_data = data
logger.debug("POST '{}'\nHeaders: '{}'\nCookies: {}\nBody: {}".format(
@@ -66,12 +121,17 @@
)
def _login(self):
+ # if there is no password - try to get local, if this available
+ if config.salt_env == "local":
+ _pass = get_local_password()
+ else:
+ _pass = get_remote_env_password()
login_payload = {
'username': config.salt_user,
- 'password': config.salt_pass,
+ 'password': _pass,
'eauth': 'pam'
}
-
+ self._pass = _pass
logger.debug("Logging in to salt master...")
_response = self.post(login_payload, path='login')
diff --git a/cfg_checker/common/settings.py b/cfg_checker/common/settings.py
index cea1f0c..1ee36b4 100644
--- a/cfg_checker/common/settings.py
+++ b/cfg_checker/common/settings.py
@@ -1,7 +1,7 @@
import os
from exception import ConfigException
-from log import logger
+from log import logger, logger_cli
from other import utils
pkg_dir = os.path.dirname(__file__)
@@ -25,10 +25,14 @@
self.date_format = "%Y-%m-%d %H:%M:%S.%f%z"
self.default_tz = "UTC"
+ self.ssh_uses_sudo = False
+ self.ssh_key = os.environ.get('SSH_KEY', None)
+ self.ssh_user = os.environ.get('SSH_USER', None)
+ self.ssh_host = os.environ.get('SSH_HOST', None)
+
self.salt_host = os.environ.get('SALT_URL', None)
self.salt_port = os.environ.get('SALT_PORT', '6969')
self.salt_user = os.environ.get('SALT_USER', 'salt')
- self.salt_pass = os.environ.get('SALT_PASSWORD', None)
self.salt_timeout = os.environ.get('SALT_TIMEOUT', 30)
self.salt_file_root = os.environ.get('SALT_FILE_ROOT', None)
self.salt_scripts_folder = os.environ.get(
@@ -44,9 +48,9 @@
None
))
- @staticmethod
- def _init_env(env_name=None):
- """[summary]
+ def _init_env(self, env_name=None):
+ """Inits the environment vars from the env file
+ Uses simple validation for the values and names
Keyword Arguments:
env_name {str} -- environment name to search configuration
@@ -58,14 +62,14 @@
"""
# load env file as init os.environment with its values
if env_name is None:
- _env_name = 'default'
+ _env_name = 'local'
else:
_env_name = env_name
_config_path = os.path.join(pkg_dir, 'etc', _env_name + '.env')
if os.path.isfile(_config_path):
with open(_config_path) as _f:
_list = _f.read().splitlines()
- logger.debug("Loading env vars from '{}'".format(_config_path))
+ logger_cli.info("# Loading env vars from '{}'".format(_config_path))
else:
raise ConfigException(
"Failed to load enviroment vars from '{}'".format(
@@ -94,7 +98,8 @@
)
)
else:
- logger.debug("Loaded total of '{}' vars".format(len(_list)))
+ logger_cli.debug("-> ...loaded total of '{}' vars".format(len(_list)))
+ self.salt_env = _env_name
def __init__(self):
"""Base configuration class. Only values that are common for all scripts