I’m using ansible to manage my homelab, and I’m using vault to store a dictionary with the passwords for my ansible user on each host. I use private keys to SSH into the hosts, but I use the passwords as become passwords, as I prefer not to allow my ansible user to sudo without a password (just in case)
I want to create a playbook that will rotate passwords on all my hosts. For each host I will use ansible.builtin.password to generate a random secure password, then ansible.builtin.user to change the password on the hosts, but I’m missing a way to save that new password on vault
Is there a way to get this done? if there isn’t, is there a specific reason not to support this?
Usually, a rotating password is handled by some centralized user management system, and similarly stored in a centralized secrets management solution. In those scenarios, Ansible would be configured to pull secrets from the centralized secrets vault dynamically so that rotated secrets are always uptodate on Ansible’s end.
That said, there is a way to do this strictly with Ansible though, but you’ll have to be careful because it can lock you out of your users if you lose track of the password and can’t become root anymore. Before you get started, I suggest making a password/passphrase protected private ssh key specifically for the root account and distribute the public key to all of your hosts for root. Then configure sshd on each host to allow root authentication with private key. I.e. set PermitRootLogin prohibit-password in sshd_config.
Then, you’re going to want to use the ansible.builtin.vault filter to encrypt your rotated passwords, and save them as host_vars in your inventory.
Something like:
---
- name: Rotate Inventory Passwords
hosts: all
gather_facts: false
vars:
vault_password: !vault | #vaulted vault password, seems redundant but you need to a way to expose the password securely
salt: '{{2**256|random(seed=inventory_hostname)}}'
new_password: "{{ lookup('ansible.builtin.password', '/dev/null') }}"
tasks:
- name: Save password to inventory
lineinfile:
path: "~/inventory/host_vars/{{ inventory_hostname }}"
regexp: ^ansible_password
line: "ansible_password={{ new_password | ansible.builtin.vault(vault_password) }}"
delegate_to: localhost
- name: Update remote hosts
ansible.builtin.user:
name: ansible_user
password: "{{ new_password | password_hash('sha512', salt=salt) }}"
You would want to run this play directly as root and to update both the inventory and the user(s) on the inventory_hosts themselves, so it doesn’t matter if Ansible and your hosts are perfectly synchronized with the password before the play.
# load root's private key into ssh-agent
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa_root # enter passphrase
# now run play
ansible-playbook rotate_passwords.yml -u root --private-key ~/.ssh/id_rsa_root
Note:
I have not tested this, and I don’t know your inventory structure, so definitely update the first task to make sure the password is getting saved to the correct ./host_vars/ location. I’m also assuming ini style vars file, but you could go with yaml or something else if you prefer.