From ea2ec6237aa97e6c434ccf4af124f0632747ef06 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 30 Oct 2013 13:18:35 -0500 Subject: [PATCH] Add ansible_sudo_pass hostvar support --- docsite/rst/intro_inventory.rst | 2 ++ lib/ansible/runner/__init__.py | 2 ++ lib/ansible/runner/connection_plugins/accelerate.py | 2 +- lib/ansible/runner/connection_plugins/local.py | 7 ++++--- lib/ansible/runner/connection_plugins/paramiko_ssh.py | 7 ++++--- lib/ansible/runner/connection_plugins/ssh.py | 7 ++++--- lib/ansible/utils/__init__.py | 5 +++-- 7 files changed, 20 insertions(+), 12 deletions(-) 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 ee5a4d0031d..9c4ed238769 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -577,6 +577,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 @@ -616,6 +617,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 91adf7bb113..95433648c0a 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 41033bd19de..594db88af09 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -844,10 +844,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))