Shell脚本:sshproxy,调用autossh连接Linux服务器,自动创建SSH动态端口代理隧道

stevehe 2021年08月02日 238次浏览
#!/bin/bash
SCRIPTPATH=$(realpath $0)
#执行autossh,自动创建SSH动态端口代理隧道

display_usage() {
	echo -e "$SCRIPTPATH\n"
    echo -e "\t执行autossh,自动创建SSH动态端口代理隧道."
	echo -e "\t默认绑定端口:0.0.0.0:8989."
	echo -e "\t参数1目的主机的为必选参数,其余为可选参数."
    echo -e "\nUsage:\n\tsshproxy [*hostname] [local proxy port] [kill exists process] [use http proxy or not(mustbe: nohttp)]"
	echo -e "Example1:\n\tsshproxy racknerd true"
	echo -e "Example2:\n\tsshproxy racknerd nohttp"
	echo -e "Example3:\n\tsshproxy racknerd 7171 true"
	echo -e "Example4:\n\tsshproxy racknerd 7171 nohttp"
	echo -e "Example5:\n\tsshproxy racknerd 7171 true nohttp"
	echo -e "\n附加用法:\n"
	echo -e "\t1.sshproxy kill  检测并杀死所有的代理进程"
	echo -e "\t2.sshproxy list  列出当前活动的所有代理"
	echo -e "\t3.sshproxy test  检测已有代理端口的可用性\n"
}
# if less than two arguments supplied, display usage
if [  $# -lt 1 ]
then
    display_usage
    exit 1
fi

# check whether user had supplied -h or --help . If yes display usage
if [[ ( $* == "--help") ||  $* == "-h" ]]
then
    display_usage
    exit 0
fi

do_KillProcess() {
	#首先尝试查询和保存代理各个端口
	local processPID=$(ps aux|grep 'ssh-for-proxy'|awk '{print $4}'|tr '\n' ' ')
	if [ ! -z "$processPID" ];then
		declare -a proxyPort ## 定义数组,存储找到的多个代理进程本地端口
		for pid in $processPID;
		do
			local port=$(netstat -ano|grep $pid|grep 'LISTENING'|grep 0.0.0.0|awk '{print $2;exit}'|awk -F ':' '{print $NF}')
			#echo "$port"
			proxyPort=(${proxyPort[@]} $port)
		done
	fi
	echo "kill Process..."
	killall autossh &> /dev/null
	killall ssh-for-proxy &> /dev/null
	killall goproxy-for-ssh &> /dev/null
	## 关闭涉及各端口的TCP连接,以保万全
	echo "kill Connections..."
	for port in ${proxyPort[@]};
		do
			#echo "Close Port connection $port"
			gsudo `cygpath -w /v/bin/cports` /close '*' $port '*' '*'
		done
	return
}

do_ListProxy() {
	local psList=$(ps aux|grep 'ssh-for-proxy')
	local pids=$(echo "$psList"|awk '{print $1}')
	local winpids=$(echo "$psList"|awk '{print $4}')
	declare -a RemoteAddrs
	declare -a ListenAddrs
	declare -a ProxyAddrs
	declare -a HostAlias #查找 ~/.ssh/config以确定主机别名
	for pid in $(echo "$winpids");
	do
		local remoteAddr=$(netstat -ano -P TCP|grep $pid|grep 'ESTABLISHED'|awk '{print $3;exit}') #仅打印一行即退出
		[ -z "$remoteAddr" ] && remoteAddr="Unknown:Unknown\t"
		RemoteAddrs=(${RemoteAddrs[@]} $remoteAddr)
		local remoteIP=$(echo $remoteAddr|cut -d ':' -f 1)
		local hostName=$(python3 /v/bin/sshfindip.py $remoteIP|sed -n '1p')
		if [ ! -z "$hostName" ];then
			local hostName=$(echo "$hostName"|tr -s ' '|tr ' ' ','|sed -r 's/^Host\,//i')
		else
			local hostName="*Unknown*"
		fi
		HostAlias=(${HostAlias[@]} "$hostName")
		local listenAddr=$(netstat -ano -P TCP|grep $pid|grep 'LISTENING'|grep '0.0.0.0'|awk '{print $2;exit}') #仅打印一行即退出
		[ -z "$listenAddr" ] && listenAddr="Unknown:Unknown\t"
		ListenAddrs=(${ListenAddrs[@]} $listenAddr)
		local proxyAddr="socks5://127.0.0.1:"$(echo $listenAddr|awk -F ':' '{print $NF}')
		ProxyAddrs=(${ProxyAddrs[@]} $proxyAddr)
	done
	declare -a unixPids
	declare -a winPids
	local unixPids=($(echo "$pids"|tr '\n' ' '))
	local winPids=($(echo "$winpids"|tr '\n' ' '))
	local proxyIndex=0
	local showType="listen"
	[ ! -z "$2" ] && showType="proxy"
	if [[ $showType == "listen" ]];then
		echo -e "PID\tWINPID\tRemote Address\t\tLocal Address\tHostName\n"
	else
		echo -e "PID\tWINPID\tRemote Address\t\tProxy Address\t\tHostName\n"
	fi
	for proxyNum in ${RemoteAddrs[@]};
	do
		#本地信息显示方式:代理地址 OR 监听地址
		#local showLocal=${ListenAddrs[$proxyIndex]} ##本地监听地址
		#local showLocal=${ProxyAddrs[$proxyIndex]} ##Socks代理地址
		if [[ $showType == "listen" ]];then
			local showLocal=${ListenAddrs[$proxyIndex]}
		else
			local showLocal=${ProxyAddrs[$proxyIndex]}
		fi
		echo -e "${unixPids[$proxyIndex]}\t${winPids[$proxyIndex]}\t$proxyNum\t$showLocal\t${HostAlias[$proxyIndex]}"
		let proxyIndex+=1
	done
}

do_TestProxy() {
	# 当前已适配开启多个Proxy进程的情况
	local processPID=$(ps aux|grep 'ssh-for-proxy'|awk '{print $4}'|tr '\n' ' ')
	if [ ! -z "$processPID" ];then
		#local proxyPort=$(netstat -ano|grep $processPID|grep 'LISTENING'|grep 0.0.0.0|awk '{print $2;exit}'|awk -F ':' '{print $NF}')
		#local proxyPort=$(netstat -ano|grep $processPID|grep 'LISTENING'|grep 0.0.0.0|awk '{print $2}')
		declare -a proxyPort ## 定义数组,存储找到的多个代理进程本地端口
		for pid in $processPID;
		do
			local port=$(netstat -ano|grep $pid|grep 'LISTENING'|grep 0.0.0.0|awk '{print $2;exit}'|awk -F ':' '{print $NF}')
			#echo "$port"
			proxyPort=(${proxyPort[@]} $port)
		done
	fi
	#echo "${proxyPort[@]}" ##数组:存储找到的所有代理的端口
	
	if [ ! -z "$processPID" -a ! -z "$proxyPort" ];then
		for dstport in ${proxyPort[@]};
		do
			echo "proxyPort:$dstport"
			echo "Proxy Address:127.0.0.1:$dstport"
			echo "Full Proxy Address:socks5://127.0.0.1:$dstport"
			nc -w 2 -v 127.0.0.1 $dstport
			curl --connect-timeout 3 -sS -x socks5://127.0.0.1:$dstport 'http://v.ynit.top/ipfull/'
			echo -e "\n"
		done
		return 0
	else
		echo "proxyPort not Found!"
	fi
}

if [[ $* == "kill" ]]
then
    do_KillProcess $@
    exit 0
fi

if [[ $* =~ ^list$ || $* =~ "list " ]]
then
    do_ListProxy $@
    exit 0
fi

if [[ $* == "test" ]]
then
    do_TestProxy $@
    exit 0
fi

proxyPort=8989
targetHost=$1
noHTTP=false

if [ $# -ge 2 ] && [ ! -z "$2" ];then
	#判断第二个参数是否为纯数字,如果是数字,则认定为自定义代理的本地端口
	expr $2 "+" 10 &> /dev/null  
	if [ $? -eq 0 ];then
		proxyPort=$2
		shift
	fi
fi

#proxyProcess=$(netstat -ano -P TCP|grep ':'$proxyPort)
proxyProcess=$(netstat -ano -P TCP|grep ':'$proxyPort|grep 'LISTENING')

if [ $(echo -n "$proxyProcess"|wc -c) -gt 0 ];then
	#echo "有进程..."
	echo "$proxyProcess"
	if [ ! -z "$2" ] && [[ "$2" == "true" ]];then
		## 不询问用户,直接杀死已有的进程
		do_KillProcess		
	else
		echo "已有代理进程存在,是否杀死进程?"
		read -p "是否终止已有进程?选择否本脚本将退出后续操作。y/n(默认为n)" killProcess
		if [ -z "$killProcess" ];then
			killProcess="no"
		fi
		if [[ "$killProcess" == "y" || "$killProcess" == "yes" ]];then
			do_KillProcess
		else
			exit 0
		fi
	fi
	:;
else
	#echo "无进程..."
	:;
fi

# 如果指定了不询问即终止进程的参数,此处$*参数前移一位
[ $# -ge 3 ] && shift 1

# 是否需要启动HTTP代理,默认均启动socks转http,端口号为socks端口前面加1,$2设为“nohttp”则不启动。
# 下面用tr统一转字符串为小写比较
if [ ! -z "$2" ] && [[ $(tr "[:upper:]" "[:lower:]" <<<"$2") == "nohttp" ]];
then
	noHTTP=true
fi

#从5656端口开始到5700,找到一个未占用的端口作为autossh管理端口
managePort=5656
manageOK=false
while [ !$manageOK -a $managePort -lt 5701 ];
do 
	#echo "循环中"
	#nc -w 2 -v 127.0.0.1 $managePort &> /dev/null
	netstat -ano -P TCP|grep 'LISTENING'|grep '127.0.0.1:'$managePort &> /dev/null
	if [ $? -ne 0 ];then
		manageOK=true
		break
	fi
	#echo "查询下一个端口"
	let managePort+=1
done
if [[ $manageOK == false ]];then
	echo "未找到可用的管理端口,程序罢工退出"
	exit 1
fi
echo "autossh使用管理端口:$managePort"

goProxyPort="1"$proxyPort

AUTOSSH_PATH=/v/bin/ssh-for-proxy autossh -M $managePort -C -N -f -D 0.0.0.0:$proxyPort $targetHost
echo "Proxy Address:127.0.0.1:$proxyPort"
echo "Full Proxy Address:socks5://127.0.0.1:$proxyPort"
echo "sshproxy Execute Done..."
sleep 1
nc -w 2 -v 127.0.0.1 $proxyPort
curl --connect-timeout 3 -sS -x socks5://127.0.0.1:$proxyPort 'http://v.ynit.top/ipfull/'
if [[ $noHTTP == false ]]
then
	echo -e "\nNow goProxy convert SOCKS proxy to HTTP:0.0.0.0:$goProxyPort\t Use:127.0.0.1:$goProxyPort"
	#以下为两种运行goproxy转换代理的方式,由bash接收进程信号会导致效率降低,建议直接cmd形式调用
	# way 1: bash run
	#/v/bin/goproxy-for-ssh http -l 0.0.0.0:$goProxyPort -b 127.0.0.1:$proxyPort &>/dev/null &
	# way 2:cmd run
	cmd /c start "GoProxy: HTTP Proxy for $targetHost" `cygpath -w /v/bin/goproxy-for-ssh` http -l 0.0.0.0:$goProxyPort -b 127.0.0.1:$proxyPort
else
	echo -e "\n"
fi

image.png