-rhesus is the monkeysphere authorized_keys generator.
+rhesus is the monkeysphere authorized_keys/known_hosts generator.
-It's goal is to take a user's auth_user_ids file, which contains gpg
-user ids (and possibly authorized_keys options), use gpg to fetch the
-keys of the specified users, do a monkeysphere policy check on each
-id, and generate authorized_keys lines for verified id.
+In authorized_keys mode, rhesus takes an auth_user_ids file, which
+contains gpg user ids, uses gpg to fetch the keys of the specified
+users, does a monkeysphere policy check on each id, and uses gpg2ssh
+to generate authorized_keys lines for each verified id. The lines are
+then combined with a user's traditional authorized_keys file to create
+a new authorized_keys file.
+In known_hosts mode, rhesus takes an auth_host_ids file, which
+contains gpg user ids of the form ssh://URL, uses gpg to fetch the
+keys of the specified hosts, does a monkeysphere policy check on each
+id, and uses gpg2ssh to generate a known_hosts lines for each verified
+id. The lines are then combined with a user's traditional known_hosts
+file to create a new known_hosts file.
+
+When run as a normal user, no special configuration is needed.
+
+When run as an administrator to update system-maintained
+authorized_keys files for each user, the following environment
+variables should be defined first:
+
+ MS_CONF=/etc/monkeysphere/monkeysphere.conf
+ USER=foo
+
+For example, the command might be run like this:
+
+ for USER in $(ls -1 /home) ; do
+ MS_CONF=/etc/monkeysphere/monkeysphere.conf rhesus --authorized_keys
+ done
-#!/bin/sh
+#!/bin/sh -e
-# rhesus: monkeysphere authorized_keys update script
+# 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 <jrollins@fifthhorseman.net>
#
# Copyright 2008, released under the GPL, version 3 or later
-##################################################
-# load conf file
-CONF_FILE=${CONF_FILE:-"/etc/monkeysphere/monkeysphere.conf"}
-. "$CONF_FILE"
-
-export GNUPGHOME
-##################################################
-
CMD=$(basename $0)
usage() {
cat <<EOF
-usage: $CMD USERNAME
+usage: $CMD -k|--known_hosts
+ $CMD -a|--authorized_keys
EOF
}
exit ${2:-'1'}
}
+log() {
+ echo -n "ms: "
+ echo "$@"
+}
+
meat() {
grep -v -e "^[[:space:]]*#" -e '^$' "$1"
}
head --line="$1" | tail -1
}
-### MAIN
+# 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
-# user name of user to update
-USERNAME="$1"
-if ! id "$USERNAME" > /dev/null ; then
- failure "User '$USERNAME' does not exist."
-fi
+# check mode
+mode="$1"
+shift 1
-AUTH_USER_IDS="$AUTH_USER_IDS_DIR"/"$USERNAME"
-if [ ! -e "$AUTH_USER_IDS" ] ; then
- failure "No auth_user_ids file for user '$USERNAME'."
+# check user
+if ! id -u "$USER" > /dev/null 2>&1 ; then
+ failure "invalid user '$USER'."
fi
-KEYDIR="$AUTH_KEYS_DIR"/"$USERNAME"/keys
-AUTH_KEYS="$AUTH_KEYS_DIR"/authorized_keys
+HOME=$(getent passwd "$USER" | cut -d: -f6)
-# make sure the gnupg home exists with proper permissions
-mkdir -p "$GNUPGHOME"
-chmod 0700 "$GNUPGHOME"
+MS_HOME=${MS_HOME:-"$HOME"/.config/monkeysphere}
-# find number of user ids in auth_user_ids file
-NLINES=$(meat "$AUTH_USER_IDS" | wc -l)
+# load conf file
+MS_CONF=${MS_CONF:-"$MS_HOME"/monkeysphere.conf}
+[ -e "$MS_CONF" ] && . "$MS_CONF"
-# clean out keys file and remake keys directory
-rm -rf "$KEYDIR"
-mkdir -p "$KEYDIR"
+# 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'}
-# loop through all user ids, and generate ssh keys
-for (( N=1; N<=$NLINES; N=N+1 )) ; do
- # get user id
- USERID=$(meat "$AUTH_USER_IDS" | cutline "$N" )
- USERID_HASH=$(echo "$USERID" | sha1sum | awk '{ print $1 }')
+export USER
+export GNUPGHOME
- KEYFILE="$KEYDIR"/"$USERID_HASH"
+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
- # search for key on keyserver
- echo "ms: validating: '$USERID'"
- RETURN=$(echo 1 | gpg --quiet --batch --command-fd 0 --with-colons --keyserver "$KEYSERVER" --search ="$USERID")
+# act on mode
+if [ "$mode" = '--known_hosts' -o "$mode" = '-k' ] ; then
- # if the key was found...
- if [ "$RETURN" ] ; then
- echo "ms: 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:')
+ # 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"
- # extract needed fields
- KEY_TRUST=$(echo "$PUB_INFO" | cut -d: -f2)
- KEY_CAPABILITY=$(echo "$PUB_INFO" | cut -d: -f12)
-
- # check if key disabled
- if echo "$KEY_CAPABILITY" | grep -q '[D]' ; then
- echo "ms: key disabled -> SKIPPING"
- continue
- fi
+ # process the keys
+ process_keys
- # check key capability
- REQUIRED_KEY_CAPABILITY=${REQUIRED_KEY_CAPABILITY:-'a'}
- if echo "$KEY_CAPABILITY" | grep -q '[$REQUIRED_KEY_CAPABILITY]' ; then
- echo "ms: key capability verified ('$KEY_CAPABILITY')."
- else
- echo "ms: unacceptable key capability ('$KEY_CAPABILITY') -> SKIPPING"
- continue
- fi
+ # 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"
- echo -n "ms: key "
-
- # if key is not fully trusted exit
- # (this includes not revoked or expired)
- # determine trust
- 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"
- # convert pgp key to ssh key, and write to cache file
- echo -n " -> generating ssh key..."
- #gpg2ssh "$KEYID" | sed -e "s/COMMENT/$USERID/" > "$KEYFILE"
- echo " done."
- continue
- ;;
- *)
- echo -n "has unknown trust" ;;
- esac
- echo ". -> SKIPPING"
+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 "ms: key not found."
+ echo "updating authorized_keys file:"
fi
-done
+ key_dir="$user_keys_dir"
+
+ # process the keys
+ process_keys
-if [ $(ls "$KEYDIR") ] ; then
- echo "ms: writing ms authorized_keys file..."
- cat "$KEYDIR"/* > "$AUTH_KEYS"
-else
- echo "ms: no gpg keys to add to authorized_keys file."
-fi
-if [ -s ~"$USERNAME"/.ssh/authorized_keys ] ; then
- echo "ms: adding user authorized_keys..."
- cat ~"$USERNAME"/.ssh/authorized_keys >> "$AUTH_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