[ssh] Add new sshpass_prompt option (#68874)
Change: Allows the user to configure sshpass (1.06+) to look for a different substring than the default "assword" that it comes with. Test Plan: Set a custom ssh password prompt on a VM with PAM and tried connecting to it. Without `ansible_sshpass_prompt` set in inventory: experienced hang. With `ansible_sshpass_prompt` in inventory: connected successfully. Tried setting `ansible_sshpass_prompt` with an older `sshpass` in PATH and got a loud error, as expected. Tickets: Fixes #34722, fixes #54743, refs #11565. Signed-off-by: Rick Elrod <rick@elrod.me>
This commit is contained in:
parent
ea04e0048d
commit
304c3e57e8
3 changed files with 68 additions and 3 deletions
|
@ -0,0 +1,2 @@
|
|||
minor_changes:
|
||||
- ssh - connection plugin now supports a new variable ``sshpass_prompt`` which gets passed to ``sshpass`` allowing the user to set a custom substring to search for a password prompt (requires sshpass 1.06+)
|
|
@ -48,6 +48,17 @@ DOCUMENTATION = '''
|
|||
- name: ansible_password
|
||||
- name: ansible_ssh_pass
|
||||
- name: ansible_ssh_password
|
||||
sshpass_prompt:
|
||||
description: Password prompt that sshpass should search for. Supported by sshpass 1.06 and up.
|
||||
default: ''
|
||||
ini:
|
||||
- section: 'ssh_connection'
|
||||
key: 'sshpass_prompt'
|
||||
env:
|
||||
- name: ANSIBLE_SSHPASS_PROMPT
|
||||
vars:
|
||||
- name: ansible_sshpass_prompt
|
||||
version_added: '2.10'
|
||||
ssh_args:
|
||||
description: Arguments to pass to all ssh cli tools
|
||||
default: '-C -o ControlMaster=auto -o ControlPersist=60s'
|
||||
|
@ -331,13 +342,19 @@ def _handle_error(remaining_retries, command, return_tuple, no_log, host, displa
|
|||
raise AnsibleAuthenticationFailure(msg)
|
||||
|
||||
# sshpass returns codes are 1-6. We handle 5 previously, so this catches other scenarios.
|
||||
# No exception is raised, so the connection is retried.
|
||||
# No exception is raised, so the connection is retried - except when attempting to use
|
||||
# sshpass_prompt with an sshpass that won't let us pass -P, in which case we fail loudly.
|
||||
elif return_tuple[0] in [1, 2, 3, 4, 6]:
|
||||
msg = 'sshpass error:'
|
||||
if no_log:
|
||||
msg = '{0} <error censored due to no log>'.format(msg)
|
||||
else:
|
||||
msg = '{0} {1}'.format(msg, to_native(return_tuple[2]).rstrip())
|
||||
details = to_native(return_tuple[2]).rstrip()
|
||||
if "sshpass: invalid option -- 'P'" in details:
|
||||
details = 'Installed sshpass version does not support customized password prompts. ' \
|
||||
'Upgrade sshpass to use sshpass_prompt, or otherwise switch to ssh keys.'
|
||||
raise AnsibleError('{0} {1}'.format(msg, details))
|
||||
msg = '{0} {1}'.format(msg, details)
|
||||
|
||||
if return_tuple[0] == 255:
|
||||
SSH_ERROR = True
|
||||
|
@ -562,6 +579,10 @@ class Connection(ConnectionBase):
|
|||
self.sshpass_pipe = os.pipe()
|
||||
b_command += [b'sshpass', b'-d' + to_bytes(self.sshpass_pipe[0], nonstring='simplerepr', errors='surrogate_or_strict')]
|
||||
|
||||
password_prompt = self.get_option('sshpass_prompt')
|
||||
if password_prompt:
|
||||
b_command += [b'-P', to_bytes(password_prompt, errors='surrogate_or_strict')]
|
||||
|
||||
if binary == 'ssh':
|
||||
b_command += [to_bytes(self._play_context.ssh_executable, errors='surrogate_or_strict')]
|
||||
else:
|
||||
|
|
|
@ -1,6 +1,48 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eux
|
||||
set -ux
|
||||
|
||||
# We skip this whole section if the test node doesn't have sshpass on it.
|
||||
if command -v sshpass > /dev/null; then
|
||||
# Check if our sshpass supports -P
|
||||
sshpass -P foo > /dev/null
|
||||
sshpass_supports_prompt=$?
|
||||
if [[ $sshpass_supports_prompt -eq 0 ]]; then
|
||||
# If the prompt is wrong, we'll end up hanging (due to sshpass hanging).
|
||||
# We should probably do something better here, like timing out in Ansible,
|
||||
# but this has been the behavior for a long time, before we supported custom
|
||||
# password prompts.
|
||||
#
|
||||
# So we search for a custom password prompt that is clearly wrong and call
|
||||
# ansible with timeout. If we time out, our custom prompt was successfully
|
||||
# searched for. It's a weird way of doing things, but it does ensure
|
||||
# that the flag gets passed to sshpass.
|
||||
timeout 5 ansible -m ping \
|
||||
-e ansible_connection=ssh \
|
||||
-e ansible_sshpass_prompt=notThis: \
|
||||
-e ansible_password=foo \
|
||||
-e ansible_user=definitelynotroot \
|
||||
-i test_connection.inventory \
|
||||
ssh-pipelining
|
||||
ret=$?
|
||||
if [[ $ret -ne 124 ]]; then
|
||||
echo "Expected to time out and we did not. Exiting with failure."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
ansible -m ping \
|
||||
-e ansible_connection=ssh \
|
||||
-e ansible_sshpass_prompt=notThis: \
|
||||
-e ansible_password=foo \
|
||||
-e ansible_user=definitelynotroot \
|
||||
-i test_connection.inventory \
|
||||
ssh-pipelining | grep 'customized password prompts'
|
||||
ret=$?
|
||||
[[ $ret -eq 0 ]] || exit $ret
|
||||
fi
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
# temporary work-around for issues due to new scp filename checking
|
||||
# https://github.com/ansible/ansible/issues/52640
|
||||
|
|
Loading…
Reference in a new issue