# Updating passwords with Ansible

19 January 2019

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!

## Debian repos over HTTPS