connection_tether-ssh.sh 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. #!/bin/bash
  2. #
  3. # Copyright (c) 2019 Clementine Computing LLC.
  4. #
  5. # This file is part of PopuFare.
  6. #
  7. # PopuFare is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU Affero General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # PopuFare is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU Affero General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Affero General Public License
  18. # along with PopuFare. If not, see <https://www.gnu.org/licenses/>.
  19. #
  20. export BASEDIR="/home/bus"
  21. . $BASEDIR/bin/common_values.sh
  22. # Do this once at boot time, but do it again after a tunnel abort request...
  23. #
  24. generate_ssh_targets
  25. ssh_fail_counter=0
  26. debug_print()
  27. {
  28. echo $@
  29. }
  30. # This function goes and looks at the version dropfiles for packages, gathering their package names
  31. # and versions to report when we check in with the server to log that we connected, and at what firmware and
  32. # config revision.
  33. #
  34. output_versions()
  35. {
  36. for file in `ls $CHECKSUM_AND_VERSION_PATH/*.version`; do
  37. echo -n " `echo $file | sed -r 's/^.*\/(.*)\.version$/\1/'`=`cat $file`";
  38. done
  39. }
  40. # This function extracts a field from the network ID dropfile by name (also used by the checkin process)
  41. #
  42. output_net_ids_field()
  43. {
  44. field="$1"
  45. cat $NETWORK_ID_DROPFILE | grep "$field" | cut -d'=' -f2 | xargs -n1 echo -n
  46. }
  47. # This function attempts to check in with the version server and report hardware and network serial numbers
  48. # so that us sysadmin types can see each time a unit attached to the network, which unit it was, and what software
  49. # it was running at the time.
  50. #
  51. perform_post_connect_checkin()
  52. {
  53. if [ -f $SERIAL_NUM_FILE ]; then
  54. busunitnum="`cat $SERIAL_NUM_FILE 2> /dev/null`"
  55. fi
  56. equipnum="`cat $EQUIP_NUM_FILE 2> /dev/null || echo 0`"
  57. version="`output_versions`"
  58. imei="`output_net_ids_field IMEI`"
  59. imsi="`output_net_ids_field IMSI`"
  60. mac="`output_net_ids_field ETH0`"
  61. # Send these gathered data to the update daemon. The leading '#' tells the server that this is a
  62. # checkin, not an update request.
  63. #
  64. echo -e "#$busunitnum\t$equipnum\t$mac\t$imei\t$imsi\t$version" | nc -q1 localhost $UPDATE_DAEMON_PORT
  65. }
  66. # This function generates the server->client port forwards to allow a sysadmin to log into any unit that is on
  67. # the network by equipment number, serial number, or bus number. The three parameters are:
  68. #
  69. # 1: The path to the file containing the identifying number
  70. # 2: The base port number on the remote server to add the identifying number to to get the server-side port that will
  71. # forward to port 22 (sshd) on the client side.
  72. # 3: An optional parameter which if present is taken as a set of command line flags to cut to apply to the contents
  73. # of the file specified by $1 to extract the numeric component (for instance, serial numbers may be in the form XYZ-1234
  74. # in which case it's really the 1234 part we're after...
  75. #
  76. generate_reverse_phonehome_component()
  77. {
  78. file="$1"
  79. base="$2"
  80. cut_cmdline="$3"
  81. # Make sure the candidate dropfile exists
  82. #
  83. if [ -f "$file" ]; then
  84. if [ -n "$cut_cmdline" ]; then
  85. # Grab the desired substring
  86. #
  87. num="`cat $file | cut $cut_cmdline`"
  88. else
  89. # Grab its contents
  90. #
  91. num="`cat $file`";
  92. fi
  93. # Make sure those contents are indeed numeric...
  94. #
  95. if (echo "$num" | egrep -q '^[0-9]+$'); then
  96. # Make sure that number is within an acceptable range so as not to overflow
  97. #
  98. if [ "$num" -gt "0" -a "$num" -le "$REVERSE_PHONE_HOME_MAX_TOKEN" ]; then
  99. echo -n " -R$((base + num)):localhost:22";
  100. fi
  101. fi
  102. fi
  103. }
  104. # This function calls the above component function for each identifying number we want to do a port forward based on...
  105. #
  106. generate_reverse_phonehome_string()
  107. {
  108. # If the reverse phone home feature is disabled, return without printing any commandline args
  109. #
  110. if [ "$REVERSE_PHONE_HOME" -eq "0" ]; then return; fi
  111. generate_reverse_phonehome_component $EQUIP_NUM_FILE $REVERSE_PHONE_HOME_EQNUM_BASE
  112. generate_reverse_phonehome_component $SERIAL_NUM_FILE $REVERSE_PHONE_HOME_SERIALNUM_BASE "-d- -f3"
  113. }
  114. # This function performs teardown on a dead ssh connection, and increments the ssh connect failure counter. If
  115. # that counter has reached its maximum value, we force pppd down and try a clean redial. Otherwise, we just sleep and
  116. # try again.
  117. #
  118. clean_up_after_tunnel_teardown()
  119. {
  120. # If the tunnel was intentionally aborted for the purpose of switching servers
  121. #
  122. if [ -f $TUNNEL_ABORT_DROPFILE ]; then
  123. debug_print "SSH tunnel aborted, removing dropfiles..."
  124. #Remove the dropfiles...
  125. /bin/rm -f $TUNNEL_DROPFILE $SSH_TUNNEL_PIDFILE
  126. #Generate new ssh target from server config dropfiles
  127. generate_ssh_targets
  128. /bin/rm -f $TUNNEL_ABORT_DROPFILE
  129. #Reset the failure counter
  130. ssh_fail_counter=0
  131. #Sleep until it is time to try again
  132. /bin/sleep $SLEEP_AFTER_TUNNEL_ABORT
  133. fi
  134. }
  135. while true; do
  136. #If we've just been asked to abort the SSH tunnel
  137. if [ -f $TUNNEL_ABORT_DROPFILE ]; then
  138. #Generate new ssh target from server config dropfiles
  139. generate_ssh_targets
  140. /bin/rm -f $TUNNEL_ABORT_DROPFILE
  141. # Reset the failure counter
  142. #
  143. ssh_fail_counter=0
  144. fi
  145. # If we have no active tunnel already...
  146. #
  147. if [ ! -f $TUNNEL_DROPFILE ]; then
  148. debug_print "Attempting to establish SSH tunnel... (Attempt number $((ssh_fail_counter + 1)))"
  149. s=`generate_reverse_phonehome_string`
  150. debug_print ">> ssh $SSH_OPTIONS -i $SSH_IDENTITY $SSH_FORWARDS $s -p $SSH_PORT $SSH_TARGET "
  151. # Attempt to create our tunnel... (incliding (if REVERSE_PHONE_HOME != 0) reverse phone home support
  152. #
  153. ssh $SSH_OPTIONS -i $SSH_IDENTITY $SSH_FORWARDS `generate_reverse_phonehome_string` -p $SSH_PORT $SSH_TARGET &
  154. # Remember its PID
  155. #
  156. echo "$!" > $SSH_TUNNEL_PIDFILE
  157. # Wait a few seconds to allow SSH negotiations
  158. #
  159. /bin/sleep $SLEEP_BEFORE_TUNNEL_TEST
  160. debug_print -n "Testing our new tunnel..."
  161. # Test to see if our tunnel is really up...
  162. #
  163. if [ "`nc localhost $HELLO_DAEMON_PORT < /dev/null`" = $HELLO_DAEMON_MESSAGE ]; then
  164. debug_print " It works."
  165. ssh_fail_counter=0
  166. debug_print "Checking in with server to report net IDs and package versions... "
  167. perform_post_connect_checkin
  168. debug_print "Touching dropfile and waiting for SSH to terminate..."
  169. # Touch our dropfile indicating the tunnel is up...
  170. #
  171. /bin/touch $TUNNEL_DROPFILE
  172. # and wait for the the SSH client process to end
  173. #
  174. wait `cat $SSH_TUNNEL_PIDFILE`
  175. # Clean Up...
  176. #
  177. clean_up_after_tunnel_teardown
  178. else
  179. debug_print " No luck..."
  180. debug_print "Issuing kill to SSH client...."
  181. # Kill the defunct and/or too slow to use SSH client
  182. #
  183. /bin/kill `cat $SSH_TUNNEL_PIDFILE`
  184. # Wait for the process to terminate
  185. #
  186. wait `cat $SSH_TUNNEL_PIDFILE`
  187. # Clean Up...
  188. #
  189. clean_up_after_tunnel_teardown
  190. fi
  191. else
  192. # This means we _think_ we have an SSH tunnel, but it's not one we set up...
  193. #
  194. debug_print -n "We seem to already have a pre-existing tunnel... Monitoring it."
  195. # Loop and periodically test this tunnel... When this condition fails, we're done...
  196. #
  197. while [ "`nc localhost $HELLO_DAEMON_PORT < /dev/null`" = $HELLO_DAEMON_MESSAGE ]; do
  198. # Sleep for a while before testing this tunnel again...
  199. #
  200. /bin/sleep $SLEEP_MONITORING_TUNNEL
  201. done
  202. # Clean Up...
  203. #
  204. clean_up_after_tunnel_teardown
  205. fi
  206. done