How to make Ansible use password if key was rejected?
Solution 1:
Here's what I do if ansible_user
is different for the 'first run' of the playbook (for example if you only have a root
user and you're going to set up a new user with an SSH key):
- Store the password in
ansible_pass
as you would if you were using password logins (remember to use Vault) this should be the password of the user you're using for the 'first run' of the playbook. - Set
ansible_user
to the username of the user you wish to use after the first run when you have users set up correctly on the server. - Set a variable of
ansible_user_first_run
to the user you're going to use for the 'first run' of the playbook, for exampleroot
. - Use a local command to attempt to connect to the server with the correct SSH key, using
ignore_errors
andchanged_when: False
- If that fails, update
ansible_user
to the value ofansible_user_first_run
Here's the code:
---
- name: Check if connection is possible
command: ssh -o User={{ ansible_user }} -o ConnectTimeout=10 -o PreferredAuthentications=publickey -o PubkeyAuthentication=yes {{ inventory_hostname }} echo "Worked"
register: result
connection: local
ignore_errors: yes
changed_when: False
- name: If no connection, change user_name
connection: local
set_fact:
ansible_user: "{{ ansible_user_first_run }}"
when: result|failed
Note: It's worth setting up transport = ssh
as paramiko can unexpectedly fail to login to the server in some configurations (e.g. when the server is set up not to accept passwords, and you're trying first with a key then a password... weird!) Also ssh transport is faster, so it's worth it anyway.
Further note: If you're using this method, you need to specify gather_facts: false
in your playbook definition file so that the setup / fact gathering tasks aren't run automatically before you get to the stage of testing passwords. If you need any of the ansible facts, you will need to explicitly call setup
in your role before accessing any of the data normally available in such places as ansible_devices
, etc. One good way of doing this is to call setup
with a when
clause that checks to see if the fact you are using is empty or not before you call it in your role.
Solution 2:
You can use --ask-pass
when running ansible-playbook.
For other tasks you asked, it is achievable by various means, eg, copy module.
Disabling root login also can be done eg. by templating sshd_conf
or inserting line in conf file.
Solution 3:
You could try the PreferredAuthentications
option, setting it to publickey,password
. The default includes these in this order, along with other options, so ansible is presumably setting this. Adding it via -o
or the client ssh_config
may prevent this.
You may be able to use a wrapper script. For example, with this in key_or_password.sh
and a pass.sh
that gives the password, running bash key_or_password.sh root@host
will try a publickey followed by a non-interactive password login.
export DISPLAY=dummy:0
export SSH_ASKPASS=$PWD/pass.sh
exec setsid ssh -v -o 'PreferredAuthentications publickey,password' "$@"
The log indicates which method succeeded with, e.g.
debug1: Authentication succeeded (publickey).