apg
is the "Automated Password Generator," a piece of software to generate random passwords. Perhaps not widely known, but undoubtedly used by a lot of systems administrators. I wrote a replacement Bash script so that I could be guaranteed of having an equivalent installed as part of our local Ansible repository rather than relying on other Ansible users to know to install a relatively obscure piece of software that may not even be available for Mac (I know it's widely available on Linux). I also like that I know exactly how this one works: it's simple, and even if you didn't write it you can read and understand the code.
Please email me if you believe there's something wrong with this from a security perspective.
Here's how it works: /dev/urandom spews out a steady stream of random bytes. Not all of these fit in the English alphabet, so we filter out characters that don't match, and then crop the remainder to the desired password length.
This is, of course, more useful in the age of password managers and/or browsers that memorize your password.
(This script has a strong English alphabet bias.)
#!/bin/bash
#
# Filename: apggo
# Purpose: Poor-man's 'apg' - a script to generate random passwords.
MINLENGTH=8
MAXLENGTH=100
SIMPLE='A-Za-z0-9'
NONSIMPLE='A-Za-z0-9!@$%^&*(){}[]=+-_/?\|~`'
function help() {
echo "Usage: $(basename "${0}") [-h] [-s] [-l <len>]"
echo " Generates a pseudo-random password of length <len>."
echo " For WORD-BASED PASSWORDS, see 'randwords' script."
echo ""
echo " '-l' for length, requires an integer value."
echo " '-h' for help."
echo " '-s' is 'simple' passwords without special characters."
}
function passgen() {
length=${1}
if (( length < MINLENGTH ))
then
echo "Password minimum length of ${MINLENGTH} enforced."
exit 4
fi
if (( length > MAXLENGTH ))
then
echo "Password maximum length of ${MAXLENGTH} enforced."
exit 4
fi
# These settings aren't needed on Linux but often are on Mac:
export LC_CTYPE=C
export LC_ALL=C
# The 'tr' string below represents all ALLOWED characters.
dd if=/dev/urandom bs=1 count=$(( 8 * length )) 2>/dev/null \
| tr -dc "${SUBS}" \
| head -c "${length}"
echo
}
SUBS="${NONSIMPLE}"
while getopts :hl:s opt ; do
case $opt in
h) help
exit 0
;;
l) # Check we have an integer (
# https://stackoverflow.com/questions/2210349/test-whether-string-is-a-valid-integer#18620446
# )
count="$OPTARG"
if ! [[ ${count} =~ ^-?[0-9]+$ ]]
then
echo "Parameter must be an integer."
help
exit 2
fi
;;
s) SUBS="${SIMPLE}"
;;
:) echo "Option -$OPTARG requires an argument." >&2
help
exit 1
;;
*) echo "unknown option ... "
help
exit 1
;;
esac
done
if [ -n "${count}" ]
then
passgen "${count}"
else
echo "Please set a value for password length."
help
exit 1
fi
Bibliography
- Generating Random Passwords - my blog entry about using /dev/urandom to generate passwords that eventually led to this script
- (what the following two say is "/dev/urandom is a good source of randomness")
- https://unix.stackexchange.com/questions/324209/when-to-use-dev-random-vs-dev-urandom
- https://www.2uo.de/myths-about-urandom