#!/bin/sh -e # rhesus: monkeysphere authorized_keys/known_hosts generating script # # When run as a normal user, no special configuration is needed. # # When run as an administrator to update users' authorized_keys files, # the following environment variables should be defined first: # # MS_CONF=/etc/monkeysphere/monkeysphere.conf # USER=foo # # ie: # # for USER in $(ls -1 /home) ; do # MS_CONF=/etc/monkeysphere/monkeysphere.conf rhesus --authorized_keys # done # # Written by # Jameson Rollins # # Copyright 2008, released under the GPL, version 3 or later CMD=$(basename $0) usage() { cat <&2 exit ${2:-'1'} } log() { echo -n "ms: " echo "$@" } meat() { grep -v -e "^[[:space:]]*#" -e '^$' "$1" } cutline() { head --line="$1" | tail -1 } # stand in for dkg's gpg2ssh program gpg2ssh() { mode="$1" keyid="$2" if [ "$mode" = '--authorized_keys' -o "$mode" = '-a' ] ; then gpgkey2ssh "$keyid" | sed -e "s/COMMENT/$userid/" elif [ "$mode" = '--known_hosts' -o "$mode" = '-k' ] ; then echo -n "$userid "; gpgkey2ssh "$keyid" | sed -e 's/ COMMENT//' fi } # expects global variables # mode REQUIRED_KEY_CAPABILITY ids_file key_dir process_keys() { local nlines local n local userid local userid_hash local return local pub_info local key_trust local key_capability local gen_key unset gen_key # find number of user ids in auth_user_ids file nlines=$(meat "$ids_file" | wc -l) # make sure gpg home exists with proper permissions mkdir -p -m 0700 "$GNUPGHOME" # clean out keys file and remake keys directory rm -rf "$key_dir" mkdir -p "$key_dir" # loop through all user ids, and generate ssh keys for n in $(seq 1 $nlines) ; do # get id userid=$(meat "$ids_file" | cutline "$n" ) userid_hash=$(echo "$userid" | sha1sum | awk '{ print $1 }') # search for key on keyserver log "validating: '$userid'" return=$(echo 1 | gpg --quiet --batch --command-fd 0 --with-colons --keyserver "$KEYSERVER" --search ="$userid") # if the key was found... if [ "$return" ] ; then echo " key found." # checking key attributes # see /usr/share/doc/gnupg/DETAILS.gz pub_info=$(gpg --fixed-list-mode --with-colons --list-keys --with-fingerprint ="$userid" | grep '^pub:') if [ -z "$pub_info" ] ; then echo " error getting pub info -> SKIPPING" continue fi # extract needed fields key_trust=$(echo "$pub_info" | cut -d: -f2) keyid=$(echo "$pub_info" | cut -d: -f5) key_capability=$(echo "$pub_info" | cut -d: -f12) # check if key disabled if echo "$key_capability" | grep -q '[D]' ; then echo " key disabled -> SKIPPING" continue fi # check key capability if echo "$key_capability" | grep -q '[$REQUIRED_KEY_CAPABILITY]' ; then echo " key capability verified ('$key_capability')." else echo " unacceptable key capability ('$key_capability') -> SKIPPING" continue fi # if key is not fully trusted exit # (this includes not revoked or expired) # determine trust echo -n " key " case "$key_trust" in 'i') echo -n "invalid" ;; 'r') echo -n "revoked" ;; 'e') echo -n "expired" ;; '-'|'q'|'n'|'m') echo -n "has unacceptable trust" ;; 'f'|'u') echo -n "fully trusted" gen_key=true ;; *) echo -n "has unknown trust" ;; esac if [ "$gen_key" ] ; then # convert pgp key to ssh key, and write to cache file echo -n " -> generating ssh key... " gpg2ssh "$mode" "$keyid" > "$key_dir"/"$userid_hash" echo "done." else echo ". -> SKIPPING" fi else echo " key not found." fi done } ######################################################################## # MAIN ######################################################################## if [ -z "$1" ] ; then usage exit 1 fi # check mode mode="$1" shift 1 # check user if ! id -u "$USER" > /dev/null 2>&1 ; then failure "invalid user '$USER'." fi HOME=$(getent passwd "$USER" | cut -d: -f6) MS_HOME=${MS_HOME:-"$HOME"/.config/monkeysphere} # load conf file MS_CONF=${MS_CONF:-"$MS_HOME"/monkeysphere.conf} [ -e "$MS_CONF" ] && . "$MS_CONF" # set config variable defaults STAGING_AREA=${STAGING_AREA:-"$MS_HOME"} AUTH_HOST_FILE=${AUTH_HOST_FILE:-"$MS_HOME"/auth_host_ids} AUTH_USER_FILE=${AUTH_USER_FILE:-"$MS_HOME"/auth_user_ids} GNUPGHOME=${GNUPGHOME:-"$HOME"/.gnupg} KEYSERVER=${KEYSERVER:-subkeys.pgp.net} REQUIRED_KEY_CAPABILITY=${REQUIRED_KEY_CAPABILITY:-'a'} export USER export GNUPGHOME host_keys_dir="$STAGING_AREA"/host_keys user_keys_dir="$STAGING_AREA"/user_keys known_hosts_stage_file="$STAGING_AREA"/known_hosts authorized_keys_stage_file="$STAGING_AREA"/authorized_keys # act on mode if [ "$mode" = '--known_hosts' -o "$mode" = '-k' ] ; then # set variables for process_keys command ids_file="$AUTH_HOST_FILE" log -n "[$USER] " if [ ! -s "$ids_file" ] ; then echo "auth_host_ids file is empty or does not exist." exit else echo "updating known_hosts file..." fi key_dir="$host_keys_dir" # process the keys process_keys # write known_hosts file > "$known_hosts_stage_file" if [ $(ls "$key_dir") ] ; then log -n "writing known_hosts stage file..." cat "$key_dir"/* > "$known_hosts_stage_file" echo "done." else log "no gpg keys to add to known_hosts file." fi if [ -s "$HOME"/.ssh/known_hosts ] ; then log -n "adding user known_hosts file... " cat "$HOME"/.ssh/known_hosts >> "$known_hosts_stage_file" echo "done." fi log "known_hosts file updated: $known_hosts_stage_file" elif [ "$mode" = '--authorized_keys' -o "$mode" = '-a' ] ; then # set variables for process_keys command ids_file="$AUTH_USER_FILE" log -n "[$USER] " if [ ! -s "$ids_file" ] ; then echo "auth_user_ids file is empty or does not exist." exit else echo "updating authorized_keys file:" fi key_dir="$user_keys_dir" # process the keys process_keys # write authorized_keys file > "$authorized_keys_stage_file" if [ $(ls "$key_dir") ] ; then log -n "writing ms authorized_keys file... " cat "$key_dir"/* > "$authorized_keys_stage_file" echo "done." else log "no gpg keys to add to authorized_keys file." fi if [ -s "$HOME"/.ssh/authorized_keys ] ; then log -n "adding user authorized_keys file... " cat "$HOME"/.ssh/authorized_keys >> "$authorized_keys_stage_file" echo "done." fi log "authorized_keys file updated: $authorized_keys_stage_file" fi