3 # monkeysphere-server: MonkeySphere server admin tool
5 # The monkeysphere scripts are written by:
6 # Jameson Rollins <jrollins@fifthhorseman.net>
8 # They are Copyright 2008, and are all released under the GPL, version 3
11 ########################################################################
13 PGRM_PATH=$(dirname $0)
15 SHARE=${SHARE:-"/usr/share/monkeysphere"}
19 VARLIB="/var/lib/monkeysphere"
22 # date in UTF format if needed
23 DATE=$(date -u '+%FT%T')
25 # unset some environment variables that could screw things up
31 ########################################################################
33 ########################################################################
37 usage: $PGRM <subcommand> [args]
38 MonkeySphere server admin tool.
41 update-users (u) [USER]... update users authorized_keys files
42 gen-key (g) [HOSTNAME] generate gpg key for the server
43 show-fingerprint (f) show server's host key fingerprint
44 publish-key (p) publish server's host key to keyserver
45 trust-key (t) KEYID [LEVEL] set owner trust for keyid
51 # generate server gpg key
58 hostName=${1:-$(hostname --fqdn)}
60 SERVICE=${SERVICE:-"ssh"}
61 userID="${SERVICE}://${hostName}"
63 GNUPGHOME="$GNUPGHOME_HOST"
64 if gpg --list-key ="$userID" > /dev/null 2>&1 ; then
65 failure "Key for '$userID' already exists"
69 KEY_TYPE=${KEY_TYPE:-"RSA"}
70 KEY_LENGTH=${KEY_LENGTH:-"2048"}
71 KEY_USAGE=${KEY_USAGE:-"auth"}
72 KEY_EXPIRE=${KEY_EXPIRE:-"0"}
74 Please specify how long the key should be valid.
75 0 = key does not expire
76 <n> = key expires in n days
77 <n>w = key expires in n weeks
78 <n>m = key expires in n months
79 <n>y = key expires in n years
81 read -p "Key is valid for? ($KEY_EXPIRE) " KEY_EXPIRE; KEY_EXPIRE=${KEY_EXPIRE:-"0"}
84 keyParameters=$(cat <<EOF
86 Key-Length: $KEY_LENGTH
89 Expire-Date: $KEY_EXPIRE
93 # add the revoker field if requested
94 # FIXME: the "1:" below assumes that $REVOKER's key is an RSA key. why?
95 # FIXME: why is this marked "sensitive"? how will this signature ever
96 # be transmitted to the expected revoker?
97 if [ "$REVOKER" ] ; then
98 keyParameters="${keyParameters}"$(cat <<EOF
100 Revoker: 1:$REVOKER sensitive
105 echo "The following key parameters will be used:"
106 echo "$keyParameters"
108 read -p "Generate key? [Y|n]: " OK; OK=${OK:=Y}
109 if [ ${OK/y/Y} != 'Y' ] ; then
114 keyParameters="${keyParameters}"$(cat <<EOF
121 log "generating server key..."
122 GNUPGHOME="$GNUPGHOME_HOST"
123 echo "$keyParameters" | gpg --batch --gen-key
125 # output the server fingerprint
126 fingerprint_server_key "=${userID}"
128 # find the key fingerprint of the server primary key
129 GNUPGHOME="$GNUPGHOME_HOST"
130 fingerprint=$(gpg --list-key --with-colons --with-fingerprint "=${userID}" | \
131 grep '^fpr:' | head -1 | cut -d: -f10)
133 # export the host key to the authentication keyring
134 GNUPGHOME="$GNUPGHOME_HOST" gpg --export "$fingerprint" | \
135 su --preserve-environment "$MONKEYSPHERE_USER" -c -- \
136 "GNUPGHOME=$GNUPGHOME_AUTHENTICATION gpg --import"
138 # set host key owner trust to ultimate in authentication keyring
139 echo "${fingerprint}:6:" | \
140 su --preserve-environment "$MONKEYSPHERE_USER" -c -- \
141 "GNUPGHOME=$GNUPGHOME_AUTHENTICATION gpg --import-ownertrust"
143 # write the key to the file
144 # NOTE: assumes that the primary key is the proper key to use
145 GNUPGHOME="$GNUPGHOME_HOST"
146 (umask 077 && gpgsecret2ssh "$fingerprint" > "${VARLIB}/ssh_host_rsa_key")
147 log "Private SSH host key output to file: ${VARLIB}/ssh_host_rsa_key"
150 # gpg output key fingerprint
151 fingerprint_server_key() {
157 ID="=ssh://$(hostname --fqdn)"
160 gpg --fingerprint --list-secret-keys "$ID"
163 # publish server key to keyserver
164 publish_server_key() {
165 read -p "really publish key to $KEYSERVER? [y|N]: " OK; OK=${OK:=N}
166 if [ ${OK/y/Y} != 'Y' ] ; then
171 # FIXME: need to figure out better way to identify host key
172 # dummy command so as not to publish fakes keys during testing
174 #gpg --keyserver "$KEYSERVER" --send-keys $(hostname -f)
175 failure "NOT PUBLISHED (to avoid permanent publication errors during monkeysphere development).
176 To publish manually, do: gpg --keyserver $KEYSERVER --send-keys $(hostname -f)"
180 # retrieve key from web of trust, and set owner trust to "full"
189 if [ -z "$keyID" ] ; then
190 failure "You must specify key to trust."
195 # get the key from the key server
196 GNUPGHOME="$GNUPGHOME_AUTHENTICATION"
197 su --preserve-environment "$MONKEYSPHERE_USER" -c -- \
198 "gpg --keyserver $KEYSERVER --recv-key $keyID"
199 if [ "$?" != 0 ] ; then
200 failure "Could not retrieve key '$keyID'."
203 # move the key from the authentication keyring to the host keyring
204 GNUPGHOME="$GNUPGHOME_AUTHENTICATION"
205 su --preserve-environment "$MONKEYSPHERE_USER" -c -- \
206 "gpg --export $keyID" | \
207 GNUPGHOME="$GNUPGHOME_HOST" gpg --import
209 # get key fingerprint
210 GNUPGHOME="$GNUPGHOME_HOST"
211 fingerprint=$(get_key_fingerprint "$keyID")
214 GNUPGHOME="$GNUPGHOME_HOST"
215 gpg --fingerprint "$fingerprint"
217 while [ -z "$trustLevel" ] ; do
219 Please decide how far you trust this user to correctly verify other users' keys
220 (by looking at passports, checking fingerprints from different sources, etc.)
222 1 = I don't know or won't say
224 3 = I trust marginally
226 5 = I trust ultimately
229 read -p "Your decision? " trustLevel
230 if echo "$trustLevel" | grep -v "[1-5]" ; then
231 echo "Unknown trust level '$trustLevel'."
233 elif [ "$trustLevel" = 'q' ] ; then
238 # attach a "non-exportable" signature to the key
239 # this is required for the key to have any validity at all
240 # the 'y's on stdin indicates "yes, i really want to sign"
241 GNUPGHOME="$GNUPGHOME_HOST"
243 gpg --quiet --lsign-key --command-fd 0 "$fingerprint"
245 # copy the host keyring into the authentication keyring
246 mv "$GNUPGHOME_AUTHENTICATION"/pubring.gpg{,.old}
247 cp "$GNUPGHOME_HOST"/pubring.gpg "$GNUPGHOME_AUTHENTICATION"/pubring.gpg
248 chown "$MONKEYSPHERE_USER" "$GNUPGHOME_AUTHENTICATION"/pubring.gpg
249 GNUPGHOME="$GNUPGHOME_AUTHENTICATION"
250 su --preserve-environment "$MONKEYSPHERE_USER" -c -- \
251 "gpg --import ${GNUPGHOME_AUTHENTICATION}/pubring.gpg.old"
253 # index trustLevel by one to difference between level in ui and level
255 trustLevel=$((trustLevel+1))
257 # import new owner trust level for key
258 GNUPGHOME="$GNUPGHOME_AUTHENTICATION"
259 echo "${fingerprint}:${trustLevel}:" | \
260 su --preserve-environment "$MONKEYSPHERE_USER" -c -- \
261 "GNUPGHOME=$GNUPGHOME_AUTHENTICATION gpg --import-ownertrust"
264 log "Owner trust updated."
266 failure "There was a problem changing owner trust."
270 ########################################################################
272 ########################################################################
275 [ "$COMMAND" ] || failure "Type '$PGRM help' for usage."
278 # set ms home directory
279 MS_HOME=${MS_HOME:-"$ETC"}
281 # load configuration file
282 MS_CONF=${MS_CONF:-"$MS_HOME"/monkeysphere-server.conf}
283 [ -e "$MS_CONF" ] && . "$MS_CONF"
285 # set empty config variable with defaults
286 MONKEYSPHERE_USER=${MONKEYSPHERE_USER:-"monkeysphere"}
287 KEYSERVER=${KEYSERVER:-"subkeys.pgp.net"}
288 CHECK_KEYSERVER=${CHECK_KEYSERVER:="true"}
289 AUTHORIZED_USER_IDS=${AUTHORIZED_USER_IDS:-"%h/.config/monkeysphere/authorized_user_ids"}
290 RAW_AUTHORIZED_KEYS=${RAW_AUTHORIZED_KEYS:-"%h/.ssh/authorized_keys"}
293 REQUIRED_USER_KEY_CAPABILITY=${REQUIRED_USER_KEY_CAPABILITY:-"a"}
294 GNUPGHOME_HOST=${GNUPGHOME_HOST:-"${VARLIB}/gnupg-host"}
295 GNUPGHOME_AUTHENTICATION=${GNUPGHOME_AUTHENTICATION:-"${VARLIB}/gnupg-authentication"}
299 export MONKEYSPHERE_USER
301 export CHECK_KEYSERVER
302 export REQUIRED_USER_KEY_CAPABILITY
303 export GNUPGHOME_HOST
304 export GNUPGHOME_AUTHENTICATION
308 'update-users'|'update-user'|'u')
310 # get users from command line
313 # or just look at all users if none specified
314 unames=$(getent passwd | cut -d: -f1)
318 MODE="authorized_keys"
321 GNUPGHOME="$GNUPGHOME_AUTHENTICATION"
323 # check to see if the gpg trust database has been initialized
324 if [ ! -s "${GNUPGHOME}/trustdb.gpg" ] ; then
325 failure "GNUPG trust database uninitialized. Please see MONKEYSPHERE-SERVER(8)."
328 # make sure the authorized_keys directory exists
329 mkdir -p "${VARLIB}/authorized_keys"
332 for uname in $unames ; do
333 # check all specified users exist
334 if ! getent passwd "$uname" >/dev/null ; then
335 error "----- unknown user '$uname' -----"
339 # set authorized_user_ids and raw authorized_keys variables,
340 # translating ssh-style path variables
341 authorizedUserIDs=$(translate_ssh_variables "$uname" "$AUTHORIZED_USER_IDS")
342 rawAuthorizedKeys=$(translate_ssh_variables "$uname" "$RAW_AUTHORIZED_KEYS")
344 # if neither is found, skip user
345 if [ ! -s "$authorizedUserIDs" ] ; then
346 if [ "$rawAuthorizedKeys" = '-' -o ! -s "$rawAuthorizedKeys" ] ; then
351 log "----- user: $uname -----"
353 # make temporary directory
356 # trap to delete temporary directory on exit
357 trap "rm -rf $TMPDIR" EXIT
359 # create temporary authorized_user_ids file
360 TMP_AUTHORIZED_USER_IDS="${TMPDIR}/authorized_user_ids"
361 touch "$TMP_AUTHORIZED_USER_IDS"
363 # create temporary authorized_keys file
364 AUTHORIZED_KEYS="${TMPDIR}/authorized_keys"
365 touch "$AUTHORIZED_KEYS"
367 # set restrictive permissions on the temporary files
368 # FIXME: is there a better way to do this?
370 chmod 0600 "$AUTHORIZED_KEYS"
371 chmod 0600 "$TMP_AUTHORIZED_USER_IDS"
372 chown -R "$MONKEYSPHERE_USER" "$TMPDIR"
374 # if the authorized_user_ids file exists...
375 if [ -s "$authorizedUserIDs" ] ; then
376 # copy user authorized_user_ids file to temporary
378 cat "$authorizedUserIDs" > "$TMP_AUTHORIZED_USER_IDS"
380 # export needed variables
381 export AUTHORIZED_KEYS
382 export TMP_AUTHORIZED_USER_IDS
384 # process authorized_user_ids file, as monkeysphere
386 su --preserve-environment "$MONKEYSPHERE_USER" -c -- \
387 ". ${SHARE}/common; process_authorized_user_ids $TMP_AUTHORIZED_USER_IDS"
390 # add user-controlled authorized_keys file path if specified
391 if [ "$rawAuthorizedKeys" != '-' -a -s "$rawAuthorizedKeys" ] ; then
392 log -n "adding raw authorized_keys file... "
393 cat "$rawAuthorizedKeys" >> "$AUTHORIZED_KEYS"
397 # openssh appears to check the contents of the
398 # authorized_keys file as the user in question, so the
399 # file must be readable by that user at least.
400 # FIXME: is there a better way to do this?
401 chown root "$AUTHORIZED_KEYS"
402 chgrp $(getent passwd "$uname" | cut -f4 -d:) "$AUTHORIZED_KEYS"
403 chmod g+r "$AUTHORIZED_KEYS"
405 # if the resulting authorized_keys file is not empty, move
407 mv -f "$AUTHORIZED_KEYS" "${VARLIB}/authorized_keys/${uname}"
409 log "authorized_keys file updated."
411 # destroy temporary directory
418 GNUPGHOME="$GNUPGHOME_HOST"
422 'show-fingerprint'|'f')
424 GNUPGHOME="$GNUPGHOME_HOST"
425 fingerprint_server_key "$@"
430 GNUPGHOME="$GNUPGHOME_HOST"
443 failure "Unknown command: '$COMMAND'
444 Type '$PGRM help' for usage."