#!/bin/sh BOOT_WAIT_DEFAULT=25 # set GO_PLAY_KIDS to no to perform all tests. GO_PLAY_KIDS=yes YESCOUNT=0 NOCOUNT=0 WHISPERSSOCK=/run/whispers/unix-sockets/whispers.sock VPNSOCK=/run/whispers/vpn/unix-sockets/vpn.sock REGISTERSLEEP=10 TESTREGISTERSLEEP=4 CONNECTSLEEP=20 TESTCONNECTSLEEP=10 function testcount() { test $1 = $2 && echo SUCCESS || echo FAILED test $1 = $2 && YESCOUNT=$(($YESCOUNT + 1)) || NOCOUNT=$(($NOCOUNT + 1)) } function dirtyssh() { ssh -q \ -o UserKnownHostsFile=/dev/null \ -o StrictHostKeyChecking=no \ -p $1 \ root@localhost \ "$2" } function herd_service_status() { echo "herd status of $2 on ${VM[$1]}:" dirtyssh ${PORT[$1]} "herd status $2" echo } function herd_test_service_status() { echo "Test if the $2 service is $3 on ${VM[$1]}." RESULT=$(herd_service_status $1 $2 | awk -- 'BEGIN{FS="[. ]"} ; /It is '$3'/ {print $5}') testcount ${RESULT} $3 } function whispers_service_status() { echo "herd status of $3/$2 on ${VM[$1]}:" dirtyssh ${PORT[$1]} "herd -s \\ /run/whispers/$3/unix-sockets/*.sock status $2" echo } function whispers_test_service_status() { echo "Test if the $3/$2 service is $4 on ${VM[$1]}." RESULT=$(whispers_service_status $1 $2 $3 | awk -- 'BEGIN{FS="[. ]"} ; /It is '$4'/ {print $5}') testcount ${RESULT} $4 } function test_forward() { echo "From ${VM[$1]}, take forward opened by ${VM[$2]} to connect to ${VM[$3]} through sshd of ${VM[$4]}." EXITHOST=reset EXITHOST=$(dirtyssh \ ${PORT[$1]} \ "ssh -q \\ -o UserKnownHostsFile=/dev/null \\ -o StrictHostKeyChecking=no \\ -p $5 \\ root@$6 \\ hostname") echo "Host name at forward exit: ${EXITHOST}, expected: ${VM[$3]}" testcount ${EXITHOST} ${VM[$3]} } function test_forwards() { # echo "*** REVERSE PORT FORWARD TESTS" # echo # test_forward $WILD01 \ # $CLIENT01 \ # $CLIENT01 \ # $SOCKS \ # ${EXTRAPORT_HOST[$SOCKS]} \ # 10.0.2.2 # echo # echo echo "*** PORT FORWARD TESTS" echo test_forward $CLIENT02 \ $CLIENT02 \ $VPN_SERVER \ $VPN_SERVER \ 36492 \ localhost echo echo } function test_book_dummy_user() { echo "Book a hostname $2 in the network state of ${VM[$1]}, test that hostnme $2 has booked one and one one voucher." dirtyssh ${PORT[$1]} "herd -s $VPNSOCK book-client network-rw $2" COUNT=reset COUNT=$(dirtyssh \ ${PORT[$1]} \ "herd -s $VPNSOCK display-network-state network-rw \\ | grep -c \"client-hostname $2\"") echo "Number of booked vouchers for $2: ${COUNT}, expected: 1" testcount ${COUNT} 1 echo } function tests_book_free_dummy_users() { echo "*** TEST DUMMY VOUCHER BOOKING BY ${VM[$VPN_SERVER]}" echo test_book_dummy_user $1 $2 test_book_dummy_user $1 $2 test_book_dummy_user $1 $3 test_book_dummy_user $1 $2 test_book_dummy_user $1 $3 test_book_dummy_user $1 $2 test_book_dummy_user $1 $4 test_book_dummy_user $1 $2 test_book_dummy_user $1 $4 test_free_dummy_user $1 $3 test_free_dummy_user $1 $2 test_book_dummy_user $1 $3 test_book_dummy_user $1 $3 test_free_dummy_user $1 $2 test_free_dummy_user $1 $3 test_book_dummy_user $1 $4 test_free_dummy_user $1 $4 test_free_dummy_user $1 $2 test_free_dummy_user $1 $2 test_book_dummy_user $1 $2 test_free_dummy_user $1 $2 test_free_dummy_user $1 $3 test_free_dummy_user $1 $4 echo } function test_free_dummy_user() { echo "Free a hostname $2 in the network state of ${VM[$1]}, test that hostnme $2 has no booked voucher." dirtyssh ${PORT[$1]} "herd -s $VPNSOCK free-client-booking network-rw $2" COUNT=reset COUNT=$(dirtyssh \ ${PORT[$1]} \ "herd -s $VPNSOCK display-network-state network-rw \\ | grep -c \"client-hostname $2\"") echo "Number of booked vouchers for $2: ${COUNT}, expected: 0" testcount ${COUNT} 0 echo } function tests_register() { echo "Register ${VM[$1]} as a prospective client in its configured VPN network." dirtyssh ${PORT[$1]} "herd -s $WHISPERSSOCK register vpn" sleep $REGISTERSLEEP echo "Test that hostname ${VM[$1]} has booked one and only one voucher." COUNT=reset COUNT=$(dirtyssh \ ${PORT[$1]} \ "herd -s $VPNSOCK display-network-state network-rw \\ | grep -c \"client-hostname ${VM[$1]}\"") echo "Number of booked vouchers for ${VM[$1]}: ${COUNT}, expected: 1" testcount ${COUNT} 1 sleep $TESTREGISTERSLEEP whispers_test_service_status $1 registered vpn running whispers_test_service_status $1 unregistered vpn stopped whispers_test_service_status $1 connecting vpn stopped whispers_test_service_status $1 disconnecting vpn stopped whispers_test_service_status $1 registering vpn stopped whispers_test_service_status $1 unregistering vpn stopped echo } function tests_unregister() { echo "Unregister ${VM[$1]} from its configured VPN network." dirtyssh ${PORT[$1]} "herd -s $WHISPERSSOCK unregister vpn" sleep $REGISTERSLEEP echo "Test that hostname ${VM[$1]} has no booked voucher." COUNT=reset COUNT=$(dirtyssh \ ${PORT[$1]} \ "herd -s $VPNSOCK display-network-state network-rw \\ | grep -c \"client-hostname ${VM[$1]}\"") echo "Number of booked vouchers for ${VM[$1]}: ${COUNT}, expected: 0" testcount ${COUNT} 0 sleep $TESTREGISTERSLEEP whispers_test_service_status $1 registered vpn stopped whispers_test_service_status $1 unregistered vpn running whispers_test_service_status $1 connecting vpn stopped whispers_test_service_status $1 disconnecting vpn stopped whispers_test_service_status $1 registering vpn stopped whispers_test_service_status $1 unregistering vpn stopped echo } function tests_register_unregister() { echo "*** REGISTRATION AND UNREGISTRATION TESTS" echo tests_register $1 tests_register $1 tests_register $2 tests_register $1 tests_unregister $1 tests_unregister $1 tests_unregister $2 tests_unregister $1 tests_register $1 tests_register $1 tests_register $2 tests_register $1 tests_unregister $1 tests_unregister $1 tests_unregister $2 tests_unregister $1 echo } function test_connect_propagate() { if dirtyssh \ ${PORT[$2]} \ "herd -s $VPNSOCK display-network-state network-rw \\ | grep ${VM[$2]} \\ | grep \"connected. #t\"" > /dev/null then AUX=${VM[$2]} else AUX=${VM[$1]} fi echo "Test that ${VM[$1]} is connected in ${AUX}'s network state." COUNT=reset COUNT=$(dirtyssh \ ${PORT[$1]} \ "herd -s $VPNSOCK display-network-state network-rw \\ | grep ${AUX} \\ | grep -c \"connected. #t\"") echo "Number of connected vouchers for ${AUX}: ${COUNT}, expected: 1" testcount ${COUNT} 1 sleep $TESTCONNECTSLEEP } function server_tun() { dirtyssh \ ${PORT[$VPN_SERVER]} "herd -s $VPNSOCK display-network-state network-rw" \ | grep ${VM[$1]} \ | sed 's/.*tun-edvice-number ([0-9]*)/tun\1/' } function tests_connect() { echo "Connect ${VM[$1]} as a VPN client of the VPN network." dirtyssh ${PORT[$1]} "herd -s $WHISPERSSOCK connect vpn" sleep $CONNECTSLEEP echo "Test that ${VM[$1]} has a tun interface." COUNT=reset COUNT=$(dirtyssh \ ${PORT[$1]} \ "ip addr \\ | grep -c \": tun\"") echo "Number of tun interfaces for ${VM[$1]}: ${COUNT}, expected: 1" testcount ${COUNT} 1 sleep $TESTCONNECTSLEEP test_connect_propagate $1 $1 test_connect_propagate $1 $2 whispers_test_service_status $1 connected vpn running whispers_test_service_status $1 disconnected vpn stopped whispers_test_service_status $1 registered vpn running whispers_test_service_status $1 unregistered vpn stopped whispers_test_service_status $1 connecting vpn stopped whispers_test_service_status $1 disconnecting vpn stopped whispers_test_service_status $1 registering vpn stopped whispers_test_service_status $1 unregistering vpn stopped echo } function tests_manual_register_connect { echo "*** REGISTER THEN CONNECT CLIENTS TESTS" echo tests_register $1 tests_connect $1 $2 tests_register $1 tests_connect $1 $2 tests_register $2 tests_connect $2 $1 tests_register $2 tests_connect $2 $1 tests_register $2 tests_connect $2 $1 tests_register $1 tests_connect $1 $2 echo } function tests_disconnect() { echo "Disconnect ${VM[$1]} as a VPN client of the VPN network." dirtyssh ${PORT[$1]} "herd -s $WHISPERSSOCK disconnect vpn" sleep $CONNECTSLEEP whispers_test_service_status $1 connected vpn stopped whispers_test_service_status $1 disconnected vpn running whispers_test_service_status $1 registered vpn running whispers_test_service_status $1 unregistered vpn stopped whispers_test_service_status $1 connecting vpn stopped whispers_test_service_status $1 disconnecting vpn stopped whispers_test_service_status $1 registering vpn stopped whispers_test_service_status $1 unregistering vpn stopped echo } function tests_disconnects_connects { echo "*** DISCONNECT THEN RECONNECT CLIENTS TESTS" echo tests_disconnect $1 tests_disconnect $1 tests_connect $1 $2 tests_connect $1 $2 tests_disconnect $2 tests_disconnect $2 tests_disconnect $2 tests_connect $2 $1 tests_connect $2 $1 tests_disconnect $2 tests_disconnect $2 echo } function tests_direct_connects { echo "*** DIRECT CONNECT CLIENTS TESTS" echo tests_unregister $1 tests_unregister $2 tests_connect $1 $2 tests_connect $2 $1 echo } function tests_disconnects_unregisters { echo "*** DISCONNECT THEN UNREGISTER CLIENTS TESTS" echo tests_disconnect $1 tests_disconnect $1 tests_unregister $1 tests_unregister $1 tests_disconnect $2 tests_disconnect $2 tests_disconnect $2 tests_unregister $2 tests_unregister $2 echo } echo "*** DEFINING VM INSTANCIATION ARRAYS" echo I=0 WILD=$I VM[$I]=wild echo "* VM: ${VM[$I]}" EXTRA_SERVICES[$I]="" PORT[$I]=$((10022+$I)) EXTRAPORT_HOST[$I]=$((${PORT[$I]}+1000)) EXTRAPORT_VM[$I]=$((${PORT[$I]}+2000)) ULTIMAPORT_HOST[$I]=$((${PORT[$I]}+3000)) ULTIMAPORT_VM[$I]=$((${PORT[$I]}+4000)) EXTRA_PACKAGES[$I]=" iproute iptables" echo I=$(($I+1)) VPN_SERVER=$I VM[$I]=vpn echo "* VM: ${VM[$I]}" ALLOW_GATEWAY[$I]="yes" EXTRA_SERVICES[$I]="" PORT[$I]=$((10022+$I)) EXTRAPORT_HOST[$I]=$((${PORT[$I]}+1000)) EXTRAPORT_VM[$I]=$((${PORT[$I]}+2000)) ULTIMAPORT_HOST[$I]=$((${PORT[$I]}+3000)) ULTIMAPORT_VM[$I]=$((${PORT[$I]}+4000)) ALLOW_TUNDEV[$I]=yes EXTRA_PACKAGES[$I]=" iproute iptables" EXTRA_SERVICES[$I]=" (service whispers-vpn-service-type)" echo I=$(($I+1)) SOCKS=$I VM[$I]=socks echo "* VM: ${VM[$I]}" ALLOW_GATEWAY[$I]="yes" BOOT_WAIT[$I]=25 EXTRA_SERVICES[$I]="" PORT[$I]=$((10022+$I)) EXTRAPORT_HOST[$I]=$((${PORT[$I]}+1000)) EXTRAPORT_VM[$I]=$((${PORT[$I]}+2000)) ULTIMAPORT_HOST[$I]=$((${PORT[$I]}+3000)) ULTIMAPORT_VM[$I]=$((${PORT[$I]}+4000)) EXTRA_PACKAGES[$I]=" iproute iptables" echo I=$(($I+1)) CLIENT01=$I VM[$I]=client01 echo "* VM: ${VM[$I]}" PORT[$I]=$((10022+$I)) EXTRAPORT_HOST[$I]=$((${PORT[$I]}+1000)) EXTRAPORT_VM[$I]=$((${PORT[$I]}+2000)) ULTIMAPORT_HOST[$I]=$((${PORT[$I]}+3000)) ULTIMAPORT_VM[$I]=$((${PORT[$I]}+4000)) EXTRA_PACKAGES[$I]=" iproute iptables" EXTRA_SERVICES[$I]=" (service elogind-service-type) (service whispers-vpn-service-type (whispers-vpn-configuration (client? #t) (server-sshd-host \"10.0.2.2\") (server-sshd-port ${PORT[$VPN_SERVER]}) (forward-exit-port 22) (%auto-register? #f)))" echo I=$(($I+1)) CLIENT02=$I VM[$I]=client02 echo "* VM: ${VM[$I]}" PORT[$I]=$((10022+$I)) EXTRAPORT_HOST[$I]=$((${PORT[$I]}+1000)) EXTRAPORT_VM[$I]=$((${PORT[$I]}+2000)) ULTIMAPORT_HOST[$I]=$((${PORT[$I]}+3000)) ULTIMAPORT_VM[$I]=$((${PORT[$I]}+4000)) EXTRA_PACKAGES[$I]=" iproute iptables" EXTRA_SERVICES[$I]=" (service whispers-vpn-service-type (whispers-vpn-configuration (client? #t) (stealth? #t) (server-sshd-host \"10.0.2.2\") (server-sshd-port ${PORT[$VPN_SERVER]}) (forward-exit-port 22) (proxy-sshd-host \"10.0.2.2\") (proxy-sshd-port ${PORT[$VPN_SERVER]}) (%auto-register? #f)))" echo for I in ${!PORT[@]} do EXTRACONTENT[$I]="" if [ -v ALLOW_TUNDEV[$I] ] then EXTRACONTENT[$I]=" (extra-content \" PermitTunnel=point-to-point\")" fi GATEWAY[$I]="" if [ -v ALLOW_GATEWAY[$I] ] then GATEWAY[$I]=" (gateway-ports? #t)" fi WAIT[$I]=$BOOT_WAIT_DEFAULT if [ -v BOOT_WAIT[$I] ] then WAIT[$I]=${BOOT_WAIT[$I]} fi done echo echo "*** CONCATENATING SYSTEM CONFIGURATIONS" echo for I in ${!PORT[@]} do echo "* VM: ${VM[$I]}" echo "(use-modules (guix records) (gnu) (whispers services whispers vpn) (whispers services ssh-tunneler) (whispers services whispers) (whispers services ssh) (whispers services whispers ssh)) (use-service-modules networking desktop) (use-package-modules ssh networking linux scm-runciter) (operating-system (host-name \"${VM[$I]}\") (timezone \"Asia/Shanghai\") (locale \"en_US.utf8\") (bootloader (bootloader-configuration (bootloader grub-bootloader) (targets '(\"/dev/sda\")))) (file-systems (cons (file-system (device (file-system-label \"root\")) (mount-point \"/\") (type \"ext4\")) %base-file-systems)) (users %base-user-accounts) (packages (append %base-packages (list${EXTRA_PACKAGES[$I]}))) (services (append (list (service dhcp-client-service-type) (service openssh-service-type (openssh-configuration (permit-root-login #t) (allow-empty-passwords? #t) (openssh openssh-sans-x) (port-number 22)${GATEWAY[$I]}${EXTRACONTENT[$I]})) (service whispers-service-type) (service whispers-ssh-service-type (whispers-ssh-configuration (users-groups-keys-forwards (append (list (ssh-user-group-keys-forwards (user-and-group (whispers-user-group (user \"root\") (group \"root\"))) (keys '(\"root/.ssh/id_rsa\"))))))))${EXTRA_SERVICES[$I]}) %base-services)))" > /tmp/${VM[$I]}.scm echo done echo echo "*** INSTANCIATING VMs" echo for I in ${!PORT[@]} do echo "* VM: ${VM[$I]}" VMRUN[$I]=$(guix system vm /tmp/${VM[$I]}.scm) echo done echo echo "*** SILENTLY BOOTING VMs..." echo for I in ${!PORT[@]} do NICSTANCE=-"nic user,model=virtio-net-pci,hostfwd=tcp::${PORT[$I]}-:22,hostfwd=tcp::${EXTRAPORT_HOST[$I]}-:${EXTRAPORT_VM[$I]},hostfwd=tcp::${ULTIMAPORT_HOST[$I]}-:${ULTIMAPORT_VM[$I]}" if [ $I = $VPN_SERVER ] then for CURPORT in $VPNPORTS do NICSTANCE=${NICSTANCE},hostfwd=tcp::${CURPORT}-:${CURPORT} done fi echo "* VM: ${VM[$I]}" ${VMRUN[$I]} \ $NICSTANCE \ -display none & sleep ${WAIT[$I]} echo done echo echo "*** SETTING PASSWORDLESS LOGIN FOR WHISPERS USERS" echo echo "* VM: ${VM[$VPN_SERVER]}" dirtyssh ${PORT[$VPN_SERVER]} 'passwd -d whispers' echo echo "* VM: ${VM[$CLIENT01]}" dirtyssh ${PORT[$CLIENT01]} 'passwd -d whispers' echo echo "* VM: ${VM[$CLIENT02]}" dirtyssh ${PORT[$CLIENT02]} 'passwd -d whispers' echo echo function full_sshagent_reports_tests() { echo "*** GENERATING AND ADDING ROOT SSH PRIVATE KEYS" echo for I in ${!PORT[@]} do echo "* VM: ${VM[$I]}" dirtyssh ${PORT[$I]} 'ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa' dirtyssh ${PORT[$I]} \ 'herd -s '$WHISPERSSOCK' restart ssh' echo done echo echo "*** REPORTING SHEPHERD STATUS" echo for I in ${!PORT[@]} do echo "* VM: ${VM[$I]}" dirtyssh ${PORT[$I]} 'herd status' echo done echo echo "*** REPORTING NETWORK INTERFACES" echo for I in ${!PORT[@]} do echo "* VM: ${VM[$I]}" dirtyssh ${PORT[$I]} 'ip addr show' echo done echo echo "*** REPORTING ROUTES" echo for I in ${!PORT[@]} do echo "* VM: ${VM[$I]}" dirtyssh ${PORT[$I]} 'ip route' echo done echo tests_book_free_dummy_users $VPN_SERVER dummy dump-dumb dumber tests_register_unregister $CLIENT02 $CLIENT01 tests_manual_register_connect $CLIENT01 $CLIENT02 tests_disconnects_connects $CLIENT01 $CLIENT02 tests_disconnects_unregisters $CLIENT01 $CLIENT02 tests_direct_connects $CLIENT01 $CLIENT02 } if [[ foo$GO_PLAY_KIDS == foono ]] then full_sshagent_reports_tests echo fi echo "*** CONNECTING CLIENTS" echo dirtyssh ${PORT[$CLIENT01]} "herd -s $WHISPERSSOCK connect vpn" sleep $CONNECTSLEEP echo dirtyssh ${PORT[$CLIENT02]} "herd -s $WHISPERSSOCK connect vpn" sleep $CONNECTSLEEP echo echo echo "*** TESTS SUMMARRY" echo echo Successes: $YESCOUNT echo Failures: $NOCOUNT echo echo echo "You can ssh into the VMs from another terminal of this host" for I in ${!PORT[@]} do echo "* VM: ${VM[$I]}, port: ${PORT[$I]}" done echo read -n 1 -r -s -p "When done playing, press any key to halt the VMs..." echo echo echo "*** HALTING VMs" echo for I in ${!PORT[@]} do echo "* VM: ${VM[$I]}" dirtyssh ${PORT[$I]} halt echo done