mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-07 13:52:54 +00:00
preliminary privlege escalation unification + pbrun
- become constants inherit existing sudo/su ones - become command line options, marked sudo/su as deprecated and moved sudo/su passwords to runas group - changed method signatures as privlege escalation is collapsed to become - added tests for su and become, diabled su for lack of support in local.py - updated playbook,play and task objects to become - added become to runner - added whoami test for become/sudo/su - added home override dir for plugins - removed useless method from ask pass - forced become pass to always be string also uses to_bytes - fixed fakerunner for tests - corrected reference in synchronize action plugin - added pfexec (needs testing) - removed unused sudo/su in runner init - removed deprecated info - updated pe tests to allow to run under sudo and not need root - normalized become options into a funciton to avoid duplication and inconsistencies - pushed suppored list to connection classs property - updated all connection plugins to latest 'become' pe - includes fixes from feedback (including typos) - added draft docs - stub of become_exe, leaving for future v2 fixes
This commit is contained in:
@@ -86,9 +86,6 @@ def shell_expand_path(path):
|
||||
path = os.path.expanduser(os.path.expandvars(path))
|
||||
return path
|
||||
|
||||
def get_plugin_paths(path):
|
||||
return ':'.join([os.path.join(x, path) for x in [os.path.expanduser('~/.ansible/plugins/'), '/usr/share/ansible_plugins/']])
|
||||
|
||||
p = load_config_file()
|
||||
|
||||
active_user = pwd.getpwuid(os.geteuid())[0]
|
||||
@@ -137,16 +134,28 @@ DEFAULT_SU_FLAGS = get_config(p, DEFAULTS, 'su_flags', 'ANSIBLE_SU_FLAG
|
||||
DEFAULT_SU_USER = get_config(p, DEFAULTS, 'su_user', 'ANSIBLE_SU_USER', 'root')
|
||||
DEFAULT_ASK_SU_PASS = get_config(p, DEFAULTS, 'ask_su_pass', 'ANSIBLE_ASK_SU_PASS', False, boolean=True)
|
||||
DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower()
|
||||
|
||||
DEFAULT_ACTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'action_plugins', 'ANSIBLE_ACTION_PLUGINS', get_plugin_paths('action_plugins'))
|
||||
DEFAULT_CACHE_PLUGIN_PATH = get_config(p, DEFAULTS, 'cache_plugins', 'ANSIBLE_CACHE_PLUGINS', get_plugin_paths('cache_plugins'))
|
||||
DEFAULT_CALLBACK_PLUGIN_PATH = get_config(p, DEFAULTS, 'callback_plugins', 'ANSIBLE_CALLBACK_PLUGINS', get_plugin_paths('callback_plugins'))
|
||||
DEFAULT_CONNECTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'connection_plugins', 'ANSIBLE_CONNECTION_PLUGINS', get_plugin_paths('connection_plugins'))
|
||||
DEFAULT_LOOKUP_PLUGIN_PATH = get_config(p, DEFAULTS, 'lookup_plugins', 'ANSIBLE_LOOKUP_PLUGINS', get_plugin_paths('lookup_plugins'))
|
||||
DEFAULT_VARS_PLUGIN_PATH = get_config(p, DEFAULTS, 'vars_plugins', 'ANSIBLE_VARS_PLUGINS', get_plugin_paths('vars_plugins'))
|
||||
DEFAULT_FILTER_PLUGIN_PATH = get_config(p, DEFAULTS, 'filter_plugins', 'ANSIBLE_FILTER_PLUGINS', get_plugin_paths('filter_plugins'))
|
||||
DEFAULT_LOG_PATH = shell_expand_path(get_config(p, DEFAULTS, 'log_path', 'ANSIBLE_LOG_PATH', ''))
|
||||
|
||||
#TODO: get rid of ternary chain mess
|
||||
BECOME_METHODS = ['sudo','su','pbrun','runas','pfexec']
|
||||
DEFAULT_BECOME = get_config(p, 'privilege_escalation', 'become', 'ANSIBLE_BECOME',True if DEFAULT_SUDO or DEFAULT_SU else False, boolean=True)
|
||||
DEFAULT_BECOME_METHOD = get_config(p, 'privilege_escalation', 'become_method', 'ANSIBLE_BECOME_METHOD','sudo' if DEFAULT_SUDO else 'su' if DEFAULT_SU else 'sudo' ).lower()
|
||||
DEFAULT_BECOME_USER = get_config(p, 'privilege_escalation', 'become_user', 'ANSIBLE_BECOME_USER',DEFAULT_SUDO_USER if DEFAULT_SUDO else DEFAULT_SU_USER if DEFAULT_SU else 'root')
|
||||
DEFAULT_BECOME_ASK_PASS = get_config(p, 'privilege_escalation', 'become_ask_pass', 'ANSIBLE_BECOME_ASK_PASS',True if DEFAULT_ASK_SUDO_PASS else False, boolean=True)
|
||||
# need to rethink impementing these 2
|
||||
DEFAULT_BECOME_EXE = None
|
||||
#DEFAULT_BECOME_EXE = get_config(p, DEFAULTS, 'become_exe', 'ANSIBLE_BECOME_EXE','sudo' if DEFAULT_SUDO else 'su' if DEFAULT_SU else 'sudo')
|
||||
#DEFAULT_BECOME_FLAGS = get_config(p, DEFAULTS, 'become_flags', 'ANSIBLE_BECOME_FLAGS',DEFAULT_SUDO_FLAGS if DEFAULT_SUDO else DEFAULT_SU_FLAGS if DEFAULT_SU else '-H')
|
||||
|
||||
|
||||
DEFAULT_ACTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'action_plugins', 'ANSIBLE_ACTION_PLUGINS', '~/.ansible/plugins/action_plugins:/usr/share/ansible_plugins/action_plugins')
|
||||
DEFAULT_CACHE_PLUGIN_PATH = get_config(p, DEFAULTS, 'cache_plugins', 'ANSIBLE_CACHE_PLUGINS', '~/.ansible/plugins/cache_plugins:/usr/share/ansible_plugins/cache_plugins')
|
||||
DEFAULT_CALLBACK_PLUGIN_PATH = get_config(p, DEFAULTS, 'callback_plugins', 'ANSIBLE_CALLBACK_PLUGINS', '~/.ansible/plugins/callback_plugins:/usr/share/ansible_plugins/callback_plugins')
|
||||
DEFAULT_CONNECTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'connection_plugins', 'ANSIBLE_CONNECTION_PLUGINS', '~/.ansible/plugins/connection_plugins:/usr/share/ansible_plugins/connection_plugins')
|
||||
DEFAULT_LOOKUP_PLUGIN_PATH = get_config(p, DEFAULTS, 'lookup_plugins', 'ANSIBLE_LOOKUP_PLUGINS', '~/.ansible/plugins/lookup_plugins:/usr/share/ansible_plugins/lookup_plugins')
|
||||
DEFAULT_VARS_PLUGIN_PATH = get_config(p, DEFAULTS, 'vars_plugins', 'ANSIBLE_VARS_PLUGINS', '~/.ansible/plugins/vars_plugins:/usr/share/ansible_plugins/vars_plugins')
|
||||
DEFAULT_FILTER_PLUGIN_PATH = get_config(p, DEFAULTS, 'filter_plugins', 'ANSIBLE_FILTER_PLUGINS', '~/.ansible/plugins/filter_plugins:/usr/share/ansible_plugins/filter_plugins')
|
||||
|
||||
CACHE_PLUGIN = get_config(p, DEFAULTS, 'fact_caching', 'ANSIBLE_CACHE_PLUGIN', 'memory')
|
||||
CACHE_PLUGIN_CONNECTION = get_config(p, DEFAULTS, 'fact_caching_connection', 'ANSIBLE_CACHE_PLUGIN_CONNECTION', None)
|
||||
CACHE_PLUGIN_PREFIX = get_config(p, DEFAULTS, 'fact_caching_prefix', 'ANSIBLE_CACHE_PLUGIN_PREFIX', 'ansible_facts')
|
||||
@@ -172,7 +181,7 @@ ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'AN
|
||||
ANSIBLE_SSH_CONTROL_PATH = get_config(p, 'ssh_connection', 'control_path', 'ANSIBLE_SSH_CONTROL_PATH', "%(directory)s/ansible-ssh-%%h-%%p-%%r")
|
||||
ANSIBLE_SSH_PIPELINING = get_config(p, 'ssh_connection', 'pipelining', 'ANSIBLE_SSH_PIPELINING', False, boolean=True)
|
||||
PARAMIKO_RECORD_HOST_KEYS = get_config(p, 'paramiko_connection', 'record_host_keys', 'ANSIBLE_PARAMIKO_RECORD_HOST_KEYS', True, boolean=True)
|
||||
# obsolete -- will be formally removed in 1.6
|
||||
# obsolete -- will be formally removed
|
||||
ZEROMQ_PORT = get_config(p, 'fireball_connection', 'zeromq_port', 'ANSIBLE_ZEROMQ_PORT', 5099, integer=True)
|
||||
ACCELERATE_PORT = get_config(p, 'accelerate', 'accelerate_port', 'ACCELERATE_PORT', 5099, integer=True)
|
||||
ACCELERATE_TIMEOUT = get_config(p, 'accelerate', 'accelerate_timeout', 'ACCELERATE_TIMEOUT', 30, integer=True)
|
||||
@@ -188,6 +197,7 @@ PARAMIKO_PTY = get_config(p, 'paramiko_connection', 'pty', 'AN
|
||||
DEFAULT_PASSWORD_CHARS = ascii_letters + digits + ".,:-_"
|
||||
|
||||
# non-configurable things
|
||||
DEFAULT_BECOME_PASS = None
|
||||
DEFAULT_SUDO_PASS = None
|
||||
DEFAULT_REMOTE_PASS = None
|
||||
DEFAULT_SUBSET = None
|
||||
|
||||
@@ -60,15 +60,12 @@ class PlayBook(object):
|
||||
timeout = C.DEFAULT_TIMEOUT,
|
||||
remote_user = C.DEFAULT_REMOTE_USER,
|
||||
remote_pass = C.DEFAULT_REMOTE_PASS,
|
||||
sudo_pass = C.DEFAULT_SUDO_PASS,
|
||||
remote_port = None,
|
||||
transport = C.DEFAULT_TRANSPORT,
|
||||
private_key_file = C.DEFAULT_PRIVATE_KEY_FILE,
|
||||
callbacks = None,
|
||||
runner_callbacks = None,
|
||||
stats = None,
|
||||
sudo = False,
|
||||
sudo_user = C.DEFAULT_SUDO_USER,
|
||||
extra_vars = None,
|
||||
only_tags = None,
|
||||
skip_tags = None,
|
||||
@@ -77,11 +74,13 @@ class PlayBook(object):
|
||||
check = False,
|
||||
diff = False,
|
||||
any_errors_fatal = False,
|
||||
su = False,
|
||||
su_user = False,
|
||||
su_pass = False,
|
||||
vault_password = False,
|
||||
force_handlers = False,
|
||||
# privelege escalation
|
||||
become = C.DEFAULT_BECOME,
|
||||
become_method = C.DEFAULT_BECOME_METHOD,
|
||||
become_user = C.DEFAULT_BECOME_USER,
|
||||
become_pass = None,
|
||||
):
|
||||
|
||||
"""
|
||||
@@ -92,13 +91,11 @@ class PlayBook(object):
|
||||
timeout: connection timeout
|
||||
remote_user: run as this user if not specified in a particular play
|
||||
remote_pass: use this remote password (for all plays) vs using SSH keys
|
||||
sudo_pass: if sudo==True, and a password is required, this is the sudo password
|
||||
remote_port: default remote port to use if not specified with the host or play
|
||||
transport: how to connect to hosts that don't specify a transport (local, paramiko, etc)
|
||||
callbacks output callbacks for the playbook
|
||||
runner_callbacks: more callbacks, this time for the runner API
|
||||
stats: holds aggregrate data about events occurring to each host
|
||||
sudo: if not specified per play, requests all plays use sudo mode
|
||||
inventory: can be specified instead of host_list to use a pre-existing inventory object
|
||||
check: don't change anything, just try to detect some potential changes
|
||||
any_errors_fatal: terminate the entire execution immediately when one of the hosts has failed
|
||||
@@ -139,21 +136,20 @@ class PlayBook(object):
|
||||
self.callbacks = callbacks
|
||||
self.runner_callbacks = runner_callbacks
|
||||
self.stats = stats
|
||||
self.sudo = sudo
|
||||
self.sudo_pass = sudo_pass
|
||||
self.sudo_user = sudo_user
|
||||
self.extra_vars = extra_vars
|
||||
self.global_vars = {}
|
||||
self.private_key_file = private_key_file
|
||||
self.only_tags = only_tags
|
||||
self.skip_tags = skip_tags
|
||||
self.any_errors_fatal = any_errors_fatal
|
||||
self.su = su
|
||||
self.su_user = su_user
|
||||
self.su_pass = su_pass
|
||||
self.vault_password = vault_password
|
||||
self.force_handlers = force_handlers
|
||||
|
||||
self.become = become
|
||||
self.become_method = become_method
|
||||
self.become_user = become_user
|
||||
self.become_pass = become_pass
|
||||
|
||||
self.callbacks.playbook = self
|
||||
self.runner_callbacks.playbook = self
|
||||
|
||||
@@ -416,10 +412,7 @@ class PlayBook(object):
|
||||
basedir=task.play.basedir,
|
||||
conditional=task.when,
|
||||
callbacks=self.runner_callbacks,
|
||||
sudo=task.sudo,
|
||||
sudo_user=task.sudo_user,
|
||||
transport=task.transport,
|
||||
sudo_pass=task.sudo_pass,
|
||||
is_playbook=True,
|
||||
check=self.check,
|
||||
diff=self.diff,
|
||||
@@ -429,13 +422,14 @@ class PlayBook(object):
|
||||
accelerate_port=task.play.accelerate_port,
|
||||
accelerate_ipv6=task.play.accelerate_ipv6,
|
||||
error_on_undefined_vars=C.DEFAULT_UNDEFINED_VAR_BEHAVIOR,
|
||||
su=task.su,
|
||||
su_user=task.su_user,
|
||||
su_pass=task.su_pass,
|
||||
vault_pass = self.vault_password,
|
||||
run_hosts=hosts,
|
||||
no_log=task.no_log,
|
||||
run_once=task.run_once,
|
||||
become=task.become,
|
||||
become_method=task.become_method,
|
||||
become_user=task.become_user,
|
||||
become_pass=task.become_pass,
|
||||
)
|
||||
|
||||
runner.module_vars.update({'play_hosts': hosts})
|
||||
@@ -616,12 +610,10 @@ class PlayBook(object):
|
||||
setup_cache=self.SETUP_CACHE,
|
||||
vars_cache=self.VARS_CACHE,
|
||||
callbacks=self.runner_callbacks,
|
||||
sudo=play.sudo,
|
||||
sudo_user=play.sudo_user,
|
||||
sudo_pass=self.sudo_pass,
|
||||
su=play.su,
|
||||
su_user=play.su_user,
|
||||
su_pass=self.su_pass,
|
||||
become=play.become,
|
||||
become_method=play.become_method,
|
||||
become_user=play.become_user,
|
||||
become_pass=self.become_pass,
|
||||
vault_pass=self.vault_password,
|
||||
transport=play.transport,
|
||||
is_playbook=True,
|
||||
|
||||
@@ -32,24 +32,25 @@ import uuid
|
||||
|
||||
class Play(object):
|
||||
|
||||
__slots__ = [
|
||||
'hosts', 'name', 'vars', 'vars_file_vars', 'role_vars', 'default_vars', 'vars_prompt', 'vars_files',
|
||||
'handlers', 'remote_user', 'remote_port', 'included_roles', 'accelerate',
|
||||
'accelerate_port', 'accelerate_ipv6', 'sudo', 'sudo_user', 'transport', 'playbook',
|
||||
'tags', 'gather_facts', 'serial', '_ds', '_handlers', '_tasks',
|
||||
'basedir', 'any_errors_fatal', 'roles', 'max_fail_pct', '_play_hosts', 'su', 'su_user',
|
||||
'vault_password', 'no_log', 'environment',
|
||||
_pb_common = [
|
||||
'accelerate', 'accelerate_ipv6', 'accelerate_port', 'any_errors_fatal', 'become',
|
||||
'become_method', 'become_user', 'environment', 'gather_facts', 'handlers', 'hosts',
|
||||
'name', 'no_log', 'remote_user', 'roles', 'serial', 'su', 'su_user', 'sudo',
|
||||
'sudo_user', 'tags', 'vars', 'vars_files', 'vars_prompt', 'vault_password',
|
||||
]
|
||||
|
||||
__slots__ = _pb_common + [
|
||||
'_ds', '_handlers', '_play_hosts', '_tasks', 'any_errors_fatal', 'basedir',
|
||||
'default_vars', 'included_roles', 'max_fail_pct', 'playbook', 'remote_port',
|
||||
'role_vars', 'transport', 'vars_file_vars',
|
||||
]
|
||||
|
||||
# to catch typos and so forth -- these are userland names
|
||||
# and don't line up 1:1 with how they are stored
|
||||
VALID_KEYS = frozenset((
|
||||
'hosts', 'name', 'vars', 'vars_prompt', 'vars_files',
|
||||
'tasks', 'handlers', 'remote_user', 'user', 'port', 'include', 'accelerate', 'accelerate_port', 'accelerate_ipv6',
|
||||
'sudo', 'sudo_user', 'connection', 'tags', 'gather_facts', 'serial',
|
||||
'any_errors_fatal', 'roles', 'role_names', 'pre_tasks', 'post_tasks', 'max_fail_percentage',
|
||||
'su', 'su_user', 'vault_password', 'no_log', 'environment',
|
||||
))
|
||||
VALID_KEYS = frozenset(_pb_common + [
|
||||
'connection', 'include', 'max_fail_percentage', 'port', 'post_tasks',
|
||||
'pre_tasks', 'role_names', 'tasks', 'user',
|
||||
])
|
||||
|
||||
# *************************************************
|
||||
|
||||
@@ -58,7 +59,7 @@ class Play(object):
|
||||
|
||||
for x in ds.keys():
|
||||
if not x in Play.VALID_KEYS:
|
||||
raise errors.AnsibleError("%s is not a legal parameter at this level in an Ansible Playbook" % x)
|
||||
raise errors.AnsibleError("%s is not a legal parameter of an Ansible Play" % x)
|
||||
|
||||
# allow all playbook keys to be set by --extra-vars
|
||||
self.vars = ds.get('vars', {})
|
||||
@@ -140,8 +141,6 @@ class Play(object):
|
||||
self._handlers = ds.get('handlers', [])
|
||||
self.remote_user = ds.get('remote_user', ds.get('user', self.playbook.remote_user))
|
||||
self.remote_port = ds.get('port', self.playbook.remote_port)
|
||||
self.sudo = ds.get('sudo', self.playbook.sudo)
|
||||
self.sudo_user = ds.get('sudo_user', self.playbook.sudo_user)
|
||||
self.transport = ds.get('connection', self.playbook.transport)
|
||||
self.remote_port = self.remote_port
|
||||
self.any_errors_fatal = utils.boolean(ds.get('any_errors_fatal', 'false'))
|
||||
@@ -149,22 +148,40 @@ class Play(object):
|
||||
self.accelerate_port = ds.get('accelerate_port', None)
|
||||
self.accelerate_ipv6 = ds.get('accelerate_ipv6', False)
|
||||
self.max_fail_pct = int(ds.get('max_fail_percentage', 100))
|
||||
self.su = ds.get('su', self.playbook.su)
|
||||
self.su_user = ds.get('su_user', self.playbook.su_user)
|
||||
self.no_log = utils.boolean(ds.get('no_log', 'false'))
|
||||
|
||||
# Fail out if user specifies conflicting privelege escalations
|
||||
if (ds.get('become') or ds.get('become_user')) and (ds.get('sudo') or ds.get('sudo_user')):
|
||||
raise errors.AnsibleError('sudo params ("become", "become_user") and su params ("sudo", "sudo_user") cannot be used together')
|
||||
if (ds.get('become') or ds.get('become_user')) and (ds.get('su') or ds.get('su_user')):
|
||||
raise errors.AnsibleError('sudo params ("become", "become_user") and su params ("su", "su_user") cannot be used together')
|
||||
if (ds.get('sudo') or ds.get('sudo_user')) and (ds.get('su') or ds.get('su_user')):
|
||||
raise errors.AnsibleError('sudo params ("sudo", "sudo_user") and su params ("su", "su_user") cannot be used together')
|
||||
|
||||
# become settings are inherited and updated normally
|
||||
self.become = ds.get('become', self.playbook.become)
|
||||
self.become_method = ds.get('become_method', self.playbook.become_method)
|
||||
self.become_user = ds.get('become_user', self.playbook.become_user)
|
||||
|
||||
# Make sure current play settings are reflected in become fields
|
||||
if 'sudo' in ds:
|
||||
self.become=ds['sudo']
|
||||
self.become_method='sudo'
|
||||
if 'sudo_user' in ds:
|
||||
self.become_user=ds['sudo_user']
|
||||
elif 'su' in ds:
|
||||
self.become=True
|
||||
self.become=ds['su']
|
||||
if 'su_user' in ds:
|
||||
self.become_user=ds['su_user']
|
||||
|
||||
# gather_facts is not a simple boolean, as None means that a 'smart'
|
||||
# fact gathering mode will be used, so we need to be careful here as
|
||||
# calling utils.boolean(None) returns False
|
||||
self.gather_facts = ds.get('gather_facts', None)
|
||||
if self.gather_facts:
|
||||
if self.gather_facts is not None:
|
||||
self.gather_facts = utils.boolean(self.gather_facts)
|
||||
|
||||
# Fail out if user specifies a sudo param with a su param in a given play
|
||||
if (ds.get('sudo') or ds.get('sudo_user')) and (ds.get('su') or ds.get('su_user')):
|
||||
raise errors.AnsibleError('sudo params ("sudo", "sudo_user") and su params '
|
||||
'("su", "su_user") cannot be used together')
|
||||
|
||||
load_vars['role_names'] = ds.get('role_names', [])
|
||||
|
||||
self._tasks = self._load_tasks(self._ds.get('tasks', []), load_vars)
|
||||
@@ -173,9 +190,6 @@ class Play(object):
|
||||
# apply any missing tags to role tasks
|
||||
self._late_merge_role_tags()
|
||||
|
||||
if self.sudo_user != 'root':
|
||||
self.sudo = True
|
||||
|
||||
# place holder for the discovered hosts to be used in this play
|
||||
self._play_hosts = None
|
||||
|
||||
@@ -429,7 +443,7 @@ class Play(object):
|
||||
|
||||
for (role, role_path, role_vars, role_params, default_vars) in roles:
|
||||
# special vars must be extracted from the dict to the included tasks
|
||||
special_keys = [ "sudo", "sudo_user", "when", "with_items" ]
|
||||
special_keys = [ "sudo", "sudo_user", "when", "with_items", "su", "su_user", "become", "become_user" ]
|
||||
special_vars = {}
|
||||
for k in special_keys:
|
||||
if k in role_vars:
|
||||
@@ -531,7 +545,7 @@ class Play(object):
|
||||
|
||||
# *************************************************
|
||||
|
||||
def _load_tasks(self, tasks, vars=None, role_params=None, default_vars=None, sudo_vars=None,
|
||||
def _load_tasks(self, tasks, vars=None, role_params=None, default_vars=None, become_vars=None,
|
||||
additional_conditions=None, original_file=None, role_name=None):
|
||||
''' handle task and handler include statements '''
|
||||
|
||||
@@ -547,8 +561,8 @@ class Play(object):
|
||||
role_params = {}
|
||||
if default_vars is None:
|
||||
default_vars = {}
|
||||
if sudo_vars is None:
|
||||
sudo_vars = {}
|
||||
if become_vars is None:
|
||||
become_vars = {}
|
||||
|
||||
old_conditions = list(additional_conditions)
|
||||
|
||||
@@ -560,14 +574,37 @@ class Play(object):
|
||||
if not isinstance(x, dict):
|
||||
raise errors.AnsibleError("expecting dict; got: %s, error in %s" % (x, original_file))
|
||||
|
||||
# evaluate sudo vars for current and child tasks
|
||||
included_sudo_vars = {}
|
||||
for k in ["sudo", "sudo_user"]:
|
||||
# evaluate privilege escalation vars for current and child tasks
|
||||
included_become_vars = {}
|
||||
for k in ["become", "become_user", "become_method", "become_exe"]:
|
||||
if k in x:
|
||||
included_sudo_vars[k] = x[k]
|
||||
elif k in sudo_vars:
|
||||
included_sudo_vars[k] = sudo_vars[k]
|
||||
x[k] = sudo_vars[k]
|
||||
included_become_vars[k] = x[k]
|
||||
elif k in become_vars:
|
||||
included_become_vars[k] = become_vars[k]
|
||||
x[k] = become_vars[k]
|
||||
|
||||
## backwards compat with old sudo/su directives
|
||||
if 'sudo' in x or 'sudo_user' in x:
|
||||
included_become_vars['become'] = x['sudo']
|
||||
x['become'] = x['sudo']
|
||||
x['become_method'] = 'sudo'
|
||||
del x['sudo']
|
||||
|
||||
if x.get('sudo_user', False):
|
||||
included_become_vars['become_user'] = x['sudo_user']
|
||||
x['become_user'] = x['sudo_user']
|
||||
del x['sudo_user']
|
||||
|
||||
elif 'su' in x or 'su_user' in x:
|
||||
included_become_vars['become'] = x['su']
|
||||
x['become'] = x['su']
|
||||
x['become_method'] = 'su'
|
||||
del x['su']
|
||||
|
||||
if x.get('su_user', False):
|
||||
included_become_vars['become_user'] = x['su_user']
|
||||
x['become_user'] = x['su_user']
|
||||
del x['su_user']
|
||||
|
||||
if 'meta' in x:
|
||||
if x['meta'] == 'flush_handlers':
|
||||
@@ -596,7 +633,7 @@ class Play(object):
|
||||
included_additional_conditions.append(x[k])
|
||||
elif type(x[k]) is list:
|
||||
included_additional_conditions.extend(x[k])
|
||||
elif k in ("include", "vars", "role_params", "default_vars", "sudo", "sudo_user", "role_name", "no_log"):
|
||||
elif k in ("include", "vars", "role_params", "default_vars", "sudo", "sudo_user", "role_name", "no_log", "become", "become_user", "su", "su_user"):
|
||||
continue
|
||||
else:
|
||||
include_vars[k] = x[k]
|
||||
@@ -643,7 +680,7 @@ class Play(object):
|
||||
for y in data:
|
||||
if isinstance(y, dict) and 'include' in y:
|
||||
y['role_name'] = new_role
|
||||
loaded = self._load_tasks(data, mv, role_params, default_vars, included_sudo_vars, list(included_additional_conditions), original_file=include_filename, role_name=new_role)
|
||||
loaded = self._load_tasks(data, mv, role_params, default_vars, included_become_vars, list(included_additional_conditions), original_file=include_filename, role_name=new_role)
|
||||
results += loaded
|
||||
elif type(x) == dict:
|
||||
task = Task(
|
||||
|
||||
@@ -24,26 +24,24 @@ import sys
|
||||
|
||||
class Task(object):
|
||||
|
||||
__slots__ = [
|
||||
'name', 'meta', 'action', 'when', 'async_seconds', 'async_poll_interval',
|
||||
'notify', 'module_name', 'module_args', 'module_vars', 'play_vars', 'play_file_vars', 'role_vars', 'role_params', 'default_vars',
|
||||
'play', 'notified_by', 'tags', 'register', 'role_name',
|
||||
'delegate_to', 'first_available_file', 'ignore_errors',
|
||||
'local_action', 'transport', 'sudo', 'remote_user', 'sudo_user', 'sudo_pass',
|
||||
'items_lookup_plugin', 'items_lookup_terms', 'environment', 'args',
|
||||
'any_errors_fatal', 'changed_when', 'failed_when', 'always_run', 'delay', 'retries', 'until',
|
||||
'su', 'su_user', 'su_pass', 'no_log', 'run_once',
|
||||
_t_common = [
|
||||
'action', 'always_run', 'any_errors_fatal', 'args', 'become', 'become_method', 'become_pass',
|
||||
'become_user', 'changed_when', 'delay', 'delegate_to', 'environment', 'failed_when',
|
||||
'first_available_file', 'ignore_errors', 'local_action', 'meta', 'name', 'no_log',
|
||||
'notify', 'register', 'remote_user', 'retries', 'run_once', 'su', 'su_pass', 'su_user',
|
||||
'sudo', 'sudo_pass', 'sudo_user', 'tags', 'transport', 'until', 'when',
|
||||
]
|
||||
|
||||
__slots__ = [
|
||||
'async_poll_interval', 'async_seconds', 'default_vars', 'first_available_file',
|
||||
'items_lookup_plugin', 'items_lookup_terms', 'module_args', 'module_name', 'module_vars',
|
||||
'notified_by', 'play', 'play_file_vars', 'play_vars', 'role_name', 'role_params', 'role_vars',
|
||||
] + _t_common
|
||||
|
||||
# to prevent typos and such
|
||||
VALID_KEYS = frozenset((
|
||||
'name', 'meta', 'action', 'when', 'async', 'poll', 'notify',
|
||||
'first_available_file', 'include', 'tags', 'register', 'ignore_errors',
|
||||
'delegate_to', 'local_action', 'transport', 'remote_user', 'sudo', 'sudo_user',
|
||||
'sudo_pass', 'when', 'connection', 'environment', 'args',
|
||||
'any_errors_fatal', 'changed_when', 'failed_when', 'always_run', 'delay', 'retries', 'until',
|
||||
'su', 'su_user', 'su_pass', 'no_log', 'run_once',
|
||||
))
|
||||
VALID_KEYS = frozenset([
|
||||
'async', 'connection', 'include', 'poll',
|
||||
] + _t_common)
|
||||
|
||||
def __init__(self, play, ds, module_vars=None, play_vars=None, play_file_vars=None, role_vars=None, role_params=None, default_vars=None, additional_conditions=None, role_name=None):
|
||||
''' constructor loads from a task or handler datastructure '''
|
||||
@@ -131,14 +129,12 @@ class Task(object):
|
||||
self.name = ds.get('name', None)
|
||||
self.tags = [ 'untagged' ]
|
||||
self.register = ds.get('register', None)
|
||||
self.sudo = utils.boolean(ds.get('sudo', play.sudo))
|
||||
self.su = utils.boolean(ds.get('su', play.su))
|
||||
self.environment = ds.get('environment', play.environment)
|
||||
self.role_name = role_name
|
||||
self.no_log = utils.boolean(ds.get('no_log', "false")) or self.play.no_log
|
||||
self.run_once = utils.boolean(ds.get('run_once', 'false'))
|
||||
|
||||
#Code to allow do until feature in a Task
|
||||
#Code to allow do until feature in a Task
|
||||
if 'until' in ds:
|
||||
if not ds.get('register'):
|
||||
raise errors.AnsibleError("register keyword is mandatory when using do until feature")
|
||||
@@ -160,24 +156,36 @@ class Task(object):
|
||||
else:
|
||||
self.remote_user = ds.get('remote_user', play.playbook.remote_user)
|
||||
|
||||
self.sudo_user = None
|
||||
self.sudo_pass = None
|
||||
self.su_user = None
|
||||
self.su_pass = None
|
||||
# Fail out if user specifies privilege escalation params in conflict
|
||||
if (ds.get('become') or ds.get('become_user') or ds.get('become_pass')) and (ds.get('sudo') or ds.get('sudo_user') or ds.get('sudo_pass')):
|
||||
raise errors.AnsibleError('incompatible parameters ("become", "become_user", "become_pass") and sudo params "sudo", "sudo_user", "sudo_pass" in task: %s' % self.name)
|
||||
|
||||
if self.sudo:
|
||||
self.sudo_user = ds.get('sudo_user', play.sudo_user)
|
||||
self.sudo_pass = ds.get('sudo_pass', play.playbook.sudo_pass)
|
||||
elif self.su:
|
||||
self.su_user = ds.get('su_user', play.su_user)
|
||||
self.su_pass = ds.get('su_pass', play.playbook.su_pass)
|
||||
if (ds.get('become') or ds.get('become_user') or ds.get('become_pass')) and (ds.get('su') or ds.get('su_user') or ds.get('su_pass')):
|
||||
raise errors.AnsibleError('incompatible parameters ("become", "become_user", "become_pass") and su params "su", "su_user", "sudo_pass" in task: %s' % self.name)
|
||||
|
||||
# Fail out if user specifies a sudo param with a su param in a given play
|
||||
if (ds.get('sudo') or ds.get('sudo_user') or ds.get('sudo_pass')) and \
|
||||
(ds.get('su') or ds.get('su_user') or ds.get('su_pass')):
|
||||
raise errors.AnsibleError('sudo params ("sudo", "sudo_user", "sudo_pass") '
|
||||
'and su params "su", "su_user", "su_pass") '
|
||||
'cannot be used together')
|
||||
if (ds.get('sudo') or ds.get('sudo_user') or ds.get('sudo_pass')) and (ds.get('su') or ds.get('su_user') or ds.get('su_pass')):
|
||||
raise errors.AnsibleError('incompatible parameters ("su", "su_user", "su_pass") and sudo params "sudo", "sudo_user", "sudo_pass" in task: %s' % self.name)
|
||||
|
||||
self.become = utils.boolean(ds.get('become', play.become))
|
||||
self.become_method = ds.get('become_method', play.become_method)
|
||||
self.become_user = ds.get('become_user', play.become_user)
|
||||
self.become_pass = ds.get('become_pass', play.playbook.become_pass)
|
||||
|
||||
# set only if passed in current task data
|
||||
if 'sudo' in ds or 'sudo_user' in ds:
|
||||
self.become=ds['sudo']
|
||||
self.become_method='sudo'
|
||||
if 'sudo_user' in ds:
|
||||
self.become_user = ds['sudo_user']
|
||||
if 'sudo_pass' in ds:
|
||||
self.become_pass = ds['sudo_pass']
|
||||
if 'su' in ds or 'su_user' in ds:
|
||||
self.become=ds['su']
|
||||
self.become_method='su'
|
||||
if 'su_user' in ds:
|
||||
self.become_user = ds['su_user']
|
||||
if 'su_pass' in ds:
|
||||
self.become_pass = ds['su_pass']
|
||||
|
||||
# Both are defined
|
||||
if ('action' in ds) and ('local_action' in ds):
|
||||
|
||||
@@ -123,7 +123,6 @@ class Runner(object):
|
||||
remote_pass=C.DEFAULT_REMOTE_PASS, # ex: 'password123' or None if using key
|
||||
remote_port=None, # if SSH on different ports
|
||||
private_key_file=C.DEFAULT_PRIVATE_KEY_FILE, # if not using keys/passwords
|
||||
sudo_pass=C.DEFAULT_SUDO_PASS, # ex: 'password123' or None
|
||||
background=0, # async poll every X seconds, else 0 for non-async
|
||||
basedir=None, # directory of playbook, if applicable
|
||||
setup_cache=None, # used to share fact data w/ other tasks
|
||||
@@ -131,8 +130,6 @@ class Runner(object):
|
||||
transport=C.DEFAULT_TRANSPORT, # 'ssh', 'paramiko', 'local'
|
||||
conditional='True', # run only if this fact expression evals to true
|
||||
callbacks=None, # used for output
|
||||
sudo=False, # whether to run sudo or not
|
||||
sudo_user=C.DEFAULT_SUDO_USER, # ex: 'root'
|
||||
module_vars=None, # a playbooks internals thing
|
||||
play_vars=None, #
|
||||
play_file_vars=None, #
|
||||
@@ -151,14 +148,15 @@ class Runner(object):
|
||||
accelerate=False, # use accelerated connection
|
||||
accelerate_ipv6=False, # accelerated connection w/ IPv6
|
||||
accelerate_port=None, # port to use with accelerated connection
|
||||
su=False, # Are we running our command via su?
|
||||
su_user=None, # User to su to when running command, ex: 'root'
|
||||
su_pass=C.DEFAULT_SU_PASS,
|
||||
vault_pass=None,
|
||||
run_hosts=None, # an optional list of pre-calculated hosts to run on
|
||||
no_log=False, # option to enable/disable logging for a given task
|
||||
run_once=False, # option to enable/disable host bypass loop for a given task
|
||||
sudo_exe=C.DEFAULT_SUDO_EXE, # ex: /usr/local/bin/sudo
|
||||
become=False, # whether to run privelege escalation or not
|
||||
become_method=C.DEFAULT_BECOME_METHOD,
|
||||
become_user=C.DEFAULT_BECOME_USER, # ex: 'root'
|
||||
become_pass=C.DEFAULT_BECOME_PASS, # ex: 'password123' or None
|
||||
become_exe=C.DEFAULT_BECOME_EXE, # ex: /usr/local/bin/sudo
|
||||
):
|
||||
|
||||
# used to lock multiprocess inputs and outputs at various levels
|
||||
@@ -201,10 +199,12 @@ class Runner(object):
|
||||
self.remote_port = remote_port
|
||||
self.private_key_file = private_key_file
|
||||
self.background = background
|
||||
self.sudo = sudo
|
||||
self.sudo_user_var = sudo_user
|
||||
self.sudo_user = None
|
||||
self.sudo_pass = sudo_pass
|
||||
self.become = become
|
||||
self.become_method = become_method
|
||||
self.become_user_var = become_user
|
||||
self.become_user = None
|
||||
self.become_pass = become_pass
|
||||
self.become_exe = become_exe
|
||||
self.is_playbook = is_playbook
|
||||
self.environment = environment
|
||||
self.complex_args = complex_args
|
||||
@@ -213,15 +213,10 @@ class Runner(object):
|
||||
self.accelerate_port = accelerate_port
|
||||
self.accelerate_ipv6 = accelerate_ipv6
|
||||
self.callbacks.runner = self
|
||||
self.su = su
|
||||
self.su_user_var = su_user
|
||||
self.su_user = None
|
||||
self.su_pass = su_pass
|
||||
self.omit_token = '__omit_place_holder__%s' % sha1(os.urandom(64)).hexdigest()
|
||||
self.vault_pass = vault_pass
|
||||
self.no_log = no_log
|
||||
self.run_once = run_once
|
||||
self.sudo_exe = sudo_exe
|
||||
|
||||
if self.transport == 'smart':
|
||||
# If the transport is 'smart', check to see if certain conditions
|
||||
@@ -369,7 +364,7 @@ class Runner(object):
|
||||
delegate['pass'] = this_info.get('ansible_ssh_pass', password)
|
||||
delegate['private_key_file'] = this_info.get('ansible_ssh_private_key_file', self.private_key_file)
|
||||
delegate['transport'] = this_info.get('ansible_connection', self.transport)
|
||||
delegate['sudo_pass'] = this_info.get('ansible_sudo_pass', self.sudo_pass)
|
||||
delegate['become_pass'] = this_info.get('ansible_become_pass', this_info.get('ansible_ssh_pass', self.become_pass))
|
||||
|
||||
# Last chance to get private_key_file from global variables.
|
||||
# this is useful if delegated host is not defined in the inventory
|
||||
@@ -481,13 +476,13 @@ class Runner(object):
|
||||
or not conn.has_pipelining
|
||||
or not C.ANSIBLE_SSH_PIPELINING
|
||||
or C.DEFAULT_KEEP_REMOTE_FILES
|
||||
or self.su):
|
||||
or self.become_method == 'su'):
|
||||
self._transfer_str(conn, tmp, module_name, module_data)
|
||||
|
||||
environment_string = self._compute_environment_string(conn, inject)
|
||||
|
||||
if "tmp" in tmp and ((self.sudo and self.sudo_user != 'root') or (self.su and self.su_user != 'root')):
|
||||
# deal with possible umask issues once sudo'ed to other user
|
||||
if "tmp" in tmp and (self.become and self.become_user != 'root'):
|
||||
# deal with possible umask issues once you become another user
|
||||
self._remote_chmod(conn, 'a+r', remote_module_path, tmp)
|
||||
|
||||
cmd = ""
|
||||
@@ -514,8 +509,8 @@ class Runner(object):
|
||||
else:
|
||||
argsfile = self._transfer_str(conn, tmp, 'arguments', args)
|
||||
|
||||
if (self.sudo and self.sudo_user != 'root') or (self.su and self.su_user != 'root'):
|
||||
# deal with possible umask issues once sudo'ed to other user
|
||||
if self.become and self.become_user != 'root':
|
||||
# deal with possible umask issues once become another user
|
||||
self._remote_chmod(conn, 'a+r', argsfile, tmp)
|
||||
|
||||
if async_jid is None:
|
||||
@@ -524,7 +519,7 @@ class Runner(object):
|
||||
cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module, argsfile]])
|
||||
else:
|
||||
if async_jid is None:
|
||||
if conn.has_pipelining and C.ANSIBLE_SSH_PIPELINING and not C.DEFAULT_KEEP_REMOTE_FILES and not self.su:
|
||||
if conn.has_pipelining and C.ANSIBLE_SSH_PIPELINING and not C.DEFAULT_KEEP_REMOTE_FILES and not self.become_method == 'su':
|
||||
in_data = module_data
|
||||
else:
|
||||
cmd = "%s" % (remote_module_path)
|
||||
@@ -536,7 +531,7 @@ class Runner(object):
|
||||
|
||||
rm_tmp = None
|
||||
if "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
|
||||
if not self.sudo or self.su or self.sudo_user == 'root' or self.su_user == 'root':
|
||||
if not self.become or self.become_user == 'root':
|
||||
# not sudoing or sudoing to root, so can cleanup files in the same step
|
||||
rm_tmp = tmp
|
||||
|
||||
@@ -546,17 +541,14 @@ class Runner(object):
|
||||
sudoable = True
|
||||
if module_name == "accelerate":
|
||||
# always run the accelerate module as the user
|
||||
# specified in the play, not the sudo_user
|
||||
# specified in the play, not the become_user
|
||||
sudoable = False
|
||||
|
||||
if self.su:
|
||||
res = self._low_level_exec_command(conn, cmd, tmp, su=True, in_data=in_data)
|
||||
else:
|
||||
res = self._low_level_exec_command(conn, cmd, tmp, sudoable=sudoable, in_data=in_data)
|
||||
res = self._low_level_exec_command(conn, cmd, tmp, become=self.become, sudoable=sudoable, in_data=in_data)
|
||||
|
||||
if "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
|
||||
if (self.sudo and self.sudo_user != 'root') or (self.su and self.su_user != 'root'):
|
||||
# not sudoing to root, so maybe can't delete files as that other user
|
||||
if self.become and self.become_user != 'root':
|
||||
# not becoming root, so maybe can't delete files as that other user
|
||||
# have to clean up temp files as original user in a second step
|
||||
cmd2 = conn.shell.remove(tmp, recurse=True)
|
||||
self._low_level_exec_command(conn, cmd2, tmp, sudoable=False)
|
||||
@@ -849,11 +841,9 @@ class Runner(object):
|
||||
def _executor_internal_inner(self, host, module_name, module_args, inject, port, is_chained=False, complex_args=None):
|
||||
''' decides how to invoke a module '''
|
||||
|
||||
# late processing of parameterized sudo_user (with_items,..)
|
||||
if self.sudo_user_var is not None:
|
||||
self.sudo_user = template.template(self.basedir, self.sudo_user_var, inject)
|
||||
if self.su_user_var is not None:
|
||||
self.su_user = template.template(self.basedir, self.su_user_var, inject)
|
||||
# late processing of parameterized become_user (with_items,..)
|
||||
if self.become_user_var is not None:
|
||||
self.become_user = template.template(self.basedir, self.become_user_var, inject)
|
||||
|
||||
# module_name may be dynamic (but cannot contain {{ ansible_ssh_user }})
|
||||
module_name = template.template(self.basedir, module_name, inject)
|
||||
@@ -893,18 +883,16 @@ class Runner(object):
|
||||
actual_transport = inject.get('ansible_connection', self.transport)
|
||||
actual_private_key_file = inject.get('ansible_ssh_private_key_file', self.private_key_file)
|
||||
actual_private_key_file = template.template(self.basedir, actual_private_key_file, inject, fail_on_undefined=True)
|
||||
self.sudo = utils.boolean(inject.get('ansible_sudo', self.sudo))
|
||||
self.sudo_user = inject.get('ansible_sudo_user', self.sudo_user)
|
||||
self.sudo_pass = inject.get('ansible_sudo_pass', self.sudo_pass)
|
||||
self.su = inject.get('ansible_su', self.su)
|
||||
self.su_pass = inject.get('ansible_su_pass', self.su_pass)
|
||||
self.sudo_exe = inject.get('ansible_sudo_exe', self.sudo_exe)
|
||||
self.become = utils.boolean(inject.get('ansible_become', inject.get('ansible_sudo', inject.get('ansible_su', self.become))))
|
||||
self.become_user = inject.get('ansible_become_user', inject.get('ansible_sudo_user', inject.get('ansible_su_user',self.become_user)))
|
||||
self.become_pass = inject.get('ansible_become_pass', inject.get('ansible_sudo_pass', inject.get('ansible_su_pass', self.become_pass)))
|
||||
self.become_exe = inject.get('ansible_become_exe', inject.get('ansible_sudo_exe', self.become_exe))
|
||||
|
||||
# select default root user in case self.sudo requested
|
||||
# select default root user in case self.become requested
|
||||
# but no user specified; happens e.g. in host vars when
|
||||
# just ansible_sudo=True is specified
|
||||
if self.sudo and self.sudo_user is None:
|
||||
self.sudo_user = 'root'
|
||||
# just ansible_become=True is specified
|
||||
if self.become and self.become_user is None:
|
||||
self.become_user = 'root'
|
||||
|
||||
if actual_private_key_file is not None:
|
||||
actual_private_key_file = os.path.expanduser(actual_private_key_file)
|
||||
@@ -937,7 +925,7 @@ class Runner(object):
|
||||
actual_user = delegate['user']
|
||||
actual_pass = delegate['pass']
|
||||
actual_private_key_file = delegate['private_key_file']
|
||||
self.sudo_pass = delegate['sudo_pass']
|
||||
self.become_pass = delegate.get('become_pass',delegate.get('sudo_pass'))
|
||||
inject = delegate['inject']
|
||||
# set resolved delegate_to into inject so modules can call _remote_checksum
|
||||
inject['delegate_to'] = self.delegate_to
|
||||
@@ -945,7 +933,7 @@ class Runner(object):
|
||||
# user/pass may still contain variables at this stage
|
||||
actual_user = template.template(self.basedir, actual_user, inject)
|
||||
actual_pass = template.template(self.basedir, actual_pass, inject)
|
||||
self.sudo_pass = template.template(self.basedir, self.sudo_pass, inject)
|
||||
self.become_pass = template.template(self.basedir, self.become_pass, inject)
|
||||
|
||||
# make actual_user available as __magic__ ansible_ssh_user variable
|
||||
inject['ansible_ssh_user'] = actual_user
|
||||
@@ -1134,7 +1122,7 @@ class Runner(object):
|
||||
if "tmp" in tmp:
|
||||
# tmp has already been created
|
||||
return False
|
||||
if not conn.has_pipelining or not C.ANSIBLE_SSH_PIPELINING or C.DEFAULT_KEEP_REMOTE_FILES or self.su:
|
||||
if not conn.has_pipelining or not C.ANSIBLE_SSH_PIPELINING or C.DEFAULT_KEEP_REMOTE_FILES or self.become_method == 'su':
|
||||
# tmp is necessary to store module source code
|
||||
return True
|
||||
if not conn.has_pipelining:
|
||||
@@ -1150,62 +1138,54 @@ class Runner(object):
|
||||
# *****************************************************
|
||||
|
||||
def _low_level_exec_command(self, conn, cmd, tmp, sudoable=False,
|
||||
executable=None, su=False, in_data=None):
|
||||
executable=None, become=False, in_data=None):
|
||||
''' execute a command string over SSH, return the output '''
|
||||
# this can be skipped with powershell modules when there is no analog to a Windows command (like chmod)
|
||||
if cmd:
|
||||
|
||||
if not cmd:
|
||||
# this can happen with powershell modules when there is no analog to a Windows command (like chmod)
|
||||
return dict(stdout='', stderr='')
|
||||
if executable is None:
|
||||
executable = C.DEFAULT_EXECUTABLE
|
||||
|
||||
if executable is None:
|
||||
executable = C.DEFAULT_EXECUTABLE
|
||||
become_user = self.become_user
|
||||
|
||||
sudo_user = self.sudo_user
|
||||
su_user = self.su_user
|
||||
# compare connection user to (su|sudo)_user and disable if the same
|
||||
# assume connection type is local if no user attribute
|
||||
this_user = getattr(conn, 'user', getpass.getuser())
|
||||
if (not become and this_user == become_user):
|
||||
sudoable = False
|
||||
become = False
|
||||
|
||||
# compare connection user to (su|sudo)_user and disable if the same
|
||||
# assume connection type is local if no user attribute
|
||||
this_user = getattr(conn, 'user', getpass.getuser())
|
||||
if (not su and this_user == sudo_user) or (su and this_user == su_user):
|
||||
sudoable = False
|
||||
su = False
|
||||
|
||||
if su:
|
||||
rc, stdin, stdout, stderr = conn.exec_command(cmd,
|
||||
tmp,
|
||||
su=su,
|
||||
su_user=su_user,
|
||||
executable=executable,
|
||||
in_data=in_data)
|
||||
else:
|
||||
rc, stdin, stdout, stderr = conn.exec_command(cmd,
|
||||
tmp,
|
||||
sudo_user,
|
||||
become_user=become_user,
|
||||
sudoable=sudoable,
|
||||
executable=executable,
|
||||
in_data=in_data)
|
||||
|
||||
if type(stdout) not in [ str, unicode ]:
|
||||
out = ''.join(stdout.readlines())
|
||||
else:
|
||||
out = stdout
|
||||
if type(stdout) not in [ str, unicode ]:
|
||||
out = ''.join(stdout.readlines())
|
||||
else:
|
||||
out = stdout
|
||||
|
||||
if type(stderr) not in [ str, unicode ]:
|
||||
err = ''.join(stderr.readlines())
|
||||
else:
|
||||
err = stderr
|
||||
if type(stderr) not in [ str, unicode ]:
|
||||
err = ''.join(stderr.readlines())
|
||||
else:
|
||||
err = stderr
|
||||
|
||||
if rc is not None:
|
||||
return dict(rc=rc, stdout=out, stderr=err)
|
||||
else:
|
||||
return dict(stdout=out, stderr=err)
|
||||
|
||||
return dict(rc=None, stdout='', stderr='')
|
||||
|
||||
if rc is not None:
|
||||
return dict(rc=rc, stdout=out, stderr=err)
|
||||
else:
|
||||
return dict(stdout=out, stderr=err)
|
||||
|
||||
# *****************************************************
|
||||
|
||||
def _remote_chmod(self, conn, mode, path, tmp, sudoable=False, su=False):
|
||||
def _remote_chmod(self, conn, mode, path, tmp, sudoable=False, become=False):
|
||||
''' issue a remote chmod command '''
|
||||
cmd = conn.shell.chmod(mode, path)
|
||||
return self._low_level_exec_command(conn, cmd, tmp, sudoable=sudoable, su=su)
|
||||
return self._low_level_exec_command(conn, cmd, tmp, sudoable=sudoable, become=become)
|
||||
|
||||
# *****************************************************
|
||||
|
||||
@@ -1217,13 +1197,11 @@ class Runner(object):
|
||||
split_path = path.split(os.path.sep, 1)
|
||||
expand_path = split_path[0]
|
||||
if expand_path == '~':
|
||||
if self.sudo and self.sudo_user:
|
||||
expand_path = '~%s' % self.sudo_user
|
||||
elif self.su and self.su_user:
|
||||
expand_path = '~%s' % self.su_user
|
||||
if self.become and self.become_user:
|
||||
expand_path = '~%s' % self.become_user
|
||||
|
||||
cmd = conn.shell.expand_user(expand_path)
|
||||
data = self._low_level_exec_command(conn, cmd, tmp, sudoable=False, su=False)
|
||||
data = self._low_level_exec_command(conn, cmd, tmp, sudoable=False, become=False)
|
||||
initial_fragment = utils.last_non_blank_line(data['stdout'])
|
||||
|
||||
if not initial_fragment:
|
||||
@@ -1287,11 +1265,11 @@ class Runner(object):
|
||||
''' make and return a temporary path on a remote box '''
|
||||
basefile = 'ansible-tmp-%s-%s' % (time.time(), random.randint(0, 2**48))
|
||||
use_system_tmp = False
|
||||
if (self.sudo and self.sudo_user != 'root') or (self.su and self.su_user != 'root'):
|
||||
if self.become and self.become_user != 'root':
|
||||
use_system_tmp = True
|
||||
|
||||
tmp_mode = None
|
||||
if self.remote_user != 'root' or ((self.sudo and self.sudo_user != 'root') or (self.su and self.su_user != 'root')):
|
||||
if self.remote_user != 'root' or (self.become and self.become_user != 'root'):
|
||||
tmp_mode = 'a+rx'
|
||||
|
||||
cmd = conn.shell.mkdtemp(basefile, use_system_tmp, tmp_mode)
|
||||
|
||||
@@ -125,7 +125,7 @@ class ActionModule(object):
|
||||
xfered = self.runner._transfer_str(conn, tmp, 'src', resultant)
|
||||
|
||||
# fix file permissions when the copy is done as a different user
|
||||
if self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
||||
if self.runner.become and self.runner.become_user != 'root':
|
||||
self.runner._remote_chmod(conn, 'a+r', xfered, tmp)
|
||||
|
||||
# run the copy module
|
||||
|
||||
@@ -234,7 +234,7 @@ class ActionModule(object):
|
||||
self._remove_tempfile_if_content_defined(content, content_tempfile)
|
||||
|
||||
# fix file permissions when the copy is done as a different user
|
||||
if (self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root') and not raw:
|
||||
if self.runner.become and self.runner.become_user != 'root' and not raw:
|
||||
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp_path)
|
||||
|
||||
if raw:
|
||||
|
||||
@@ -78,7 +78,7 @@ class ActionModule(object):
|
||||
|
||||
# use slurp if sudo and permissions are lacking
|
||||
remote_data = None
|
||||
if remote_checksum in ('1', '2') or self.runner.sudo:
|
||||
if remote_checksum in ('1', '2') or self.runner.become:
|
||||
slurpres = self.runner._execute_module(conn, tmp, 'slurp', 'src=%s' % source, inject=inject)
|
||||
if slurpres.is_successful():
|
||||
if slurpres.result['encoding'] == 'base64':
|
||||
|
||||
@@ -50,7 +50,7 @@ class ActionModule(object):
|
||||
tmp_src = tmp + src
|
||||
conn.put_file(src, tmp_src)
|
||||
|
||||
if self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
||||
if self.runner.become and self.runner.become_user != 'root':
|
||||
if not self.runner.noop_on_check(inject):
|
||||
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp)
|
||||
|
||||
|
||||
@@ -113,8 +113,7 @@ class ActionModule(object):
|
||||
|
||||
sudoable = True
|
||||
# set file permissions, more permissive when the copy is done as a different user
|
||||
if ((self.runner.sudo and self.runner.sudo_user != 'root') or
|
||||
(self.runner.su and self.runner.su_user != 'root')):
|
||||
if self.runner.become and self.runner.become_user != 'root':
|
||||
chmod_mode = 'a+rx'
|
||||
sudoable = False
|
||||
else:
|
||||
|
||||
@@ -78,7 +78,7 @@ class ActionModule(object):
|
||||
|
||||
# Store original transport and sudo values.
|
||||
self.original_transport = inject.get('ansible_connection', self.runner.transport)
|
||||
self.original_sudo = self.runner.sudo
|
||||
self.original_become = self.runner.become
|
||||
self.transport_overridden = False
|
||||
|
||||
if inject.get('delegate_to') is None:
|
||||
@@ -87,7 +87,7 @@ class ActionModule(object):
|
||||
if self.original_transport != 'local':
|
||||
inject['ansible_connection'] = 'local'
|
||||
self.transport_overridden = True
|
||||
self.runner.sudo = False
|
||||
self.runner.become = False
|
||||
|
||||
def run(self, conn, tmp, module_name, module_args,
|
||||
inject, complex_args=None, **kwargs):
|
||||
@@ -143,7 +143,7 @@ class ActionModule(object):
|
||||
# use a delegate host instead of localhost
|
||||
use_delegate = True
|
||||
|
||||
# COMPARE DELEGATE, HOST AND TRANSPORT
|
||||
# COMPARE DELEGATE, HOST AND TRANSPORT
|
||||
process_args = False
|
||||
if not dest_host is src_host and self.original_transport != 'local':
|
||||
# interpret and inject remote host info into src or dest
|
||||
@@ -160,7 +160,7 @@ class ActionModule(object):
|
||||
if not use_delegate or not user:
|
||||
user = inject.get('ansible_ssh_user',
|
||||
self.runner.remote_user)
|
||||
|
||||
|
||||
if use_delegate:
|
||||
# FIXME
|
||||
private_key = inject.get('ansible_ssh_private_key_file', self.runner.private_key_file)
|
||||
@@ -172,7 +172,7 @@ class ActionModule(object):
|
||||
if not private_key is None:
|
||||
private_key = os.path.expanduser(private_key)
|
||||
options['private_key'] = private_key
|
||||
|
||||
|
||||
# use the mode to define src and dest's url
|
||||
if options.get('mode', 'push') == 'pull':
|
||||
# src is a remote path: <user>@<host>, dest is a local path
|
||||
@@ -192,7 +192,7 @@ class ActionModule(object):
|
||||
rsync_path = options.get('rsync_path', None)
|
||||
|
||||
# If no rsync_path is set, sudo was originally set, and dest is remote then add 'sudo rsync' argument.
|
||||
if not rsync_path and self.transport_overridden and self.original_sudo and not dest_is_local:
|
||||
if not rsync_path and self.transport_overridden and self.original_become and not dest_is_local and self.runner.become_method == 'sudo':
|
||||
rsync_path = 'sudo rsync'
|
||||
|
||||
# make sure rsync path is quoted.
|
||||
@@ -206,8 +206,8 @@ class ActionModule(object):
|
||||
# run the module and store the result
|
||||
result = self.runner._execute_module(conn, tmp, 'synchronize', module_args, complex_args=options, inject=inject)
|
||||
|
||||
# reset the sudo property
|
||||
self.runner.sudo = self.original_sudo
|
||||
# reset the sudo property
|
||||
self.runner.become = self.original_become
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ class ActionModule(object):
|
||||
xfered = self.runner._transfer_str(conn, tmp, 'source', resultant)
|
||||
|
||||
# fix file permissions when the copy is done as a different user
|
||||
if self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
||||
if self.runner.become and self.runner.become_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
||||
self.runner._remote_chmod(conn, 'a+r', xfered, tmp)
|
||||
|
||||
# run the copy module
|
||||
|
||||
@@ -99,7 +99,7 @@ class ActionModule(object):
|
||||
# handle check mode client side
|
||||
# fix file permissions when the copy is done as a different user
|
||||
if copy:
|
||||
if self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
||||
if self.runner.become and self.runner.become_user != 'root':
|
||||
if not self.runner.noop_on_check(inject):
|
||||
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp)
|
||||
# Build temporary module_args.
|
||||
|
||||
@@ -230,7 +230,7 @@ class ActionModule(object):
|
||||
self._remove_tempfile_if_content_defined(content, content_tempfile)
|
||||
|
||||
# fix file permissions when the copy is done as a different user
|
||||
if (self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root') and not raw:
|
||||
if self.runner.become and self.runner.become_user != 'root' and not raw:
|
||||
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp_path)
|
||||
|
||||
if raw:
|
||||
|
||||
@@ -109,7 +109,7 @@ class ActionModule(object):
|
||||
xfered = self.runner._transfer_str(conn, tmp, 'source', resultant)
|
||||
|
||||
# fix file permissions when the copy is done as a different user
|
||||
if self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
||||
if self.runner.become and self.runner.become_user != 'root':
|
||||
self.runner._remote_chmod(conn, 'a+r', xfered, tmp)
|
||||
|
||||
# run the copy module
|
||||
|
||||
@@ -50,6 +50,7 @@ class Connection(object):
|
||||
self.accport = port[1]
|
||||
self.is_connected = False
|
||||
self.has_pipelining = False
|
||||
self.become_methods_supported=['sudo']
|
||||
|
||||
if not self.port:
|
||||
self.port = constants.DEFAULT_REMOTE_PORT
|
||||
@@ -226,11 +227,11 @@ class Connection(object):
|
||||
else:
|
||||
return response.get('rc') == 0
|
||||
|
||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
||||
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||
''' run a command on the remote host '''
|
||||
|
||||
if su or su_user:
|
||||
raise AnsibleError("Internal Error: this module does not support running commands via su")
|
||||
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||
|
||||
if in_data:
|
||||
raise AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||
@@ -238,8 +239,8 @@ class Connection(object):
|
||||
if executable == "":
|
||||
executable = constants.DEFAULT_EXECUTABLE
|
||||
|
||||
if self.runner.sudo and sudoable and sudo_user:
|
||||
cmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd)
|
||||
if self.runner.become and sudoable:
|
||||
cmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe)
|
||||
|
||||
vvv("EXEC COMMAND %s" % cmd)
|
||||
|
||||
@@ -292,8 +293,8 @@ class Connection(object):
|
||||
if fd.tell() >= fstat.st_size:
|
||||
last = True
|
||||
data = dict(mode='put', data=base64.b64encode(data), out_path=out_path, last=last)
|
||||
if self.runner.sudo:
|
||||
data['user'] = self.runner.sudo_user
|
||||
if self.runner.become:
|
||||
data['user'] = self.runner.become_user
|
||||
data = utils.jsonify(data)
|
||||
data = utils.encrypt(self.key, data)
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import subprocess
|
||||
from ansible import errors
|
||||
from ansible import utils
|
||||
from ansible.callbacks import vvv
|
||||
import ansible.constants as C
|
||||
|
||||
class Connection(object):
|
||||
''' Local chroot based connections '''
|
||||
@@ -31,6 +32,7 @@ class Connection(object):
|
||||
def __init__(self, runner, host, port, *args, **kwargs):
|
||||
self.chroot = host
|
||||
self.has_pipelining = False
|
||||
self.become_methods_supported=C.BECOME_METHODS
|
||||
|
||||
if os.geteuid() != 0:
|
||||
raise errors.AnsibleError("chroot connection requires running as root")
|
||||
@@ -60,16 +62,16 @@ class Connection(object):
|
||||
|
||||
return self
|
||||
|
||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
||||
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||
''' run a command on the chroot '''
|
||||
|
||||
if su or su_user:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
||||
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||
|
||||
if in_data:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||
|
||||
# We enter chroot as root so sudo stuff can be ignored
|
||||
# We enter chroot as root so we ignore privlege escalation?
|
||||
|
||||
if executable:
|
||||
local_cmd = [self.chroot_cmd, self.chroot, executable, '-c', cmd]
|
||||
|
||||
@@ -53,6 +53,8 @@ class Connection(object):
|
||||
else:
|
||||
self.port = port
|
||||
|
||||
self.become_methods_supported=[]
|
||||
|
||||
def connect(self):
|
||||
''' activates the connection object '''
|
||||
|
||||
@@ -64,11 +66,11 @@ class Connection(object):
|
||||
socket = self.context.socket(zmq.REQ)
|
||||
addr = "tcp://%s:%s" % (self.host, self.port)
|
||||
socket.connect(addr)
|
||||
self.socket = socket
|
||||
self.socket = socket
|
||||
|
||||
return self
|
||||
|
||||
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh', in_data=None, su_user=None, su=None):
|
||||
def exec_command(self, cmd, tmp_path, become_user, sudoable=False, executable='/bin/sh', in_data=None):
|
||||
''' run a command on the remote host '''
|
||||
|
||||
if in_data:
|
||||
@@ -76,7 +78,7 @@ class Connection(object):
|
||||
|
||||
vvv("EXEC COMMAND %s" % cmd)
|
||||
|
||||
if (self.runner.sudo and sudoable) or (self.runner.su and su):
|
||||
if self.runner.become and sudoable:
|
||||
raise errors.AnsibleError(
|
||||
"When using fireball, do not specify sudo or su to run your tasks. " +
|
||||
"Instead sudo the fireball action with sudo. " +
|
||||
|
||||
@@ -53,16 +53,14 @@ class Connection(object):
|
||||
self.client = fc.Client(self.host)
|
||||
return self
|
||||
|
||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False,
|
||||
executable='/bin/sh', in_data=None, su=None, su_user=None):
|
||||
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False,
|
||||
executable='/bin/sh', in_data=None):
|
||||
''' run a command on the remote minion '''
|
||||
|
||||
if su or su_user:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
||||
|
||||
if in_data:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||
|
||||
# totally ignores privlege escalation
|
||||
vvv("EXEC %s" % (cmd), host=self.host)
|
||||
p = self.client.command.run(cmd)[self.host]
|
||||
return (p[0], '', p[1], p[2])
|
||||
|
||||
@@ -24,6 +24,7 @@ import shutil
|
||||
import subprocess
|
||||
from ansible import errors
|
||||
from ansible.callbacks import vvv
|
||||
import ansible.constants as C
|
||||
|
||||
class Connection(object):
|
||||
''' Local chroot based connections '''
|
||||
@@ -61,6 +62,7 @@ class Connection(object):
|
||||
self.runner = runner
|
||||
self.host = host
|
||||
self.has_pipelining = False
|
||||
self.become_methods_supported=C.BECOME_METHODS
|
||||
|
||||
if os.geteuid() != 0:
|
||||
raise errors.AnsibleError("jail connection requires running as root")
|
||||
@@ -91,16 +93,16 @@ class Connection(object):
|
||||
local_cmd = '%s "%s" %s' % (self.jexec_cmd, self.jail, cmd)
|
||||
return local_cmd
|
||||
|
||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
||||
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||
''' run a command on the chroot '''
|
||||
|
||||
if su or su_user:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
||||
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||
|
||||
if in_data:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||
|
||||
# We enter chroot as root so sudo stuff can be ignored
|
||||
# Ignores privilege escalation
|
||||
local_cmd = self._generate_cmd(executable, cmd)
|
||||
|
||||
vvv("EXEC %s" % (local_cmd), host=self.jail)
|
||||
|
||||
@@ -22,6 +22,7 @@ import os
|
||||
import subprocess
|
||||
from ansible import errors
|
||||
from ansible.callbacks import vvv
|
||||
import ansible.constants as C
|
||||
|
||||
class Connection(object):
|
||||
''' Local lxc based connections '''
|
||||
@@ -50,6 +51,7 @@ class Connection(object):
|
||||
self.host = host
|
||||
# port is unused, since this is local
|
||||
self.port = port
|
||||
self.become_methods_supported=C.BECOME_METHODS
|
||||
|
||||
def connect(self, port=None):
|
||||
''' connect to the lxc; nothing to do here '''
|
||||
@@ -65,16 +67,16 @@ class Connection(object):
|
||||
local_cmd = '%s -q -c lxc:/// lxc-enter-namespace %s -- %s' % (self.cmd, self.lxc, cmd)
|
||||
return local_cmd
|
||||
|
||||
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
||||
def exec_command(self, cmd, tmp_path, become_user, sudoable=False, executable='/bin/sh', in_data=None):
|
||||
''' run a command on the chroot '''
|
||||
|
||||
if su or su_user:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
||||
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||
|
||||
if in_data:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||
|
||||
# We enter lxc as root so sudo stuff can be ignored
|
||||
# We ignore privelege escalation!
|
||||
local_cmd = self._generate_cmd(executable, cmd)
|
||||
|
||||
vvv("EXEC %s" % (local_cmd), host=self.lxc)
|
||||
|
||||
@@ -26,6 +26,7 @@ from ansible import errors
|
||||
from ansible import utils
|
||||
from ansible.callbacks import vvv
|
||||
|
||||
|
||||
class Connection(object):
|
||||
''' Local based connections '''
|
||||
|
||||
@@ -33,31 +34,34 @@ class Connection(object):
|
||||
self.runner = runner
|
||||
self.host = host
|
||||
# port is unused, since this is local
|
||||
self.port = port
|
||||
self.port = port
|
||||
self.has_pipelining = False
|
||||
|
||||
# TODO: add su(needs tty), pbrun, pfexec
|
||||
self.become_methods_supported=['sudo']
|
||||
|
||||
def connect(self, port=None):
|
||||
''' connect to the local host; nothing to do here '''
|
||||
|
||||
return self
|
||||
|
||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
||||
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||
''' run a command on the local host '''
|
||||
|
||||
# su requires to be run from a terminal, and therefore isn't supported here (yet?)
|
||||
if su or su_user:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
||||
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||
|
||||
if in_data:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||
|
||||
if not self.runner.sudo or not sudoable:
|
||||
if self.runner.become and sudoable:
|
||||
local_cmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '-H', self.runner.become_exe)
|
||||
else:
|
||||
if executable:
|
||||
local_cmd = executable.split() + ['-c', cmd]
|
||||
else:
|
||||
local_cmd = cmd
|
||||
else:
|
||||
local_cmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd)
|
||||
executable = executable.split()[0] if executable else None
|
||||
|
||||
vvv("EXEC %s" % (local_cmd), host=self.host)
|
||||
@@ -66,13 +70,19 @@ class Connection(object):
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
if self.runner.sudo and sudoable and self.runner.sudo_pass:
|
||||
if self.runner.become and sudoable and self.runner.become_pass:
|
||||
fcntl.fcntl(p.stdout, fcntl.F_SETFL,
|
||||
fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||
fcntl.fcntl(p.stderr, fcntl.F_SETFL,
|
||||
fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||
sudo_output = ''
|
||||
while not sudo_output.endswith(prompt) and success_key not in sudo_output:
|
||||
become_output = ''
|
||||
while success_key not in become_output:
|
||||
|
||||
if prompt and become_output.endswith(prompt):
|
||||
break
|
||||
if utils.su_prompts.check_su_prompt(become_output):
|
||||
break
|
||||
|
||||
rfd, wfd, efd = select.select([p.stdout, p.stderr], [],
|
||||
[p.stdout, p.stderr], self.runner.timeout)
|
||||
if p.stdout in rfd:
|
||||
@@ -81,13 +91,13 @@ class Connection(object):
|
||||
chunk = p.stderr.read()
|
||||
else:
|
||||
stdout, stderr = p.communicate()
|
||||
raise errors.AnsibleError('timeout waiting for sudo password prompt:\n' + sudo_output)
|
||||
raise errors.AnsibleError('timeout waiting for %s password prompt:\n' % self.runner.become_method + become_output)
|
||||
if not chunk:
|
||||
stdout, stderr = p.communicate()
|
||||
raise errors.AnsibleError('sudo output closed while waiting for password prompt:\n' + sudo_output)
|
||||
sudo_output += chunk
|
||||
if success_key not in sudo_output:
|
||||
p.stdin.write(self.runner.sudo_pass + '\n')
|
||||
raise errors.AnsibleError('%s output closed while waiting for password prompt:\n' % self.runner.become_method + become_output)
|
||||
become_output += chunk
|
||||
if success_key not in become_output:
|
||||
p.stdin.write(self.runner.become_pass + '\n')
|
||||
fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK)
|
||||
fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) & ~os.O_NONBLOCK)
|
||||
|
||||
|
||||
@@ -125,6 +125,9 @@ class Connection(object):
|
||||
self.private_key_file = private_key_file
|
||||
self.has_pipelining = False
|
||||
|
||||
# TODO: add pbrun, pfexec
|
||||
self.become_methods_supported=['sudo', 'su', 'pbrun']
|
||||
|
||||
def _cache_key(self):
|
||||
return "%s__%s__" % (self.host, self.user)
|
||||
|
||||
@@ -184,9 +187,12 @@ class Connection(object):
|
||||
|
||||
return ssh
|
||||
|
||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
||||
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||
''' run a command on the remote host '''
|
||||
|
||||
if self.runner.become and sudoable and self.runner.become_method not in self.become_methods_supported:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||
|
||||
if in_data:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||
|
||||
@@ -206,7 +212,7 @@ class Connection(object):
|
||||
|
||||
no_prompt_out = ''
|
||||
no_prompt_err = ''
|
||||
if not (self.runner.sudo and sudoable) and not (self.runner.su and su):
|
||||
if not (self.runner.become and sudoable):
|
||||
|
||||
if executable:
|
||||
quoted_command = executable + ' -c ' + pipes.quote(cmd)
|
||||
@@ -224,50 +230,46 @@ class Connection(object):
|
||||
chan.get_pty(term=os.getenv('TERM', 'vt100'),
|
||||
width=int(os.getenv('COLUMNS', 0)),
|
||||
height=int(os.getenv('LINES', 0)))
|
||||
if self.runner.sudo or sudoable:
|
||||
shcmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd)
|
||||
elif self.runner.su or su:
|
||||
shcmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd)
|
||||
if self.runner.become and sudoable:
|
||||
shcmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe)
|
||||
|
||||
vvv("EXEC %s" % shcmd, host=self.host)
|
||||
sudo_output = ''
|
||||
become_output = ''
|
||||
|
||||
try:
|
||||
|
||||
chan.exec_command(shcmd)
|
||||
|
||||
if self.runner.sudo_pass or self.runner.su_pass:
|
||||
if self.runner.become_pass:
|
||||
|
||||
while True:
|
||||
|
||||
if success_key in sudo_output or \
|
||||
(self.runner.sudo_pass and sudo_output.endswith(prompt)) or \
|
||||
(self.runner.su_pass and utils.su_prompts.check_su_prompt(sudo_output)):
|
||||
if success_key in become_output or \
|
||||
(prompt and become_output.endswith(prompt)) or \
|
||||
utils.su_prompts.check_su_prompt(become_output)):
|
||||
break
|
||||
chunk = chan.recv(bufsize)
|
||||
|
||||
if not chunk:
|
||||
if 'unknown user' in sudo_output:
|
||||
if 'unknown user' in become_output:
|
||||
raise errors.AnsibleError(
|
||||
'user %s does not exist' % sudo_user)
|
||||
'user %s does not exist' % become_user)
|
||||
else:
|
||||
raise errors.AnsibleError('ssh connection ' +
|
||||
'closed waiting for password prompt')
|
||||
sudo_output += chunk
|
||||
become_output += chunk
|
||||
|
||||
if success_key not in sudo_output:
|
||||
if success_key not in become_output:
|
||||
|
||||
if sudoable:
|
||||
chan.sendall(self.runner.sudo_pass + '\n')
|
||||
elif su:
|
||||
chan.sendall(self.runner.su_pass + '\n')
|
||||
chan.sendall(self.runner.become_pass + '\n')
|
||||
else:
|
||||
no_prompt_out += sudo_output
|
||||
no_prompt_err += sudo_output
|
||||
no_prompt_out += become_output
|
||||
no_prompt_err += become_output
|
||||
|
||||
except socket.timeout:
|
||||
|
||||
raise errors.AnsibleError('ssh timed out waiting for sudo.\n' + sudo_output)
|
||||
raise errors.AnsibleError('ssh timed out waiting for privilege escalation.\n' + become_output)
|
||||
|
||||
stdout = ''.join(chan.makefile('rb', bufsize))
|
||||
stderr = ''.join(chan.makefile_stderr('rb', bufsize))
|
||||
|
||||
@@ -34,6 +34,7 @@ from ansible.callbacks import vvv
|
||||
from ansible import errors
|
||||
from ansible import utils
|
||||
|
||||
|
||||
class Connection(object):
|
||||
''' ssh based connections '''
|
||||
|
||||
@@ -48,6 +49,9 @@ class Connection(object):
|
||||
self.HASHED_KEY_MAGIC = "|1|"
|
||||
self.has_pipelining = True
|
||||
|
||||
# TODO: add pbrun, pfexec
|
||||
self.become_methods_supported=['sudo', 'su', 'pbrun']
|
||||
|
||||
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX)
|
||||
self.cp_dir = utils.prepare_writeable_dir('$HOME/.ansible/cp',mode=0700)
|
||||
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN)
|
||||
@@ -140,7 +144,7 @@ class Connection(object):
|
||||
os.write(self.wfd, "%s\n" % self.password)
|
||||
os.close(self.wfd)
|
||||
|
||||
def _communicate(self, p, stdin, indata, su=False, sudoable=False, prompt=None):
|
||||
def _communicate(self, p, stdin, indata, sudoable=False, prompt=None):
|
||||
fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK)
|
||||
fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) & ~os.O_NONBLOCK)
|
||||
# We can't use p.communicate here because the ControlMaster may have stdout open as well
|
||||
@@ -157,23 +161,20 @@ class Connection(object):
|
||||
while True:
|
||||
rfd, wfd, efd = select.select(rpipes, [], rpipes, 1)
|
||||
|
||||
# fail early if the sudo/su password is wrong
|
||||
if self.runner.sudo and sudoable:
|
||||
if self.runner.sudo_pass:
|
||||
# fail early if the become password is wrong
|
||||
if self.runner.become and sudoable:
|
||||
if self.runner.become_pass:
|
||||
incorrect_password = gettext.dgettext(
|
||||
"sudo", "Sorry, try again.")
|
||||
"Privilege Escalation", "Sorry, try again.")
|
||||
if stdout.endswith("%s\r\n%s" % (incorrect_password,
|
||||
prompt)):
|
||||
raise errors.AnsibleError('Incorrect sudo password')
|
||||
raise errors.AnsibleError('Incorrect become password')
|
||||
|
||||
if stdout.endswith(prompt):
|
||||
raise errors.AnsibleError('Missing sudo password')
|
||||
|
||||
if self.runner.su and su and self.runner.su_pass:
|
||||
incorrect_password = gettext.dgettext(
|
||||
"su", "Sorry")
|
||||
if stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)):
|
||||
raise errors.AnsibleError('Incorrect su password')
|
||||
if prompt:
|
||||
if stdout.endswith(prompt):
|
||||
raise errors.AnsibleError('Missing become password')
|
||||
elif stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)):
|
||||
raise errors.AnsibleError('Incorrect becom password')
|
||||
|
||||
if p.stdout in rfd:
|
||||
dat = os.read(p.stdout.fileno(), 9000)
|
||||
@@ -256,9 +257,12 @@ class Connection(object):
|
||||
vvv("EXEC previous known host file not found for %s" % host)
|
||||
return True
|
||||
|
||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su_user=None, su=False):
|
||||
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||
''' run a command on the remote host '''
|
||||
|
||||
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||
|
||||
ssh_cmd = self._password_cmd()
|
||||
ssh_cmd += ["ssh", "-C"]
|
||||
if not in_data:
|
||||
@@ -276,25 +280,22 @@ class Connection(object):
|
||||
ssh_cmd += ['-6']
|
||||
ssh_cmd += [self.host]
|
||||
|
||||
if su and su_user:
|
||||
sudocmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd)
|
||||
ssh_cmd.append(sudocmd)
|
||||
elif not self.runner.sudo or not sudoable:
|
||||
if self.runner.become and sudoable:
|
||||
becomecmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe)
|
||||
ssh_cmd.append(becomecmd)
|
||||
else:
|
||||
prompt = None
|
||||
if executable:
|
||||
ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd))
|
||||
else:
|
||||
ssh_cmd.append(cmd)
|
||||
else:
|
||||
sudocmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd)
|
||||
ssh_cmd.append(sudocmd)
|
||||
|
||||
vvv("EXEC %s" % ' '.join(ssh_cmd), host=self.host)
|
||||
|
||||
not_in_host_file = self.not_in_host_file(self.host)
|
||||
|
||||
if C.HOST_KEY_CHECKING and not_in_host_file:
|
||||
# lock around the initial SSH connectivity so the user prompt about whether to add
|
||||
# lock around the initial SSH connectivity so the user prompt about whether to add
|
||||
# the host to known hosts is not intermingled with multiprocess output.
|
||||
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX)
|
||||
fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX)
|
||||
@@ -306,9 +307,8 @@ class Connection(object):
|
||||
|
||||
no_prompt_out = ''
|
||||
no_prompt_err = ''
|
||||
if (self.runner.sudo and sudoable and self.runner.sudo_pass) or \
|
||||
(self.runner.su and su and self.runner.su_pass):
|
||||
# several cases are handled for sudo privileges with password
|
||||
if self.runner.become and sudoable and self.runner.become_pass:
|
||||
# several cases are handled for escalated privileges with password
|
||||
# * NOPASSWD (tty & no-tty): detect success_key on stdout
|
||||
# * without NOPASSWD:
|
||||
# * detect prompt on stdout (tty)
|
||||
@@ -317,13 +317,14 @@ class Connection(object):
|
||||
fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||
fcntl.fcntl(p.stderr, fcntl.F_SETFL,
|
||||
fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||
sudo_output = ''
|
||||
sudo_errput = ''
|
||||
become_output = ''
|
||||
become_errput = ''
|
||||
|
||||
while True:
|
||||
if success_key in sudo_output or \
|
||||
(self.runner.sudo_pass and sudo_output.endswith(prompt)) or \
|
||||
(self.runner.su_pass and utils.su_prompts.check_su_prompt(sudo_output)):
|
||||
while success_key not in become_output:
|
||||
|
||||
if prompt and become_output.endswith(prompt):
|
||||
break
|
||||
if utils.su_prompts.check_su_prompt(become_output):
|
||||
break
|
||||
|
||||
rfd, wfd, efd = select.select([p.stdout, p.stderr], [],
|
||||
@@ -331,36 +332,34 @@ class Connection(object):
|
||||
if p.stderr in rfd:
|
||||
chunk = p.stderr.read()
|
||||
if not chunk:
|
||||
raise errors.AnsibleError('ssh connection closed waiting for sudo or su password prompt')
|
||||
sudo_errput += chunk
|
||||
raise errors.AnsibleError('ssh connection closed waiting for a privilege escalation password prompt')
|
||||
become_errput += chunk
|
||||
incorrect_password = gettext.dgettext(
|
||||
"sudo", "Sorry, try again.")
|
||||
if sudo_errput.strip().endswith("%s%s" % (prompt, incorrect_password)):
|
||||
raise errors.AnsibleError('Incorrect sudo password')
|
||||
elif prompt and sudo_errput.endswith(prompt):
|
||||
stdin.write(self.runner.sudo_pass + '\n')
|
||||
"become", "Sorry, try again.")
|
||||
if become_errput.strip().endswith("%s%s" % (prompt, incorrect_password)):
|
||||
raise errors.AnsibleError('Incorrect become password')
|
||||
elif prompt and become_errput.endswith(prompt):
|
||||
stdin.write(self.runner.become_pass + '\n')
|
||||
|
||||
if p.stdout in rfd:
|
||||
chunk = p.stdout.read()
|
||||
if not chunk:
|
||||
raise errors.AnsibleError('ssh connection closed waiting for sudo or su password prompt')
|
||||
sudo_output += chunk
|
||||
raise errors.AnsibleError('ssh connection closed waiting for %s password prompt' % self.runner.become_method)
|
||||
become_output += chunk
|
||||
|
||||
if not rfd:
|
||||
# timeout. wrap up process communication
|
||||
stdout = p.communicate()
|
||||
raise errors.AnsibleError('ssh connection error waiting for sudo or su password prompt')
|
||||
raise errors.AnsibleError('ssh connection error while waiting for %s password prompt' % self.runner.become_method)
|
||||
|
||||
if success_key not in sudo_output:
|
||||
if success_key not in become_output:
|
||||
if sudoable:
|
||||
stdin.write(self.runner.sudo_pass + '\n')
|
||||
elif su:
|
||||
stdin.write(self.runner.su_pass + '\n')
|
||||
stdin.write(self.runner.become_pass + '\n')
|
||||
else:
|
||||
no_prompt_out += sudo_output
|
||||
no_prompt_err += sudo_errput
|
||||
no_prompt_out += become_output
|
||||
no_prompt_err += become_errput
|
||||
|
||||
(returncode, stdout, stderr) = self._communicate(p, stdin, in_data, su=su, sudoable=sudoable, prompt=prompt)
|
||||
(returncode, stdout, stderr) = self._communicate(p, stdin, in_data, sudoable=sudoable, prompt=prompt)
|
||||
|
||||
if C.HOST_KEY_CHECKING and not_in_host_file:
|
||||
# lock around the initial SSH connectivity so the user prompt about whether to add
|
||||
|
||||
@@ -72,6 +72,10 @@ class Connection(object):
|
||||
self.shell_id = None
|
||||
self.delegate = None
|
||||
|
||||
# Add runas support
|
||||
#self.become_methods_supported=['runas']
|
||||
self.become_methods_supported=[]
|
||||
|
||||
def _winrm_connect(self):
|
||||
'''
|
||||
Establish a WinRM connection over HTTP/HTTPS.
|
||||
@@ -143,7 +147,11 @@ class Connection(object):
|
||||
self.protocol = self._winrm_connect()
|
||||
return self
|
||||
|
||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable=None, in_data=None, su=None, su_user=None):
|
||||
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable=None, in_data=None):
|
||||
|
||||
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||
|
||||
cmd = cmd.encode('utf-8')
|
||||
cmd_parts = shlex.split(cmd, posix=False)
|
||||
if '-EncodedCommand' in cmd_parts:
|
||||
|
||||
@@ -26,6 +26,7 @@ import subprocess
|
||||
from subprocess import Popen,PIPE
|
||||
from ansible import errors
|
||||
from ansible.callbacks import vvv
|
||||
import ansible.constants as C
|
||||
|
||||
class Connection(object):
|
||||
''' Local zone based connections '''
|
||||
@@ -68,6 +69,7 @@ class Connection(object):
|
||||
self.runner = runner
|
||||
self.host = host
|
||||
self.has_pipelining = False
|
||||
self.become_methods_supported=C.BECOME_METHODS
|
||||
|
||||
if os.geteuid() != 0:
|
||||
raise errors.AnsibleError("zone connection requires running as root")
|
||||
@@ -98,17 +100,16 @@ class Connection(object):
|
||||
local_cmd = '%s "%s" %s' % (self.zlogin_cmd, self.zone, cmd)
|
||||
return local_cmd
|
||||
|
||||
#def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable=None, in_data=None, su=None, su_user=None):
|
||||
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable=None, in_data=None):
|
||||
''' run a command on the zone '''
|
||||
|
||||
if su or su_user:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
||||
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||
|
||||
if in_data:
|
||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||
|
||||
# We enter zone as root so sudo stuff can be ignored
|
||||
# We happily ignore privelege escalation
|
||||
if executable == '/bin/sh':
|
||||
executable = None
|
||||
local_cmd = self._generate_cmd(executable, cmd)
|
||||
|
||||
@@ -992,14 +992,12 @@ def base_parser(constants=C, usage="", output_opts=False, runas_opts=False,
|
||||
default=constants.DEFAULT_HOST_LIST)
|
||||
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
|
||||
help="set additional variables as key=value or YAML/JSON", default=[])
|
||||
parser.add_option('-u', '--user', default=constants.DEFAULT_REMOTE_USER, dest='remote_user',
|
||||
help='connect as this user (default=%s)' % constants.DEFAULT_REMOTE_USER)
|
||||
parser.add_option('-k', '--ask-pass', default=False, dest='ask_pass', action='store_true',
|
||||
help='ask for SSH password')
|
||||
parser.add_option('--private-key', default=C.DEFAULT_PRIVATE_KEY_FILE, dest='private_key_file',
|
||||
parser.add_option('--private-key', default=constants.DEFAULT_PRIVATE_KEY_FILE, dest='private_key_file',
|
||||
help='use this file to authenticate the connection')
|
||||
parser.add_option('-K', '--ask-sudo-pass', default=False, dest='ask_sudo_pass', action='store_true',
|
||||
help='ask for sudo password')
|
||||
parser.add_option('--ask-su-pass', default=False, dest='ask_su_pass', action='store_true',
|
||||
help='ask for su password')
|
||||
parser.add_option('--ask-vault-pass', default=False, dest='ask_vault_pass', action='store_true',
|
||||
help='ask for vault password')
|
||||
parser.add_option('--vault-password-file', default=constants.DEFAULT_VAULT_PASSWORD_FILE,
|
||||
@@ -1025,22 +1023,35 @@ def base_parser(constants=C, usage="", output_opts=False, runas_opts=False,
|
||||
help='log output to this directory')
|
||||
|
||||
if runas_opts:
|
||||
parser.add_option("-s", "--sudo", default=constants.DEFAULT_SUDO, action="store_true",
|
||||
dest='sudo', help="run operations with sudo (nopasswd)")
|
||||
# priv user defaults to root later on to enable detecting when this option was given here
|
||||
parser.add_option('-K', '--ask-sudo-pass', default=False, dest='ask_sudo_pass', action='store_true',
|
||||
help='ask for sudo password (deprecated, use become)')
|
||||
parser.add_option('--ask-su-pass', default=False, dest='ask_su_pass', action='store_true',
|
||||
help='ask for su password (deprecated, use become)')
|
||||
parser.add_option("-s", "--sudo", default=constants.DEFAULT_SUDO, action="store_true", dest='sudo',
|
||||
help="run operations with sudo (nopasswd) (deprecated, use become)")
|
||||
parser.add_option('-U', '--sudo-user', dest='sudo_user', default=None,
|
||||
help='desired sudo user (default=root)') # Can't default to root because we need to detect when this option was given
|
||||
parser.add_option('-u', '--user', default=constants.DEFAULT_REMOTE_USER,
|
||||
dest='remote_user', help='connect as this user (default=%s)' % constants.DEFAULT_REMOTE_USER)
|
||||
help='desired sudo user (default=root) (deprecated, use become)')
|
||||
parser.add_option('-S', '--su', default=constants.DEFAULT_SU, action='store_true',
|
||||
help='run operations with su (deprecated, use become)')
|
||||
parser.add_option('-R', '--su-user', default=None,
|
||||
help='run operations with su as this user (default=%s) (deprecated, use become)' % constants.DEFAULT_SU_USER)
|
||||
|
||||
# consolidated privilege escalation (become)
|
||||
parser.add_option("-b", "--become", default=constants.DEFAULT_BECOME, action="store_true", dest='become',
|
||||
help="run operations with become (nopasswd implied)")
|
||||
parser.add_option('--become-method', dest='become_method', default=constants.DEFAULT_BECOME_METHOD, type='string',
|
||||
help="privilege escalation method to use (default=%s), valid choices: [ %s ]" % (constants.DEFAULT_BECOME_METHOD, ' | '.join(constants.BECOME_METHODS)))
|
||||
parser.add_option('--become-user', default=None, dest='become_user', type='string',
|
||||
help='run operations as this user (default=%s)' % constants.DEFAULT_BECOME_USER)
|
||||
parser.add_option('--ask-become-pass', default=False, dest='become_ask_pass', action='store_true',
|
||||
help='ask for privilege escalation password')
|
||||
|
||||
parser.add_option('-S', '--su', default=constants.DEFAULT_SU,
|
||||
action='store_true', help='run operations with su')
|
||||
parser.add_option('-R', '--su-user', help='run operations with su as this '
|
||||
'user (default=%s)' % constants.DEFAULT_SU_USER)
|
||||
|
||||
if connect_opts:
|
||||
parser.add_option('-c', '--connection', dest='connection',
|
||||
default=C.DEFAULT_TRANSPORT,
|
||||
help="connection type to use (default=%s)" % C.DEFAULT_TRANSPORT)
|
||||
default=constants.DEFAULT_TRANSPORT,
|
||||
help="connection type to use (default=%s)" % constants.DEFAULT_TRANSPORT)
|
||||
|
||||
if async_opts:
|
||||
parser.add_option('-P', '--poll', default=constants.DEFAULT_POLL_INTERVAL, type='int',
|
||||
@@ -1059,7 +1070,6 @@ def base_parser(constants=C, usage="", output_opts=False, runas_opts=False,
|
||||
help="when changing (small) files and templates, show the differences in those files; works great with --check"
|
||||
)
|
||||
|
||||
|
||||
return parser
|
||||
|
||||
def parse_extra_vars(extra_vars_opts, vault_pass):
|
||||
@@ -1106,41 +1116,58 @@ def ask_vault_passwords(ask_vault_pass=False, ask_new_vault_pass=False, confirm_
|
||||
|
||||
return vault_pass, new_vault_pass
|
||||
|
||||
def ask_passwords(ask_pass=False, ask_sudo_pass=False, ask_su_pass=False, ask_vault_pass=False):
|
||||
def ask_passwords(ask_pass=False, become_ask_pass=False, ask_vault_pass=False, become_method=C.DEFAULT_BECOME_METHOD):
|
||||
sshpass = None
|
||||
sudopass = None
|
||||
supass = None
|
||||
becomepass = None
|
||||
vaultpass = None
|
||||
sudo_prompt = "sudo password: "
|
||||
su_prompt = "su password: "
|
||||
become_prompt = ''
|
||||
|
||||
if ask_pass:
|
||||
sshpass = getpass.getpass(prompt="SSH password: ")
|
||||
become_prompt = "%s password[defaults to SSH password]: " % become_method.upper()
|
||||
if sshpass:
|
||||
sshpass = to_bytes(sshpass, errors='strict', nonstring='simplerepr')
|
||||
sudo_prompt = "sudo password [defaults to SSH password]: "
|
||||
su_prompt = "su password [defaults to SSH password]: "
|
||||
else:
|
||||
become_prompt = "%s password: " % become_method.upper()
|
||||
|
||||
if ask_sudo_pass:
|
||||
sudopass = getpass.getpass(prompt=sudo_prompt)
|
||||
if ask_pass and sudopass == '':
|
||||
sudopass = sshpass
|
||||
if sudopass:
|
||||
sudopass = to_bytes(sudopass, errors='strict', nonstring='simplerepr')
|
||||
|
||||
if ask_su_pass:
|
||||
supass = getpass.getpass(prompt=su_prompt)
|
||||
if ask_pass and supass == '':
|
||||
supass = sshpass
|
||||
if supass:
|
||||
supass = to_bytes(supass, errors='strict', nonstring='simplerepr')
|
||||
if become_ask_pass:
|
||||
becomepass = getpass.getpass(prompt=become_prompt)
|
||||
if ask_pass and becomepass == '':
|
||||
becomepass = sshpass
|
||||
if becomepass:
|
||||
becomepass = to_bytes(becomepass)
|
||||
|
||||
if ask_vault_pass:
|
||||
vaultpass = getpass.getpass(prompt="Vault password: ")
|
||||
if vaultpass:
|
||||
vaultpass = to_bytes(vaultpass, errors='strict', nonstring='simplerepr').strip()
|
||||
|
||||
return (sshpass, sudopass, supass, vaultpass)
|
||||
return (sshpass, becomepass, vaultpass)
|
||||
|
||||
|
||||
def choose_pass_prompt(options):
|
||||
|
||||
if options.ask_su_pass:
|
||||
return 'su'
|
||||
elif options.ask_sudo_pass:
|
||||
return 'sudo'
|
||||
|
||||
return options.become_method
|
||||
|
||||
def normalize_become_options(options):
|
||||
|
||||
options.become_ask_pass = options.become_ask_pass or options.ask_sudo_pass or options.ask_su_pass or C.DEFAULT_BECOME_ASK_PASS
|
||||
options.become_user = options.become_user or options.sudo_user or options.su_user or C.DEFAULT_BECOME_USER
|
||||
|
||||
if options.become:
|
||||
pass
|
||||
elif options.sudo:
|
||||
options.become = True
|
||||
options.become_method = 'sudo'
|
||||
elif options.su:
|
||||
options.become = True
|
||||
options.become_method = 'su'
|
||||
|
||||
|
||||
def do_encrypt(result, encrypt, salt_size=None, salt=None):
|
||||
if PASSLIB_AVAILABLE:
|
||||
@@ -1194,38 +1221,63 @@ def boolean(value):
|
||||
else:
|
||||
return False
|
||||
|
||||
def make_become_cmd(cmd, user, shell, method, flags=None, exe=None):
|
||||
"""
|
||||
helper function for connection plugins to create privilege escalation commands
|
||||
"""
|
||||
|
||||
randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32))
|
||||
success_key = 'BECOME-SUCCESS-%s' % randbits
|
||||
prompt = None
|
||||
becomecmd = None
|
||||
|
||||
shell = shell or '$SHELL'
|
||||
|
||||
if method == 'sudo':
|
||||
# Rather than detect if sudo wants a password this time, -k makes sudo always ask for
|
||||
# a password if one is required. Passing a quoted compound command to sudo (or sudo -s)
|
||||
# directly doesn't work, so we shellquote it with pipes.quote() and pass the quoted
|
||||
# string to the user's shell. We loop reading output until we see the randomly-generated
|
||||
# sudo prompt set with the -p option.
|
||||
prompt = '[sudo via ansible, key=%s] password: ' % randbits
|
||||
exe = exe or C.DEFAULT_SUDO_EXE
|
||||
becomecmd = '%s -k && %s %s -S -p "%s" -u %s %s -c "%s"' % \
|
||||
(exe, exe, flags or C.DEFAULT_SUDO_FLAGS, prompt, user, shell, 'echo %s; %s' % (success_key, cmd))
|
||||
|
||||
elif method == 'su':
|
||||
exe = exe or C.DEFAULT_SU_EXE
|
||||
flags = flags or C.DEFAULT_SU_FLAGS
|
||||
becomecmd = '%s %s %s -c "%s -c %s"' % (exe, flags, user, shell, pipes.quote('echo %s; %s' % (success_key, cmd)))
|
||||
|
||||
elif method == 'pbrun':
|
||||
exe = exe or 'pbrun'
|
||||
flags = flags or ''
|
||||
becomecmd = '%s -b -l %s -u %s "%s"' % (exe, flags, user, 'echo %s; %s' % (success_key,cmd))
|
||||
|
||||
elif method == 'pfexec':
|
||||
exe = exe or 'pfexec'
|
||||
flags = flags or ''
|
||||
# No user as it uses it's own exec_attr to figure it out
|
||||
becomecmd = '%s %s "%s"' % (exe, flags, 'echo %s; %s' % (success_key,cmd))
|
||||
|
||||
if becomecmd is None:
|
||||
raise errors.AnsibleError("Privilege escalation method not found: %s" % method)
|
||||
|
||||
return (('%s -c ' % shell) + pipes.quote(becomecmd), prompt, success_key)
|
||||
|
||||
|
||||
def make_sudo_cmd(sudo_exe, sudo_user, executable, cmd):
|
||||
"""
|
||||
helper function for connection plugins to create sudo commands
|
||||
"""
|
||||
# Rather than detect if sudo wants a password this time, -k makes
|
||||
# sudo always ask for a password if one is required.
|
||||
# Passing a quoted compound command to sudo (or sudo -s)
|
||||
# directly doesn't work, so we shellquote it with pipes.quote()
|
||||
# and pass the quoted string to the user's shell. We loop reading
|
||||
# output until we see the randomly-generated sudo prompt set with
|
||||
# the -p option.
|
||||
randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32))
|
||||
prompt = '[sudo via ansible, key=%s] password: ' % randbits
|
||||
success_key = 'SUDO-SUCCESS-%s' % randbits
|
||||
sudocmd = '%s -k && %s %s -S -p "%s" -u %s %s -c %s' % (
|
||||
sudo_exe, sudo_exe, C.DEFAULT_SUDO_FLAGS,
|
||||
prompt, sudo_user, executable or '$SHELL', pipes.quote('echo %s; %s' % (success_key, cmd)))
|
||||
return ('/bin/sh -c ' + pipes.quote(sudocmd), prompt, success_key)
|
||||
return make_become_cmd(cmd, sudo_user, executable, 'sudo', C.DEFAULT_SUDO_FLAGS, sudo_exe)
|
||||
|
||||
|
||||
def make_su_cmd(su_user, executable, cmd):
|
||||
"""
|
||||
Helper function for connection plugins to create direct su commands
|
||||
"""
|
||||
# TODO: work on this function
|
||||
randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32))
|
||||
success_key = 'SUDO-SUCCESS-%s' % randbits
|
||||
sudocmd = '%s %s %s -c "%s -c %s"' % (
|
||||
C.DEFAULT_SU_EXE, C.DEFAULT_SU_FLAGS, su_user, executable or '$SHELL',
|
||||
pipes.quote('echo %s; %s' % (success_key, cmd))
|
||||
)
|
||||
return ('/bin/sh -c ' + pipes.quote(sudocmd), None, success_key)
|
||||
return make_become_cmd(cmd, su_user, executable, 'su', C.DEFAULT_SU_FLAGS, C.DEFAULT_SU_EXE)
|
||||
|
||||
def get_diff(diff):
|
||||
# called by --diff usage in playbook and runner via callbacks
|
||||
@@ -1577,9 +1629,9 @@ def update_hash(hash, key, new_value):
|
||||
hash[key] = value
|
||||
|
||||
def censor_unlogged_data(data):
|
||||
'''
|
||||
'''
|
||||
used when the no_log: True attribute is passed to a task to keep data from a callback.
|
||||
NOT intended to prevent variable registration, but only things from showing up on
|
||||
NOT intended to prevent variable registration, but only things from showing up on
|
||||
screen
|
||||
'''
|
||||
new_data = {}
|
||||
@@ -1589,5 +1641,19 @@ def censor_unlogged_data(data):
|
||||
new_data['censored'] = 'results hidden due to no_log parameter'
|
||||
return new_data
|
||||
|
||||
def check_mutually_exclusive_privilege(options, parser):
|
||||
|
||||
# privilege escalation command line arguments need to be mutually exclusive
|
||||
if (options.su or options.su_user or options.ask_su_pass) and \
|
||||
(options.sudo or options.sudo_user or options.ask_sudo_pass) or \
|
||||
(options.su or options.su_user or options.ask_su_pass) and \
|
||||
(options.become or options.become_user or options.become_ask_pass) or \
|
||||
(options.sudo or options.sudo_user or options.ask_sudo_pass) and \
|
||||
(options.become or options.become_user or options.become_ask_pass):
|
||||
|
||||
parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
|
||||
"and su arguments ('-su', '--su-user', and '--ask-su-pass') "
|
||||
"and become arguments ('--become', '--become-user', and '--ask-become-pass')"
|
||||
" are exclusive of each other")
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user