Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelogs/fragments/334_synchronize_using_become_pass.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
minor_changes:
- synchronize - elevating privileges now works even when `sudo` requires entering the `become_pass`
2 changes: 1 addition & 1 deletion docs/ansible.posix.synchronize_module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ Notes

- The user and permissions for the synchronize `dest` are those of the `remote_user` on the destination host or the `become_user` if `become=yes` is active.
- In Ansible 2.0 a bug in the synchronize module made become occur on the "local host". This was fixed in Ansible 2.0.1.
- Currently, synchronize is limited to elevating permissions via passwordless sudo. This is because rsync itself is connecting to the remote machine and rsync doesn't give us a way to pass sudo credentials in.
- Currently, synchronize is limited to elevating permissions via sudo. This now even works when password entry is required.
- Currently there are only a few connection types which support synchronize (ssh, paramiko, local, and docker) because a sync strategy has been determined for those connection types. Note that the connection for these must not need a password as rsync itself is making the connection and rsync does not provide us a way to pass a password to the connection.
- Expect that dest=~/x will be ~<remote_user>/x even if using sudo.
- Inspect the verbose output to validate the destination user/host/path are what was expected.
Expand Down
18 changes: 16 additions & 2 deletions plugins/action/synchronize.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,10 +383,24 @@ def run(self, tmp=None, task_vars=None):
# If no rsync_path is set, become was originally set, and dest is
# remote then add privilege escalation here.
if self._play_context.become_method == 'sudo':

# if become is set, we can either rely on passwordless sudo or pass the password
if self._play_context.become_pass is None:
rsync_path = 'sudo '
else:
# pass the become password using the environment so that the synchronize module
# can wrap ssh on the host with a shell script that injects the password into
# stdin, allowing for `sudo -S` on the target machine to retrieve the password
if hasattr(self._task, 'environment'):
self._task.environment = []
self._task.environment.append({'BECOME_PASS': self._play_context.become_pass})
_tmp_args['_ssh_wrapper'] = True
rsync_path = 'sudo -S '

if self._play_context.become_user:
rsync_path = 'sudo -u %s rsync' % self._play_context.become_user
rsync_path += '-u %s rsync' % self._play_context.become_user
else:
rsync_path = 'sudo rsync'
rsync_path += 'rsync'
# TODO: have to add in the rest of the become methods here

# We cannot use privilege escalation on the machine running the
Expand Down
11 changes: 10 additions & 1 deletion plugins/modules/synchronize.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@
delegate_to host when delegate_to is used).
- The user and permissions for the synchronize `dest` are those of the `remote_user` on the destination host or the `become_user` if `become=yes` is active.
- In Ansible 2.0 a bug in the synchronize module made become occur on the "local host". This was fixed in Ansible 2.0.1.
- Currently, synchronize is limited to elevating permissions via passwordless sudo. This is because rsync itself is connecting to the remote machine
- Currently, synchronize is limited to elevating permissions via sudo. This now even works when password entry is required.
and rsync doesn't give us a way to pass sudo credentials in.
- Currently there are only a few connection types which support synchronize (ssh, paramiko, local, and docker) because a sync strategy has been
determined for those connection types. Note that the connection for these must not need a password as rsync itself is making the connection and
Expand Down Expand Up @@ -414,6 +414,7 @@ def main():
rsync_opts=dict(type='list', default=[], elements='str'),
ssh_args=dict(type='str'),
ssh_connection_multiplexing=dict(type='bool', default=False),
_ssh_wrapper=dict(type='bool', default=False),
partial=dict(type='bool', default=False),
verify_host=dict(type='bool', default=False),
delay_updates=dict(type='bool', default=True),
Expand Down Expand Up @@ -456,6 +457,7 @@ def main():
rsync_opts = module.params['rsync_opts']
ssh_args = module.params['ssh_args']
ssh_connection_multiplexing = module.params['ssh_connection_multiplexing']
ssh_wrapper = module.params['_ssh_wrapper']
verify_host = module.params['verify_host']
link_dest = module.params['link_dest']
delay_updates = module.params['delay_updates']
Expand Down Expand Up @@ -550,6 +552,13 @@ def main():
ssh_cmd_str = ' '.join(shlex_quote(arg) for arg in ssh_cmd)
if ssh_args:
ssh_cmd_str += ' %s' % ssh_args
# When `become: yes` is set but the account on the target requires a password for sudo, we have to supply
# it from the host side by wrapping the remote shell and inserting the password into stdin.
# In the ActionPlugin, the password is assigned to the BECOME_PASS environment variable, so we will not have
# to make it visible if anyone logs the command issued by ansible.
# Adapted from https://askubuntu.com/a/1263657
if ssh_wrapper:
ssh_cmd_str = '/bin/sh -c "{ echo $BECOME_PASS; cat - ; } | ' + ssh_cmd_str + ' $0 $* &"'
cmd.append('--rsh=%s' % shlex_quote(ssh_cmd_str))

if rsync_path:
Expand Down