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:
Brian Coca
2014-11-24 16:36:31 -05:00
parent 17c710e713
commit 5f6db0e164
45 changed files with 841 additions and 472 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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(

View File

@@ -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):

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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':

View File

@@ -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)

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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:

View File

@@ -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

View File

@@ -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)

View File

@@ -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]

View File

@@ -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. " +

View File

@@ -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])

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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))

View File

@@ -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

View File

@@ -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:

View File

@@ -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)

View File

@@ -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")