#!/usr/bin/env bash # Tests to ensure that the monkeysphere is working # # unset MONKEYSPHERE_TEST_NO_EXAMINE to get a prompt to examine the # test state after failure. # Authors: # Daniel Kahn Gillmor <dkg@fifthhorseman.net> # Jameson Rollins <jrollins@fifthhorseman.net> # Micah Anderson <micah@riseup.net> # # Copyright: © 2008-2010 # License: GPL v3 or later # these tests should all be able to run as a non-privileged user. # put all the test output to stdout exec 2>&1 # all subcommands in this script should complete without failure: set -e # piped commands should return the code of the first non-zero return set -o pipefail # make sure the TESTDIR is an absolute path, not a relative one. export TESTDIR=$(cd $(dirname "$0") && pwd) source "$TESTDIR"/common ## make sure that the right tools are installed to run the test. the ## test has *more* requirements than plain ol' monkeysphere: [ -f /usr/sbin/sshd ] || { echo "You must have sshd installed to run this test." ; exit 1; } which socat >/dev/null || { echo "You must have socat installed to run this test." ; exit 1; } perl -MCrypt::OpenSSL::RSA -e 1 2>/dev/null || { echo "You must have the perl module Crypt::OpenSSL::RSA installed to run this test. On debian-derived systems, you can set this up with: apt-get install libcrypt-openssl-rsa-perl" ; exit 1; } perl -MDigest::SHA -e 1 2>/dev/null || { echo "You must have the perl module Digest::SHA installed to run this test. On debian-derived systems, you can set this up with: apt-get install libdigest-sha-perl" ; exit 1; } ## FIXME: other checks? ###################################################################### ### FUNCTIONS # gpg command for test admin user gpgadmin() { chmod 0700 "$TEMPDIR"/admin "$TEMPDIR"/admin/.gnupg GNUPGHOME="$TEMPDIR"/admin/.gnupg gpg --no-tty "$@" } # test ssh connection # first argument is expected return code from ssh connection ssh_test() { local RETURN=0 umask 0077 CODE=${1:-0} # start the ssh daemon on the socket echo "##### starting ssh server..." socat EXEC:"/usr/sbin/sshd -f ${SSHD_CONFIG} -i -D -e" "UNIX-LISTEN:${SOCKET}" 2> "$TEMPDIR"/sshd.log & SSHD_PID="$!" # wait until the socket is created before continuing while [ ! -S "$SOCKET" ] ; do sleep 1 done # make a client connection to the socket echo "##### starting ssh client..." ssh-agent bash -c \ "monkeysphere subkey-to-ssh-agent && ssh -F $TEMPDIR/testuser/.ssh/config ${target_hostname:-testhost.example} true" \ || RETURN="$?" # kill the sshd process if it's still running kill "$SSHD_PID" || true wait SSHD_PID= if [ "$RETURN" = "$CODE" ] ; then echo "##### ssh connection test PASSED. returned: $RETURN" return 0 else echo "##### ssh connection test FAILED. returned: $RETURN, expected: $CODE" return 1 fi } # invoke this instead of ssh_test() if you want this test to be # skipped when the working directory has bad permissions. ssh_good_perm_test() { if [ "$TEMPDIR_PERMISSIONS_SAFE" = no ] ; then echo "WARNING!!! Test SKIPPED because we are running in an unsafe working directory." else ssh_test "$@" fi } SSHD_PID= ## setup trap trap failed_cleanup EXIT ###################################################################### ### SETUP VARIABLES ## set up some variables to ensure that we're operating strictly in ## the tests, not system-wide: # set up temp dir # NOTE: /tmp can not be used as the temp dir here, since the # permissions on /tmp are usually such that they will not pass the # monkeysphere/ssh path permission checking. If you need to use a # different location than the current source, please set $TMPDIR # somewhere with tighter permissions. mkdir -p "$TESTDIR"/tmp TEMPDIR=$(mktemp -d "${TMPDIR:-$TESTDIR/tmp}/monkeyspheretest.XXXXXXX") # Use the local copy of executables first, instead of system ones. # This should help us test without installing. export PATH="$TESTDIR"/../src:"$PATH" export MONKEYSPHERE_SYSDATADIR="$TEMPDIR" export MONKEYSPHERE_SYSCONFIGDIR="$TEMPDIR" export MONKEYSPHERE_SYSSHAREDIR="$TESTDIR"/../src/share export MONKEYSPHERE_MONKEYSPHERE_USER=$(whoami) HOST_KEY_FILE="$MONKEYSPHERE_SYSCONFIGDIR"/host_keys.pub.pgp export MONKEYSPHERE_CHECK_KEYSERVER=false # example.org does not respond to the HKP port, so this should cause # any keyserver connection attempts that do happen (they shouldn't!) # to hang, so we'll notice them: export MONKEYSPHERE_KEYSERVER=example.org export MONKEYSPHERE_LOG_LEVEL=DEBUG export MONKEYSPHERE_CORE_KEYLENGTH=1024 export MONKEYSPHERE_PROMPT=false # unset SUBKEYS_FOR_AGENT variable which, if set, would confuse the # into trying to use the user's key, instead of the testuser's key unset MONKEYSPHERE_SUBKEYS_FOR_AGENT export SSHD_CONFIG="$TEMPDIR"/sshd_config export SOCKET="$TEMPDIR"/ssh-socket # Make sure $DISPLAY is set to convince ssh and monkeysphere to fall # back on $SSH_ASKPASS. Make sure it's not set to the current actual # $DISPLAY (if one exists) because this test suite should not be doing # *anything* with any running X11 session. export DISPLAY=monkeys ## we cannot do proper directory permissions checking if the current ## working directory has unsatisfactory permissions: if "$MONKEYSPHERE_SYSSHAREDIR"/checkperms $(whoami) "$TEMPDIR"; then echo "Permissions on temporary directory '$TEMPDIR' are OK for permissions checks." TEMPDIR_PERMISSIONS_SAFE=yes else cat <<EOF !!!WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING!!! Permissions on testing directory '$TEMPDIR' are too loose to do proper strict permissions checking. Some tests will be disabled or ignored. To avoid this warning (and to make sure that all tests are run properly), please run these tests within a directory that meets sshd's standards for "StrictModes yes" -- the directory (and every one of its parents) should be owned only be the user running this test or root, and should not be writable by group or other. !!!WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING!!! EOF # FIXME: what else should we do with this knowledge to make sure # that the appropriate tests are handled properly? TEMPDIR_PERMISSIONS_SAFE=no # this is a new option (as of 0.26) to disable filesystem # permission checks. # it should operate by analogy with StrictModes from sshd_config(5) export MONKEYSPHERE_STRICT_MODES=false fi ###################################################################### ### CONFIGURE ENVIRONMENTS # copy in admin and testuser home to tmp echo echo "##################################################" echo "### configuring testuser home..." (cd "$TESTDIR"/home && find testuser | cpio -pdu "$TEMPDIR") # set up environment for testuser export TESTHOME="$TEMPDIR"/testuser export GNUPGHOME="$TESTHOME"/.gnupg chmod 0700 "$GNUPGHOME" export SSH_ASKPASS="$TESTHOME"/.ssh/askpass export MONKEYSPHERE_HOME="$TESTHOME"/.monkeysphere cat <<EOF >> "$TESTHOME"/.ssh/config UserKnownHostsFile $TESTHOME/.ssh/known_hosts IdentityFile $TESTHOME/.ssh/no-such-identity ProxyCommand $TESTHOME/.ssh/proxy-command %h %p $SOCKET EOF cat <<EOF >> "$MONKEYSPHERE_HOME"/monkeysphere.conf KNOWN_HOSTS=$TESTHOME/.ssh/known_hosts EOF get_gpg_prng_arg >> "$GNUPGHOME"/gpg.conf echo echo "##################################################" echo "### configuring admin home..." (cd "$TESTDIR"/home && find admin | cpio -pdu "$TEMPDIR") # set up sshd echo echo "##################################################" echo "### configuring sshd..." cp "$TESTDIR"/etc/ssh/sshd_config "$SSHD_CONFIG" # write the sshd_config cat <<EOF >> "$SSHD_CONFIG" HostKey ${MONKEYSPHERE_SYSDATADIR}/ssh_host_rsa_key AuthorizedKeysFile ${MONKEYSPHERE_SYSDATADIR}/authorized_keys/%u EOF # disable sshd's strict permissions settings so that some tests can # complete when running under a dubious path: if [ "$TEMPDIR_PERMISSIONS_SAFE" != yes ] ; then cat <<EOF >> "$SSHD_CONFIG" StrictModes no EOF fi ###################################################################### ### SERVER HOST SETUP # import host key echo echo "##################################################" echo "### import host key..." ssh-keygen -b 1024 -t rsa -N '' -f "$TEMPDIR"/ssh_host_rsa_key monkeysphere-host import-key "$TEMPDIR"/ssh_host_rsa_key ssh://testhost.example echo echo "##################################################" echo "### getting host key fingerprint..." SSHHOSTKEYID=$( monkeysphere-host show-keys | grep '^OpenPGP fingerprint: ' | cut -f3 -d\ ) echo "$SSHHOSTKEYID" # change host key expiration echo echo "##################################################" echo "### setting host key expiration..." monkeysphere-host set-expire 1 # FIXME: how do we check that the expiration has really been set? # certify host key with the "Admin's Key". # (this would normally be done via keyservers) echo echo "##################################################" echo "### certifying server host key..." < "$HOST_KEY_FILE" gpgadmin --import echo y | gpgadmin --command-fd 0 --sign-key "$SSHHOSTKEYID" # FIXME: add revoker? # FIXME: how can we test publish-key without flooding junk into the # keyservers? # FIXME: should we run "diagnostics" here to test setup? ###################################################################### ### SERVER AUTHENTICATION SETUP # set up monkeysphere authentication echo echo "##################################################" echo "### setup monkeysphere authentication..." cp "$TESTDIR"/etc/monkeysphere/monkeysphere-authentication.conf "$TEMPDIR"/ cat <<EOF >> "$TEMPDIR"/monkeysphere-authentication.conf AUTHORIZED_USER_IDS="$MONKEYSPHERE_HOME/authorized_user_ids" EOF monkeysphere-authentication setup get_gpg_prng_arg >> "$MONKEYSPHERE_SYSDATADIR"/authentication/sphere/gpg.conf # add admin as identity certifier for testhost.example echo echo "##################################################" echo "### adding admin as certifier..." monkeysphere-authentication add-id-certifier "$TEMPDIR"/admin/.gnupg/pubkey.gpg echo echo "##################################################" echo "### list certifiers..." monkeysphere-authentication list-certifiers # FIXME: should we run "diagnostics" here to test setup? ###################################################################### ### TESTUSER SETUP # generate an auth subkey for the test user that expires in 2 days echo echo "##################################################" echo "### generating key for testuser..." monkeysphere gen-subkey # add server key to testuser keychain echo echo "##################################################" echo "### export server key to testuser..." gpgadmin --armor --export "$SSHHOSTKEYID" | gpg --import # teach the "server" about the testuser's key echo echo "##################################################" echo "### export testuser key to server..." gpg --export testuser | monkeysphere-authentication gpg-cmd --import # update authorized_keys for user echo echo "##################################################" echo "### update server authorized_keys file for this testuser..." monkeysphere-authentication update-users $(whoami) # FIXME: this is maybe not failing properly for: # ms: improper group or other writability on path '/tmp'. ###################################################################### ### TESTS ## see whether keys-for-userid works from the client's perspective: echo echo "##################################################" echo "### testing monkeysphere keys-for-userid ..." diff -q <( monkeysphere keys-for-userid ssh://testhost.example ) <( cut -f1,2 -d' ' < "$TEMPDIR"/ssh_host_rsa_key.pub ) # connect to test sshd, using monkeysphere ssh-proxycommand to verify # the identity before connection. This should work in both directions! echo echo "##################################################" echo "### ssh connection test for success..." ssh_test # remove the testuser's authorized_user_ids file, update, and make # sure that the ssh authentication FAILS echo echo "##################################################" echo "### removing testuser authorized_user_ids and updating..." mv "$TESTHOME"/.monkeysphere/authorized_user_ids{,.bak} monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_test 255 mv "$TESTHOME"/.monkeysphere/authorized_user_ids{.bak,} # put improper permissions on authorized_user_ids file, update, and # make sure ssh authentication FAILS echo echo "##################################################" echo "### setting group writability on authorized_user_ids and updating..." chmod g+w "$TESTHOME"/.monkeysphere/authorized_user_ids monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test 255 chmod g-w "$TESTHOME"/.monkeysphere/authorized_user_ids echo echo "##################################################" echo "### setting other writability on authorized_user_ids and updating..." chmod o+w "$TESTHOME"/.monkeysphere/authorized_user_ids monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test 255 chmod o-w "$TESTHOME"/.monkeysphere/authorized_user_ids monkeysphere-authentication update-users $(whoami) # test symlinks echo echo "##################################################" echo "### setup for symlink tests..." cp -a "$TESTHOME"/.monkeysphere{,.linktest} echo echo "##################################################" echo "### make authorized_user_ids an absolute symlink and updating..." mv "$TESTHOME"/.monkeysphere/authorized_user_ids{,.bak} ln -s "$TESTHOME"/.monkeysphere{.linktest,}/authorized_user_ids monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for success..." ssh_test echo echo "##################################################" echo "### create bad permissions on link dir and updating..." chmod o+w "$TESTHOME"/.monkeysphere.linktest monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test 255 chmod o-w "$TESTHOME"/.monkeysphere.linktest echo echo "##################################################" echo "### make authorized_user_ids a relative symlink and updating..." ln -sf ../.monkeysphere.linktest/authorized_user_ids "$TESTHOME"/.monkeysphere/authorized_user_ids monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for success..." ssh_test echo echo "##################################################" echo "### create bad permissions on link dir updating..." chmod o+w "$TESTHOME"/.monkeysphere.linktest monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test 255 chmod o-w "$TESTHOME"/.monkeysphere.linktest # FIXME: implement check of link path, and uncomment this test # echo # echo "##################################################" # echo "### create bad permissions on link dir and updating..." # chmod o+w "$TESTHOME"/.monkeysphere # monkeysphere-authentication update-users $(whoami) # echo # echo "##################################################" # echo "### ssh connection test for failure..." # ssh_good_perm_test 255 # chmod o-w "$TESTHOME"/.monkeysphere rm "$TESTHOME"/.monkeysphere/authorized_user_ids mv "$TESTHOME"/.monkeysphere/authorized_user_ids{.bak,} echo echo "##################################################" echo "### make .monkeysphere directory an absolute symlink and updating..." mv "$TESTHOME"/.monkeysphere{,.bak} ln -s "$TESTHOME"/.monkeysphere{.linktest,} monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for success..." ssh_test echo echo "##################################################" echo "### create bad permissions on link dir and updating..." chmod o+w "$TESTHOME"/.monkeysphere.linktest monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test 255 chmod o-w "$TESTHOME"/.monkeysphere.linktest echo echo "##################################################" echo "### make .monkeysphere directory a relative symlink and updating..." ln -sfn .monkeysphere.linktest "$TESTHOME"/.monkeysphere monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for success..." ssh_test echo echo "##################################################" echo "### create bad permissions on link dir updating..." chmod o+w "$TESTHOME"/.monkeysphere.linktest monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test 255 chmod o-w "$TESTHOME"/.monkeysphere.linktest rm "$TESTHOME"/.monkeysphere mv "$TESTHOME"/.monkeysphere{.bak,} # ensure we're back to normal: echo echo "##################################################" echo "### making sure we are back to normal..." monkeysphere-authentication update-users $(whoami) ssh_test echo echo "##################################################" echo "### ssh connection test directly to 'testhost2.example' without new name..." target_hostname=testhost2.example ssh_test 255 echo echo "##################################################" echo "### add servicename, certify by admin, import by user..." monkeysphere-host add-servicename ssh://testhost2.example <"$HOST_KEY_FILE" gpgadmin --import printf "y\ny\n" | gpgadmin --command-fd 0 --sign-key "$SSHHOSTKEYID" echo echo "##################################################" echo "### ssh connection test with hostname 'testhost2.example' added..." gpgadmin --export "$SSHHOSTKEYID" | gpg --import gpg --check-trustdb ssh_test echo echo "##################################################" echo "### ssh connection test directly to 'testhost2.example' ..." gpg --import <"$HOST_KEY_FILE" gpg --check-trustdb target_hostname=testhost2.example ssh_test echo echo "##################################################" echo "### ssh connection test for failure with 'testhost2.example' revoked..." monkeysphere-host revoke-servicename ssh://testhost2.example gpg --import <"$HOST_KEY_FILE" gpg --check-trustdb target_hostname=testhost2.example ssh_test 255 # FIXME: addtest: remove admin as id-certifier and check ssh failure # FIXME: addtest: how do we test that set-expire makes sense after new # servicenames have been added? # test to make sure things are OK after the previous tests: echo echo "##################################################" echo "### settings reset, updating..." monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for success..." ssh_test echo echo "##################################################" echo "### Testing TLS setup..." openssl req -config "$TESTDIR"/openssl.cnf -x509 -newkey rsa:1024 -subj '/DC=example/DC=testhost/CN=testhost.example/' -days 3 -keyout "$TEMPDIR"/tls_key.pem -nodes >"$TEMPDIR"/tls_cert.pem monkeysphere-host import-key "$TEMPDIR"/tls_key.pem https://testhost.example # FIXME: how can we test this via an https client? # We don't currently provide one. # FIXME: should we test other monkeysphere-host operations somehow now # that we have more than one key in the host keyring? echo echo "##################################################" echo "### revoking ssh host key..." # generate the revocation certificate and feed it directly to the test # user's keyring (we're not publishing to the keyservers) monkeysphere-host revoke-key "$SSHHOSTKEYID" | gpg --import echo echo "##################################################" echo "### ssh connection test for failure..." ssh_test 255 ###################################################################### trap - EXIT echo echo "##################################################" echo " Monkeysphere basic tests completed successfully!" echo "##################################################" cleanup