#!/bin/bash # SSH Tunneling script 1.0, by Macie # Source code: www.linuxfusion.net/macie/Scripts/ssh_tunnel # Purpose: ssh tunneling with failover possibility clear if [ `id -un` != "root" ]; then echo "You must be root to run $0" exit 0 fi # Configuration iptables="/sbin/iptables" screen="/usr/bin/screen" server1="host1" server2="host2" ssh_port1="22" ssh_port2="22" user="user" eth_card="lo" vip_port="1080" dns="0.0.0.0 0.0.0.0" if [ ! -x "$iptables" ]; then echo "Missing iptables bin" exit 0 fi if [ ! -x "$screen" ]; then echo "Missing screen bin" exit 0 fi if [ "$1" == "-k" ]; then if [ -f "/var/run/ssh_tunnel.pid" ]; then process=`cat /var/run/ssh_tunnel.pid` kill -15 $process echo "ssh_tunnel stopped" exit 0 else echo "Nothing to stop" fi fi if [ -f "/var/run/ssh_tunnel.pid" ]; then printf "SSH Tunnel already up on pid: " cat /var/run/ssh_tunnel.pid echo "Try: $0 -k" exit 0 fi # Server 1 port (++1) let vip_fo_s1=vip_port+1 # Server 2 port (++2) let vip_fo_s2=vip_port+2 function mrule() { if [ "$mrule" == "s1_start" ]; then vip_from=$vip_fo_s1 mexe="-A" fi if [ "$mrule" == "s1_stop" ]; then vip_from=$vip_fo_s1 mexe="-D" fi if [ "$mrule" == "s2_start" ]; then vip_from=$vip_fo_s2 mexe="-A" fi if [ "$mrule" == "s2_stop" ]; then vip_from=$vip_fo_s2 mexe="-D" fi mrule="$iptables -t nat $mexe POSTROUTING -o $eth_card -j SNAT --to-source 127.0.0.1; $iptables $mexe PREROUTING -t nat -p tcp -i $eth_card --dport $vip_port -j DNAT --to 127.0.0.1:$vip_from; $iptables $mexe FORWARD -p tcp -i $eth_card -d 127.0.0.1 --dport $vip_from -j ACCEPT; $iptables $mexe PREROUTING -t nat -p udp -i $eth_card -d 127.0.0.1 --dport $vip_port -j DNAT --to 127.0.0.1:$vip_from; $iptables $mexe FORWARD -p udp -i $eth_card -d 127.0.0.1 --dport $vip_from -j ACCEPT; echo 1 > /proc/sys/net/ipv4/ip_forward" echo "$mrule" > /tmp/.viprule chmod 700 /tmp/.viprule /tmp/.viprule rm -f /tmp/.viprule } echo "$$" > /var/run/ssh_tunnel.pid trap cleanup 1 2 3 6 15 function cleanup() { printf "Event: ";date +"%T %D" echo ' >> Exit requested!' mv -f /etc/resolv.conf.vpn /etc/resolv.conf if [ ! $pid1 ]; then echo " >> No $server1 tunnel present" else kill -9 $pid1 sleep 1 if [ -d "/proc/$pid1" ]; then kill -9 $pid1 fi echo " >> $server1 tunnel stopped" fi if [ ! $pid2 ]; then echo " >> No $server2 tunnel present" else kill -9 $pid2 sleep 1 if [ -d "/proc/$pid2" ]; then kill -9 $pid2 fi echo " >> $server2 tunnel stopped" fi rm -f /var/run/ssh_tunnel.pid screen -wipe 1>/dev/null 2>/dev/null if [ ! "$vip_on" ]; then echo ' >> No VIP to clean. Done.' exit 0 fi if [ "$vip_on" == server1 ]; then echo " >> VIP on $server1" mrule="s1_stop"; mrule; echo ' >> Now deactivated' exit 0 fi if [ "$vip_on" == server2 ]; then echo " >> VIP on $server2" mrule="s2_stop"; mrule; echo ' >> Now deactivated' exit 0 fi } while true; do # If server1 died failover to server2 pid1=`ps aux |grep SCREEN |grep $server1 |awk '{print $2}'` pid2=`ps aux |grep SCREEN |grep $server2 |awk '{print $2}'` if [ "$pid1_on" != "$pid1" ]&&[ "$pid1" ]; then pid1_on=$pid1 printf "Event: ";date +"%T %D" echo " >> Found new $server1 tunnel on $pid1" fi if [ ! "$pid1" ]&&[ "$pid2" ]&&[ "$vip_on" == "server1" ]; then printf "Event: ";date +"%T %D" echo " >> Failing VIP over to $server2" # Stop forwarding to server1 mrule="s1_stop"; mrule; # Start forwarding to server 2 mrule="s2_start"; mrule; echo " >> Failover accomplished" # Lets reopen server 1 connection echo " >> Respawning $server1 tunnel" screen -d -m -S $server1 ssh -L $vip_fo_s1:localhost:* $user@$server1 -p $ssh_port1 sleep 1 vip_on="server2" fi # If server2 died failover to server1 pid1=`ps aux |grep SCREEN |grep $server1 |awk '{print $2}'` pid2=`ps aux |grep SCREEN |grep $server2 |awk '{print $2}'` if [ "$pid2_on" != "$pid2" ]&&[ "$pid2" ]; then pid2_on=$pid2 printf "Event: ";date +"%T %D" echo " >> Found new $server2 tunnel on $pid2" fi if [ ! "$pid2" ]&&[ "$pid1" ]&&[ "$vip_on" == "server2" ]; then printf "Event: ";date +"%T %D" echo " >> Failing VIP over $server1" # Stop forwarding to server2 mrule="s2_stop"; mrule; # Start forwarding to server 1 mrule="s1_start"; mrule; echo " >> Failover accomplished" # Lets reopen server 2 connection echo " >> Respawning $server2 tunnel" screen -d -m -S $server2 ssh -L $vip_fo_s2:localhost:* $user@$server2 -p $ssh_port2 sleep 1 vip_on="server1" fi if [ "$pid1" ]&&[ ! "$pid2" ]&&[ "$vip_on" == "server1" ]; then printf "Event: ";date +"%T %D" echo " >> VIP is OK but failover died" echo " >> Restarting current failover ($server2)" screen -d -m -S $server2 ssh -L $vip_fo_s2:localhost:* $user@$server2 -p $ssh_port2 fi if [ "$pid2" ]&&[ ! "$pid1" ]&&[ "$vip_on" == "server2" ]; then printf "Event: ";date +"%T %D" echo " >> VIP is OK but failover died" echo " >> Restarting current failover ($server1)" screen -d -m -S $server1 ssh -L $vip_fo_s1:localhost:* $user@$server1 -p $ssh_port1 fi # If both connections are dead... if [ ! "$pid1" ] && [ ! "$pid2" ]; then printf "Event: ";date +"%T %D" echo " >> Both tunnels are dead!" echo " >> Checking VIP" if [ "$vip_on" == "server1" ]; then mrule="s1_stop"; mrule; echo " >> VIP on $server1 stopped" fi if [ "$vip_on" == "server2" ]; then mrule="s2_stop"; mrule; echo " >> VIP on $server2 stopped" fi if [ ! "$vip_on" ]; then echo " >> No active VIP present" fi # Lets open both tunnels echo " >> Starting tunnel to \"$server1\"" screen -d -m -S $server1 ssh -L $vip_fo_s1:localhost:* $user@$server1 -p $ssh_port1 echo " >> Starting tunnel to \"$server2\"" screen -d -m -S $server2 ssh -L $vip_fo_s2:localhost:* $user@$server2 -p $ssh_port2 echo " >> Starting VIP" mrule="s1_start"; mrule; vip_on="server1" echo " >> Setting DNS" mv -f /etc/resolv.conf /etc/resolv.conf.vpn echo "# Generated by ssh_tunnel" > /etc/resolv.conf for d in $dns; do echo "nameserver $d" >> /etc/resolv.conf done echo " >> All set!" fi sleep 1 done