The technical task is quite simple - to make automatic password rotation. Instead of storing it somewhere, delivering it somehow - you can greatly simplify it and generate it using an algorithm.

There are many benefits from this:

  • no need to think about how to generate and rotate passwords (especially for new or dynamic hosts)
  • the frequency of changing the password depends entirely on the formula
  • predictability for history
  • the raw password is not stored anywhere

What I came to:

  • I don’t need unique passwords for each host.
  • hosts are divided into groups and each group has its own seed (generated by: openssl rand -base64 24)
  • the algorithm is based on the username, seed and date
  • the date is chosen based on the year and week number (i.e. the password will change every Sunday)
  • the hash is cut to 15 characters. This is done specifically for several reasons:
    • so that it can be quickly entered into the console when something goes wrong
    • a similar algorithm is used to update passwords in IPMI (most have a 15 character limit)

The saltstack formula is as follows:

{% set password_seed = salt['pillar.get']('host:password:seed') %}
{% set curtime = None | strftime('%Y%V') %}
{% set password_plain = salt['hashutil.md5_digest']('-'.join(['root', password_seed, curtime]))[:15] %}

{{ tpldot }}.user.root:
  user.present:
    - name: root
    - password: {{ password_plain }}
    - hash_password: True

Let’s show the state:

root@bastion01:~# salt-call state.show_sls users.root --out yaml
local:
  users.user.root:
    user:
    - name: root
    - password: c8f867fcb4047cf
    - hash_password: true
    - present
    - order: 10000
    __sls__: users.root
    __env__: base

For completeness, let’s show how to get this password via python:

#!/usr/bin/env python3

from datetime import datetime
import hashlib

SEED = '__SECRET__'
date = datetime.now().strftime("%Y%V")

plain = f'root-{SEED}-{date}'
passwordHash = hashlib.md5(plain.encode()).hexdigest()[:15]

print('password:', passwordHash)

p.s. I have password authorization over the network disabled, and the only way to get into the machine via password is through the console (IPMI).

p.s.s. In production for over 2 years. Works on 1000+ servers and is divided into 30+ host groups (each group has its own seed)