--- title: "Updating passwords with Ansible" date: 2019-01-19T00:00:00+01:00 draft: false share: false --- I've recently migrated from [KeePassXC](https://keepassxc.org/) to [Bitwarden_RS](https://github.com/dani-garcia/bitwarden_rs) (which I highly recommend, by the way) to manage my passwords. I figured it was an opportunity to update passwords I hadn't changed in... *years*. My Linux users' passwords were among those. Instead of updating them manually on each machine, I thought there might be a way to do so with Ansible, and since it turns out there is, I thought I might as well share it here! **Please be careful when it comes to password modification automation. You might end up locking yourself out of your servers.** ## Generating password hashes First thing first, we'll need to generate `passwd` compatible password hashes for our users. In this example, I'll generate a hash for the *P@ssw0rd* password : ``` mkpasswd -m sha-512 Password: $6$wAVPV.Coc$o3FNxs9EPgXF54hv1BeKtfoMnLwE5VATL71jlHQHeVyCaevnnxfSp/x1UbJ00F3qlyyfUAmscuGXImoHmXBFa. ``` You might notice you'll get a different result if you run the same command again : ``` mkpasswd -m sha-512 Password: $6$iFBWJD3300m$CYZJRSfZ4scHYNI9ggqe8WYef7Qym2Oi5ycgb64VsbU3.WM1GoJYlh1sawENTD7nrXVCthvs8LRPw1CVjzkP71 ``` That's because, unless you specify it, the salt used by `mkpasswd` to encrypt the password is random. You can choose a salt with the `-S` parameter if you want : ``` mkpasswd -m sha-512 -S hmmmsalt Password: $6$hmmmsalt$RwZR2r9W5cSv5bVgeSFPX0rJiovWOD5kMDFey1xPR6JtasqQZqHTiuW5JoQ.0VCW6oNHJlgOYJ.auhl82gfX8/ mkpasswd -m sha-512 -S hmmmsalt Password: $6$hmmmsalt$RwZR2r9W5cSv5bVgeSFPX0rJiovWOD5kMDFey1xPR6JtasqQZqHTiuW5JoQ.0VCW6oNHJlgOYJ.auhl82gfX8/ captainark@heimdall ~ % ``` ## Ansible vault Passwords are *secrets*. Even if there are hashed, you don't want them to be publicly accessible. If, like me, your Ansible configuration is in a git repository, you should not commit them in an unencrypted file. Ansible offers a way to encrypt a file containing variables with the `ansible-vault` command. We first have to create a vault : ``` cd /your/ansible/project mkdir -p group_vars/all/ ansible-vault create group_vars/all/vault.yml New Vault password: Confirm New Vault password: ``` As you can see, the command will ask you to provide a password for the vault. It will then open the file in your `$EDITOR` (that'll be `vim` if you're a cool kid). The file is like any Ansible vault file. I'll create a variable for the `root` and `captainark` users in my case : ```yaml vault_captainarkpwd: "$6$hmmmsalt$RwZR2r9W5cSv5bVgeSFPX0rJiovWOD5kMDFey1xPR6JtasqQZqHTiuW5JoQ.0VCW6oNHJlgOYJ.auhl82gfX8/" vault_rootpwd: "$6$hmmmsalt$RwZR2r9W5cSv5bVgeSFPX0rJiovWOD5kMDFey1xPR6JtasqQZqHTiuW5JoQ.0VCW6oNHJlgOYJ.auhl82gfX8/" ``` An [Ansible best practice](https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#variables-and-vaults) is to prepend all variables that are stored in a vault with the `vault_` prefix. It makes where they are stored clear when reading through a playbook. Once saved, if you try to `cat` the file, you won't be able to see its actual content : ``` cat group_vars/all/vault.yml $ANSIBLE_VAULT;1.1;AES256 32393530376537323233633636316330373136316265316662646437393533376135666232656366 3335623863333865666133666634633233616531636634370a663965356466383039326262313066 62633434396465313666333032663130343434326665386333323733633062613832653530393761 6333653338393231640a623938616634626462653965613766313335386136333362313033363735 64313662623039363365326639633231306335366432306361613837656364356464373837656565 61636538376262333762376235306337303531386638643632316361323037393230366537393132 35373861373863613666303531353737373130353330643535353238633665653236633130653064 39346330326566633262613535386633613565633566623934613066613238353739386133346535 64643762333462653966633363653439633037373161316663646261663764393332653732656335 61373462666336343533333162663637656236333739633065623939323937663137376431346231 38653338386539383663613230656165313566363733396134386366626430313235343264643938 64306163353437366362616166666565316663366163346565313436343537366330363932303038 37653138643165353138393466343063666535313933663066633832353331643838356539303533 61626237356538353261326136613239336662346337363331393037623863623433336432353461 32356136316339623139346330333235363331653634373836333730653436636563323134616337 36643433356133376138 ``` To view the variables in the file, you can use the `ansible-vault view` command : ```yaml ansible-vault view group_vars/all/vault.yml Vault password: vault_captainarkpwd: "$6$hmmmsalt$RwZR2r9W5cSv5bVgeSFPX0rJiovWOD5kMDFey1xPR6JtasqQZqHTiuW5JoQ.0VCW6oNHJlgOYJ.auhl82gfX8/" vault_rootpwd: "$6$hmmmsalt$RwZR2r9W5cSv5bVgeSFPX0rJiovWOD5kMDFey1xPR6JtasqQZqHTiuW5JoQ.0VCW6oNHJlgOYJ.auhl82gfX8/" ``` To edit the vault's content, you can use the `ansible-vault edit group_vars/all/vault.yml` command. ## The playbook Now that our vault is ready, all that's left is to run the following playbook : ```yaml --- - hosts: all become: yes become_method: sudo tasks: - name: PASSWORDS | Check if the captainark user exists shell: id -u captainark register: captainark_exists ignore_errors: true - name: PASSWORDS | Update captainark password user: name: captainark password: "{{ vault_captainarkpwd }}" update_password: always when: captainark_exists.rc == 0 - name: PASSWORDS | Create captainark user user: name: captainark password: "{{ vault_captainarkpwd }}" shell: /usr/bin/zsh uid: 1000 groups: adm,sudo,apps when: captainark_exists.rc != 0 - name: PASSWORDS | Update root password user: name: root password: "{{ vault_rootpwd }}" update_password: always ``` I've called this playbook `passwords.yml`. To run it, I simply execute the following command : ``` ansible-playbook passwords.yml --ask-vault-pass ``` The command asks for the vault password to decrypt its contents. The playbook will first check if the `captainark` user exists, and it will update its password if it does. If it doesn't, the user will be created with the defined options. Since the `root` user should always exist, the playbook changes its password without checking. **N.B. :** If you've decided to store your vault somewhere else, you might need to add a task at the beginning of the playbook to load it, like so : ```yaml - name: PASSWORDS | Load the vault include_vars: /path/to/your/vault.yml ``` ## Conclusion That's all! As always, I hope someone finds this article useful! If you do, please let me know in the comments here, on [Twitter](https://twitter.com/captainark) or on the [Fediverse](https://social.captainark.net/users/captainark/)! Also, if you have any questions, feel free to hit me up on my [Rocket.Chat](https://chat.captainark.net) instance! Hopefully I'll write about it in the future!