major overhaul of rhesus. it now handles creation of both
authorJameson Graef Rollins <jrollins@phys.columbia.edu>
Mon, 5 May 2008 01:46:20 +0000 (21:46 -0400)
committerJameson Graef Rollins <jrollins@phys.columbia.edu>
Mon, 5 May 2008 01:46:20 +0000 (21:46 -0400)
authorized_keys and known_hosts files, and is capable of being used by
an administrator to maintain authorized_keys files for users.
monkeysphere.conf file was updated for use by system administrator.

monkeysphere.conf
rhesus/README
rhesus/rhesus

index 1a6cff19597873aa4753bfe8eb31368618ac1504..a54b6bdb016da8d38be6aeef083800e26d3e36c3 100644 (file)
@@ -1,18 +1,15 @@
-# monkeysphere configuration file
-# this is currently meant to be sourced by bash.
+# monkeysphere system configuration file
 
-# configuration directory
-CONF_DIR=/etc/monkeysphere
+# This is particular configuration is meant to be sourced by the
+# rhesus shell script when run in administrative mode to maintain
+# authorized_keys files for users.
 
-# where the per-user authorized user id files are stored
-AUTH_USER_IDS_DIR="$CONF_DIR"/auth_user_ids
+AUTH_USER_FILE=/etc/monkeysphere/auth_user_ids/"$USER"
 
-# where the per-user authorized_keys info is stored
-#AUTH_KEYS_DIR=/var/lib/monkeysphere/authorized_keys
-AUTH_KEYS_DIR="$CONF_DIR"/authorized_keys
+STAGING_AREA=/var/lib/monkeysphere/stage/"$USER"
 
 # gpg home directory for server
-GNUPGHOME="$CONF_DIR"/gnupg
+GNUPGHOME=/etc/monkeysphere/gnupg
 
 # gpg keyserver to search for keys
 KEYSERVER=subkeys.pgp.net
index 226361c86484b80ebf196dd673c084ab4f537b3e..4d383d524fdacd15e63c2656afe7694d84f10648 100644 (file)
@@ -1,7 +1,30 @@
-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
index 0c7e1003db01326e0c0a46282aa068c731ea9bb6..fc2f2f57553017c64790a736e7e58e0c4240c8ba 100755 (executable)
@@ -1,25 +1,32 @@
-#!/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
 }
 
@@ -28,6 +35,11 @@ failure() {
     exit ${2:-'1'}
 }
 
+log() {
+    echo -n "ms: "
+    echo "$@"
+}
+
 meat() {
     grep -v -e "^[[:space:]]*#" -e '^$' "$1"
 }
@@ -36,116 +48,224 @@ cutline() {
     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