captainarkdotnet/content/post/2019-01-19-updating-passwor...

6.8 KiB

title date draft share
Updating passwords with Ansible 2019-01-19T00:00:00+01:00 false false

I've recently migrated from KeePassXC to 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 :

vault_captainarkpwd: "$6$hmmmsalt$RwZR2r9W5cSv5bVgeSFPX0rJiovWOD5kMDFey1xPR6JtasqQZqHTiuW5JoQ.0VCW6oNHJlgOYJ.auhl82gfX8/"
vault_rootpwd: "$6$hmmmsalt$RwZR2r9W5cSv5bVgeSFPX0rJiovWOD5kMDFey1xPR6JtasqQZqHTiuW5JoQ.0VCW6oNHJlgOYJ.auhl82gfX8/"

An Ansible best practice 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 :

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 :

---
- 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 :

  - 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 or on the Fediverse!

Also, if you have any questions, feel free to hit me up on my Rocket.Chat instance! Hopefully I'll write about it in the future!