#!/bin/bash # # @author Gerhard Steinbeis (info [at] tinned-software [dot] net) # @copyright Copyright (c) 2013 version=0.6.4 # @license http://opensource.org/licenses/GPL-3.0 GNU General Public License, version 3 # @package net # # Configuration file for the ssh-tunnel-manager script. Default location for # the config file is /etc/ssh-tunnel-manager.conf or if that does not exist, # the ssh-tunnel-manager.conf in the same directory as the script itself. # # # Define names for the tunnel to identify them. The list needs to be configured # in the same order as the tunnel config in the TUNELS list. # #TUNNEL_NAMES=( # "Tunnel-A" # "Tunnel-B" #) # # Ths TUNNELS array is used to configure the individual tunnels. Each # configuration entry needs to follow the SSH options. An example of # how such a configuration line might look like is listed here. # # TUNNELS=( # "-p 1234 username@host1.example.com -L 10001:127.0.0.1:3306 -L 10011:127.0.0.1:27017" # "-p 1234 username@host2.example.com -L 10002:127.0.0.1:3306 -L 10012:127.0.0.1:27017" #) # # The RECONNECT_TIMER is used in case of a tunnel connection to be lost. After # the script is detecting that the connection was lost, the time defined the # time to wait before the the script tries to reconnect the tunnel. # RECONNECT_TIMER=10 # # The LOGFILE setting defines the path of the logfile. You have the possibility to use # the $SCRIPT_PATH variable to define the path of the logfile to be the same # as the script directory. # LOGFILE="" # # This DBG setting is adding additional details to the logfile. The values are 0 # to hide the extra log content and 1 to show it. # DBG=0 # # Default configuration file to load settings from can be defined here. # DEFAULT_CONFIG="ssh-tunnel-manager.conf" # # Non Config values # # Get script directory SCRIPT_PATH="$(cd $(dirname $0);pwd -P)" LOGFILE="$SCRIPT_PATH/ssh-tunnel-manager.log" # # Parse all parameters # HELP=0 TERMINATE=0 COMMAND_INDEX_NAME='' while [ $# -gt 0 ]; do case $1 in # General parameter -h|--help) HELP=1 shift ;; -v|--version) echo echo "Copyright (c) 2013 Tinned-Software (Gerhard Steinbeis)" echo "License GNUv3: GNU General Public License version 3 <http://opensource.org/licenses/GPL-3.0>" echo echo "`basename $0` version $version" echo exit 0 ;; # specific parameters --config) # load settings file if [[ -f "$2" ]]; then . $2 CONFIG_FILE=$2 CONFIG_LOADED=1 else HELP=1 echo "ERROR: Specified config file cound not be found." fi shift 2 ;; # specific parameters start) COMMAND='start' shift if [[ $# -gt 0 ]]; then COMMAND_INDEX_NAME=$1 shift fi ;; stop) COMMAND='stop' shift if [[ $# -gt 0 ]]; then COMMAND_INDEX_NAME=$1 shift fi ;; restart) COMMAND='restart' shift if [[ $# -gt 0 ]]; then COMMAND_INDEX_NAME=$1 shift fi ;; status) COMMAND='status' shift if [[ $# -gt 0 ]]; then COMMAND_INDEX_NAME=$1 shift fi ;; show) COMMAND='show' shift ;; manage) COMMAND='manage' INDEX=$2 shift 2 ;; # undefined parameter *) echo "ERROR: Unknown option '$1'" HELP=1 shift break ;; esac done # check if a command has been provided if [[ -z "${COMMAND}" ]]; then if [ "$HELP" -ne "1" ]; then echo "ERROR: No command defined in parameter list." HELP=1 fi fi # Load the default configuration file if no other configuration file is provided if [[ -z "$CONFIG_LOADED" ]]; then # Check if configuration file exists if [[ -f "/etc/$DEFAULT_CONFIG" ]]; then . /etc/$DEFAULT_CONFIG CONFIG_FILE=/etc/$DEFAULT_CONFIG else if [[ -f "$SCRIPT_PATH/$DEFAULT_CONFIG" ]]; then . $SCRIPT_PATH/$DEFAULT_CONFIG CONFIG_FILE=$SCRIPT_PATH/$DEFAULT_CONFIG fi fi fi # check if a tunnel configuration has been provided if [[ "${#TUNNELS[@]}" -lt "1" && ! -z "${COMMAND}" ]]; then if [ "$HELP" -ne "1" ]; then echo "ERROR: No tunnel configuration found." HELP=1 fi fi # show help message if [ "$HELP" -eq "1" ]; then echo echo "Copyright (c) 2013 Tinned-Software (Gerhard Steinbeis)" echo "License GNUv3: GNU General Public License version 3 <http://opensource.org/licenses/GPL-3.0>" echo echo "This script is used to setup multiple ssh tunnels and manage to keep " echo "them alive. This script will launch monitoring instances to keep the " echo "individual tunnels alive. See the configuration file for more details " echo "about the configuration." echo echo "Usage: `basename $0` [-hv] [--config filename.conf] [start|stop|status|restart|show] [tunnel-ID]" echo " -h --help print this usage and exit" echo " -v --version print version information and exit" echo " --config Configuration file to read parameters from" echo " start Start the ssh tunnels configured" echo " stop Stop the ssh tunnels configured" echo " status Check the status of the ssh tunnel" echo " show Show the ssh tunnel configuration and there tunnel-ID" echo " restart Restart the ssh tunnels configured" echo " tunnel-ID To start/stop/restart/status one specific tunnel identified by its ID" echo exit 1 fi # # Function to print out a string including a time and date info at the # beginning of the line. If the string is empty, only the timestamp is printed # out. # # @param $1 The string to print out # @param $2 (optional) Option to echo like ">>logfile.log" # function echotime { TIME=`date "+[%Y-%m-%d %H:%M:%S]"` echo -e "$TIME - $@" >>$LOGFILE } # # This function will search the index of the name provided to this function. # function get_id_from_name { for (( idx=0; idx<${#TUNNEL_NAMES[@]}; idx++ )) do if [[ "$1" == "${TUNNEL_NAMES[$idx]}" ]] then echo $idx return fi done echo -1 } # Execute function signal_terminate() receiving TERM signal # # Function to print out a string including a time and date info at the # beginning of the line. If the string is empty, only the timestamp is printed # out. # # @param $1 The string to print out # @param $2 (optional) Option to echo like ">>logfile.log" # function signal_terminate { echotime "MANAGER - Received TERM for tunnel ${TUNNEL_NAMES[$INDEX]} (ID $INDEX)" TERMINATE=1 } trap 'signal_terminate' TERM SCRIPT_PID=$$ # # start procedure according to the action # case $COMMAND in restart) echotime "COMM - Execute RESTART procedure ... " $0 --config $CONFIG_FILE stop $COMMAND_INDEX_NAME sleep 2 $0 --config $CONFIG_FILE start $COMMAND_INDEX_NAME echotime "COMM - Execute RESTART procedure ... Done" echotime "" ;; stop) echotime "COMM - Execute STOP procedure ... " # check if a tunnel index has been provided if [[ "$COMMAND_INDEX_NAME" != '' ]]; then COMMAND_INDEX=$(get_id_from_name "$COMMAND_INDEX_NAME") if [[ "$COMMAND_INDEX" -eq "-1" ]]; then echo "The tunnel with the name '$COMMAND_INDEX_NAME' can not be found." exit 1 fi IDX_START=$COMMAND_INDEX IDX_END=$((COMMAND_INDEX+1)) else IDX_START=0 IDX_END=${#TUNNELS[@]} fi for (( idx=$IDX_START; idx<$IDX_END; idx++ )); do # notify "manage" script of terminate request. This avoids the restart of the tunnel RESULT_PID=`ps aux | grep -v grep | grep "$0 --config $CONFIG_FILE manage $idx" | awk '{print $2}' | tr '\n' ' '` [ "$DBG" -gt "0" ] && echotime "STOP - *** DBG-CMD: ps aux | grep -v grep | grep \"$0 --config $CONFIG_FILE manage $idx\" | awk '{print \$2}'" for PID in $RESULT_PID; do kill $PID &>/dev/null done echotime "STOP - Stop sent manager of tunnel '${TUNNEL_NAMES[$idx]}' (ID $idx) ... PID: $RESULT_PID" # Terminate the ssh tunnel processes. RESULT_PID=`ps aux | grep -v grep | grep "ssh -N ${TUNNELS[$idx]}" | awk '{print $2}' | tr '\n' ' '` [ "$DBG" -gt "0" ] && echotime "STOP - *** DBG-CMD: ps aux | grep -v grep | grep \"ssh -N ${TUNNELS[$idx]}\" | awk '{print \$2}'" for PID in $RESULT_PID; do kill $PID &>/dev/null done echotime "STOP - Stopped tunnel '${TUNNEL_NAMES[$idx]}' (ID $idx) ... PID: $RESULT_PID" # check if the tunnels really down sleep "0.3" TUNNELS_COUNT=0 TMANAGER_COUNT=0 TUNNELS_COUNT=`ps aux | grep -v grep | grep "ssh -N ${TUNNELS[$idx]}" | awk '{print $2}' | wc -l` TMANAGER_COUNT=`ps aux | grep -v grep | grep "$0 --config $CONFIG_FILE manage $idx" | awk '{print $2}' | wc -l` if [[ "$TUNNELS_COUNT" -lt "1" ]] && [[ "$TUNNELS_COUNT" -lt "1" ]]; then echo "Stopping tunnel '${TUNNEL_NAMES[$idx]}' ... Done" else echo "Stopping tunnel '${TUNNEL_NAMES[$idx]}' ... Failed" fi done echotime "COMM - Execute STOP procedure ... Done" echotime "" ;; status) echotime "COMM - Execute STATUS procedure ... " # check if a tunnel index has been provided if [[ "$COMMAND_INDEX_NAME" != '' ]]; then COMMAND_INDEX=$(get_id_from_name "$COMMAND_INDEX_NAME") if [[ "$COMMAND_INDEX" -eq "-1" ]]; then echo "The tunnel with the name '$COMMAND_INDEX_NAME' can not be found." exit 1 fi IDX_START=$COMMAND_INDEX IDX_END=$((COMMAND_INDEX+1)) else IDX_START=0 IDX_END=${#TUNNELS[@]} fi EXIT_CODE=0 for (( idx=$IDX_START; idx<$IDX_END; idx++ )); do # get the list of processs RESULT=0 RESULT=`ps aux | grep -v grep | grep "ssh -N ${TUNNELS[$idx]}" | wc -l` # show the result if [[ "$RESULT" -gt "0" ]]; then echotime "STATUS - Status of Tunnel '${TUNNEL_NAMES[$idx]}' (ID $idx) is ... running" echo "Status of Tunnel '${TUNNEL_NAMES[$idx]}' is ... running" else echotime "STATUS - Status of Tunnel '${TUNNEL_NAMES[$idx]}' (ID $idx) is ... NOT running" echo "Status of Tunnel '${TUNNEL_NAMES[$idx]}' is ... NOT running" EXIT_CODE=1 fi done echotime "COMM - Execute STATUS procedure ... Done" echotime "" exit $EXIT_CODE ;; show) for (( idx=0; idx<${#TUNNELS[@]}; idx++ )); do # show the tunnel config echo "Configuration of Tunnel '${TUNNEL_NAMES[$idx]}' ... ${TUNNELS[$idx]}" done ;; start) echotime "COMM - Execute START procedure ... " # check if a tunnel index has been provided if [[ "$COMMAND_INDEX_NAME" != '' ]]; then COMMAND_INDEX=$(get_id_from_name "$COMMAND_INDEX_NAME") if [[ "$COMMAND_INDEX" -eq "-1" ]]; then echo "The tunnel with the name '$COMMAND_INDEX_NAME' can not be found." exit 1 fi IDX_START=$COMMAND_INDEX IDX_END=$((COMMAND_INDEX+1)) else IDX_START=0 IDX_END=${#TUNNELS[@]} fi for (( idx=$IDX_START; idx<$IDX_END; idx++ )); do RESULT_PID=0 RESULT_PID=`ps aux | grep -v grep | grep "$0 --config $CONFIG_FILE manage $idx" | awk '{print $2}' | tr '\n' ' '` if [[ ! -z $RESULT_PID ]]; then echotime "START - Already running tunnel '${TUNNEL_NAMES[$idx]}' (ID $idx) ... PID: $RESULT_PID" echo "Starting tunnel '${TUNNEL_NAMES[$idx]}' ... Already running" else $0 --config $CONFIG_FILE manage $idx & sleep "0.2" RESULT_PID=`ps aux | grep -v grep | grep "$0 --config $CONFIG_FILE manage $idx" | awk '{print $2}' | tr '\n' ' '` [ "$DBG" -gt "0" ] && echotime "START - *** DBG-CMD: ps aux | grep -v grep | grep \"$0 --config $CONFIG_FILE manage $idx\" | awk '{print \$2}'" echotime "START - Starting tunnel '${TUNNEL_NAMES[$idx]}' (ID $idx) ... PID: $RESULT_PID" echo "Starting tunnel '${TUNNEL_NAMES[$idx]}' ... Done" fi # sleep before every cycle to aviod overloading done echotime "COMM - Execute START procedure ... Done" echotime "" ;; manage) echotime "MANAGE - Connecting tunnel ${TUNNEL_NAMES[$INDEX]} (ID $INDEX) with parameters ... ${TUNNELS[$INDEX]}" while [ "$TERMINATE" -eq "0" ] do SSH_RESULT=`ssh -N ${TUNNELS[$INDEX]} 2>&1` if [[ "$TERMINATE" -eq "0" ]]; then echotime "MANAGE - Detected tunnel '${TUNNEL_NAMES[$INDEX]}' (ID $INDEX) disconnected. $SSH_RESULT" sleep $RECONNECT_TIMER echotime "MANAGE - Reconnecting tunnel '${TUNNEL_NAMES[$INDEX]}' (ID $INDEX) with parameters ... ${TUNNELS[$INDEX]}" else echotime "MANAGE - Shutdown manager for tunnel '${TUNNEL_NAMES[$INDEX]}' (ID $INDEX) " fi done ;; esac