Is there a way for a playbook to edit vault?

Hi all, ansible n00b here

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?

Thanks

Joso

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.

1 Like

Will give that one a shot. Thanks :grin: