diff --git a/docsite/rst/intro_inventory.rst b/docsite/rst/intro_inventory.rst index 1545cf8bcf2..3812ad35b8c 100644 --- a/docsite/rst/intro_inventory.rst +++ b/docsite/rst/intro_inventory.rst @@ -190,6 +190,8 @@ mentioned:: The default ssh user name to use. ansible_ssh_pass The ssh password to use (this is insecure, we strongly recommend using --ask-pass or SSH keys) + ansible_sudo_pass + The sudo password to use (this is insecure, we strongly recommend using --ask-pass or SSH keys) ansible_connection Connection type of the host. Candidates are local, ssh or paramiko. The default is paramiko before Ansible 1.2, and 'smart' afterwards which detects whether usage of 'ssh' would be feasible based on whether ControlPersist is supported. ansible_ssh_private_key_file diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 202c31d5afc..eebb5349c83 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -585,6 +585,7 @@ class Runner(object): actual_pass = inject.get('ansible_ssh_pass', self.remote_pass) actual_transport = inject.get('ansible_connection', self.transport) actual_private_key_file = inject.get('ansible_ssh_private_key_file', self.private_key_file) + self.sudo_pass = inject.get('ansible_sudo_pass', self.sudo_pass) if self.accelerate and actual_transport != 'local': #Fix to get the inventory name of the host to accelerate plugin @@ -624,6 +625,7 @@ class Runner(object): actual_pass = delegate_info.get('ansible_ssh_pass', actual_pass) actual_private_key_file = delegate_info.get('ansible_ssh_private_key_file', self.private_key_file) actual_transport = delegate_info.get('ansible_connection', self.transport) + self.sudo_pass = delegate_info.get('ansible_sudo_pass', self.sudo_pass) for i in delegate_info: if i.startswith("ansible_") and i.endswith("_interpreter"): inject[i] = delegate_info[i] diff --git a/lib/ansible/runner/connection_plugins/accelerate.py b/lib/ansible/runner/connection_plugins/accelerate.py index 89f6a1e1048..39e19e271c5 100644 --- a/lib/ansible/runner/connection_plugins/accelerate.py +++ b/lib/ansible/runner/connection_plugins/accelerate.py @@ -165,7 +165,7 @@ class Connection(object): executable = constants.DEFAULT_EXECUTABLE if self.runner.sudo and sudoable and sudo_user: - cmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) + cmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC COMMAND %s" % cmd) diff --git a/lib/ansible/runner/connection_plugins/local.py b/lib/ansible/runner/connection_plugins/local.py index c7c933c88c1..d62f2e4c06c 100644 --- a/lib/ansible/runner/connection_plugins/local.py +++ b/lib/ansible/runner/connection_plugins/local.py @@ -49,7 +49,7 @@ class Connection(object): else: local_cmd = cmd else: - local_cmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) + local_cmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC %s" % (local_cmd), host=self.host) p = subprocess.Popen(local_cmd, shell=isinstance(local_cmd, basestring), @@ -63,7 +63,7 @@ class Connection(object): 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): + while not sudo_output.endswith(prompt) and success_key not in sudo_output: rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout, p.stderr], self.runner.timeout) if p.stdout in rfd: @@ -77,7 +77,8 @@ class Connection(object): stdout, stderr = p.communicate() raise errors.AnsibleError('sudo output closed while waiting for password prompt:\n' + sudo_output) sudo_output += chunk - p.stdin.write(self.runner.sudo_pass + '\n') + if success_key not in sudo_output: + p.stdin.write(self.runner.sudo_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) diff --git a/lib/ansible/runner/connection_plugins/paramiko_ssh.py b/lib/ansible/runner/connection_plugins/paramiko_ssh.py index ab0d3d2f5e6..abf9dafc8f7 100644 --- a/lib/ansible/runner/connection_plugins/paramiko_ssh.py +++ b/lib/ansible/runner/connection_plugins/paramiko_ssh.py @@ -202,13 +202,13 @@ class Connection(object): chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0))) - shcmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) + shcmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC %s" % shcmd, host=self.host) sudo_output = '' try: chan.exec_command(shcmd) if self.runner.sudo_pass: - while not sudo_output.endswith(prompt): + while not sudo_output.endswith(prompt) and success_key not in sudo_output: chunk = chan.recv(bufsize) if not chunk: if 'unknown user' in sudo_output: @@ -218,7 +218,8 @@ class Connection(object): raise errors.AnsibleError('ssh connection ' + 'closed waiting for password prompt') sudo_output += chunk - chan.sendall(self.runner.sudo_pass + '\n') + if success_key not in sudo_output: + chan.sendall(self.runner.sudo_pass + '\n') except socket.timeout: raise errors.AnsibleError('ssh timed out waiting for sudo.\n' + sudo_output) diff --git a/lib/ansible/runner/connection_plugins/ssh.py b/lib/ansible/runner/connection_plugins/ssh.py index 860d4cb8fef..5ee45ba6a9b 100644 --- a/lib/ansible/runner/connection_plugins/ssh.py +++ b/lib/ansible/runner/connection_plugins/ssh.py @@ -165,7 +165,7 @@ class Connection(object): else: ssh_cmd.append(cmd) else: - sudocmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) + sudocmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % ssh_cmd, host=self.host) @@ -198,7 +198,7 @@ class Connection(object): fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK) sudo_output = '' - while not sudo_output.endswith(prompt): + while not sudo_output.endswith(prompt) and success_key not in sudo_output: rfd, wfd, efd = select.select([p.stdout], [], [p.stdout], self.runner.timeout) if p.stdout in rfd: @@ -209,7 +209,8 @@ class Connection(object): else: stdout = p.communicate() raise errors.AnsibleError('ssh connection error waiting for sudo password prompt') - stdin.write(self.runner.sudo_pass + '\n') + if success_key not in sudo_output: + stdin.write(self.runner.sudo_pass + '\n') fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK) # We can't use p.communicate here because the ControlMaster may have stdout open as well diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index fa473601ac3..afa9ed56793 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -849,10 +849,11 @@ def make_sudo_cmd(sudo_user, executable, cmd): # 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' % ( C.DEFAULT_SUDO_EXE, C.DEFAULT_SUDO_EXE, C.DEFAULT_SUDO_FLAGS, - prompt, sudo_user, executable or '$SHELL', pipes.quote(cmd)) - return ('/bin/sh -c ' + pipes.quote(sudocmd), prompt) + prompt, sudo_user, executable or '$SHELL', pipes.quote('echo %s; %s' % (success_key, cmd))) + return ('/bin/sh -c ' + pipes.quote(sudocmd), prompt, success_key) _TO_UNICODE_TYPES = (unicode, type(None))