Last week Corey Quinn asked an awesome question on twitter- paraphrasing, what's the best way to use your Github SSH Keys to log into your personal services?
This reminded me of one of my favorite little tricks- OpenSSH's AuthorizedKeysCommand
option. This OpenSSH configuration option allows administrators to specify a command that dynamically generates an authorized_keys
list. This command takes the username as its only argument, gets called before any on system key files (your standard ~/.ssh/authorized_keys
file for instance) are checked, and outputs keys to stdout in the same format. If the script doesn't return a matching key then OpenSSH falls back to its normal lookup.
This trick can be used to pull keys from a variety of places, and Github is no exception. The nice thing about Github is that it publishes the public SSH key of every user in an easy to access place, https://github.com/<username>.keys
(for example, my keys are published at https://github.com/tedivm.keys). We can create a simple bash script to act as our AuthorizedKeysCommand
to pull down those keys and give them go OpenSSH.
#!/usr/bin/env bash
# Explicit "allow" list in case Github users overlap with system users
ALLOWED_USERS="tedivm"
KEY_URL="https://github.com/${1}.keys"
KEY_FILE=$(eval echo ~$1/.ssh/authorized_keys2)
if [[ -z $1 ]]; then
>&2 echo "Username required."
exit 1
fi
if [[ ! " $ALLOWED_USERS " =~ .*\ $1\ .* ]]; then
>&2 echo "User not in allowed list."
exit 1
fi
TMP_AUTHORIZED_KEYS=$(mktemp)
HTTP_STATUS=$(curl -m 5 -s -o $TMP_AUTHORIZED_KEYS -w "%{http_code}" $KEY_URL)
PUBLIC_KEYS=$(cat $TMP_AUTHORIZED_KEYS)
rm $TMP_AUTHORIZED_KEYS
if [ $HTTP_STATUS != "200" ]; then
>&2 echo "Pulling keys from Github failed with status code ${HTTP_STATUS}"
exit 1
else
mkdir -p $(eval echo ~$1/.ssh)
chown $1:$1 $(eval echo ~$1/.ssh)
echo "$PUBLIC_KEYS" > $KEY_FILE
cat $KEY_FILE
fi
There's a couple of important things to note about this script-
- There's a hardcoded set of Github users that are allowed. This way users on Github with overlapping usernames in your system won't be able to log in.
- It assumes your Github username is the same as your system username. You can add some username mapping if that isn't the case.
- We copy the keys onto the system for the user, so that if Github is down the systems are still accessible. The script can be called directly in a cronjob to automate that copy and keep keys fresh.
- The command to get the key returns the HTTP status directly and stores the response in a file, which is the easiest way to get both with one curl command.
- There's a five second timeout on the curl command. OpenSSH will try the system keys if this script fails, but only if the script fails before OpenSSH times out. So curl has to time out long before that would happen.
We'll place that script at /opt/github_authorized_keys.sh
and set it to execute (chmod /opt/github_authorized_keys.sh
). Then update OpenSSH's config file, typically at /etc/ssh/sshd_config
, to set the command and the user to run as. Since we want to push files to different users the command needs to run as root- if that makes you uncomfortable you can create a dedicated user and remove the file creation parts of the script.
AuthorizedKeysCommand /opt/github_authorized_keys.sh
AuthorizedKeysCommandUser root
Now restart OpenSSH and log into your server- your keys will be pulled directly from Github.