Generating Random Passwords

I created some accounts on a remote machine using ansible and the authorized_key: module. Then I attempted to ssh in as the new account. This failed. I logged into the machine as root and established that the accounts had been correctly created and the keys correctly added, complete with correct permissions (.ssh/ = 600, authorized_keys = 400). Yes. Next stop, /var/log/auth_log, where I found "user xxxx not allowed because account is locked". It was (of course) Stack Overflow that answered this one, although I should have thought of it: accounts are created disabled until a password is added, even though the machine only allows SSH-key login. I can of course set a password with ansible, but that presents a problem: our ansible scripts are in a git repo, and storing passwords in a git repo is a VERY BAD IDEA(TM). (Even with SSH key login only this is a security risk.) I had a bit of a brainwave, and thought "get it from /dev/random." This allows you to have a random, non-repeatable password - you don't know what it is, but you also don't care as you're using an SSH key. This turns out to be slightly trickier than I initially thought as /dev/random and /dev/urandom spew 8-bit bytes instead of 7-bit ASCII characters, so a bit of filtering is called for:

export LC_CTYPE=C ; head -c 500 /dev/urandom | tr -dc 'a-zA-Z0-9~!@#$%^&*(){}[]\|/-_=+?/' | head -c 16

The LC_CTYPE is necessary because (on the Mac I initially worked on this) it assumes a UTF-8 character encoding, and tr gets upset when it doesn't get it. So we sidestep that. (This isn't a problem on the Linux system I tested it on: don't bother with LC_CTYPE.) head is used to lop off the first 500 characters of the string. tr then does the work of d deleting the c COMPLEMENT of the group of characters provided. Normally, tr -d '<set of chars>' deletes what's in the set of characters, this time we want it to delete what's NOT in the set. Finally, chop the remainder down to a password-appropriate length.

/dev/random is a complete bust on my primary Linux system: the man page explains that it won't provide output unless it's of high enough quality ... Well, being unable to provide 500 characters in 90 seconds is a bit of an impediment to the script. Which is why I'm using the lower quality /dev/urandom.

In the end a better solution was found for the ansible script, but I found this interesting enough to save.