标签归档:反向代理

SSH反向代理内网穿透

SSH可以用来做正向Socks代理外,还可以用来做反向代理。典型的应用场景是:让内网的机器对外网可见,常见手段是在路由器上做端口映射,但是由于路由器的IP地址会变化(除非你有固定不变的IP),所以还需要在内网或路由器上运行一个动态DNS的工具(这种工具实际就是往外网发送IP地址),然后通过域名获取到最新的IP,而SSH做反向代理就不需要这些步骤,当然,需要外网有一台机器作为接入点。

简单来说,就是内网机器和外网的机器建立一条TCP隧道,外网预期分配的端口通过SSHD服务转发到内网来,内网的数据出去也一样。TCP隧道,头尾都对应端口号,比如把外网的2222端口,通过TCP隧道跟内网的22端口接起来。

ssh -CNR 2222:127.0.0.1:22 -b 0.0.0.0 www@外网IP

然后输入外网IP的www用户的密码就可以建立一条TCP隧道。可以看到,这个时候外网IP的2222端口开放了,它是由SSHD服务开启的。这里的-b参数说明希望外网IP的监听地址是0.0.0.0,就是0.0.0.0:2222,不过可能看到的是127.0.0.1:2222,这个不是我们所期望的。为了让反向代理真正可靠的使用,还需要解决如下几个问题:

0 自动登录问题
这个问题最常见的是放置公钥,内网ssh通过-i来指定私钥文件。如果希望使用密码来登录,那么需要借助sshpass这个包装器,之所以需要它,那是因为ssh不支持通过参数指定密码:

yum install sshpass

#格式
sshpass -p "密码" ssh -CNR 3080:127.0.0.1:80 www@xx.xx.xx.xx

1 在外网监听任意地址
首先,内网ssh命令需要使用-b指定监听地址为0.0.0.0,然后外网的sshd服务的配置,需要修改GatewayPorts为yes:

vi /etc/ssh/sshd_config
GatewayPorts yes

可能还需要重启一下sshd服务。

2 TCP隧道长时保持连接
在内网上运行ssh命令时,加上-o TCPKeepAlive=yes来让TCP保持长时连接。

3 防止TCP被中断
发送大量的数据或长时没有数据发送时,都可能导致TCP连接被卡住或被中间的路由器杀掉。所以需要建立一个心跳检查,在内网运行ssh时添加-o ServerAliveInterval=10 -o ServerAliveCountMax=6来维持心跳,这里的意思是每10秒检测一次,如果连续6次都无法获取响应,那么进程将退出。

4 防止进程异常退出
通过设置TCP长链接和定时发送心跳数据来保存通道畅通,但是从实际使用来看,这些还是远远不够的。进程异常退出监控起来很简单,但是实际遇到过这种情况:通道实际已经无法通信,但是本地进程并未退出,这个情况可能是远程进程异常退出,但是本地进程并没有收到通知,或者中间路由的问题,所以为了解决这个问题,我们可以定时发送数据,根据响应来判断是否要重启本地进程,于是有了以下脚本:

#!/bin/bash

cmm="/usr/bin/kill"
if [ ! -f "$cmm" ]; then
    cmm="/bin/kill"
fi

#不等于空说明通道通畅
ping=`curl -s http://x.x.x.x:2222`
if [ "$ping" = "" ]; then
	ps aux | grep 'x.x.x.x' | grep -v grep | awk '{print $2"|"$6}' | cat | while read line
	do
    		pid=`echo $line | cut -d "|" -f 1`
    		rss=`echo $line | cut -d "|" -f 2`

        	kill=`$cmm -9 $pid`
	        date=`date "+%Y-%m-%d %H:%M:%S"`

	        echo $date' -- 'PID:$pid' - Was Killed.'
	done
fi

#反代
live=`ps -efH | grep 'ssh -CN -R 2222:127.0.0.1:22' | grep -v 'grep' | wc -l`
if [ $live -eq 0 ]; then
    ssh -CN -R 2222:127.0.0.1:22 -b 0.0.0.0 -o TCPKeepAlive=yes -o ServerAliveInterval=10 -o ServerAliveCountMax=6 sshproxy@x.x.x.x -p 22 > /dev/null 2>&1 &
fi

live=`ps -efH | grep 'ssh -CN -R 8000:127.0.0.1:8000' | grep -v 'grep' | wc -l`
if [ $live -eq 0 ]; then
    ssh -CN -R 8000:127.0.0.1:8000 -b 0.0.0.0 -o TCPKeepAlive=yes -o ServerAliveInterval=10 -o ServerAliveCountMax=6 sshproxy@x.x.x.x -p 22 > /dev/null 2>&1 &
fi

使用curl来发送数据,如果通道畅通就会有响应,否则就无响应,无响应则强制杀掉本地进程。紧接着重启进程。

这个方法虽然很粗暴,但是很实用。从运行来看,还是相当稳定的(实际我仅仅是把内网的一个服务映射出去,偶尔用一用,但是用的时候要保证能用,比如内网的Gitlab)。

Zen-cart 使用反向代理后后台whos_online功能的修正

如果Zen-cart程序放置在反向代理服务器之后,后台whos_online功能显示的都是前端方向代理服务器的IP,那么这样就无法分辨有多少个人在线了。目前最常用的反向代理当数Nginx了,在配置中一般使用如下指令:

proxy_set_header X-Forwarded-For $remote_addr;

它把当前的真实访问的IP通过X-Forwarded-For传递给后端服务器,这样后端就可以记录下真实的IP。

Zen-cart中后端的whos_online功能获取IP是在includes/functions/whos_online.php文件中实现的,所以需要修改一下这个文件,主要判断如果存在X-Forwarded-For这个值,就使用它来作为真实IP:

//新增加的函数,用来判断是否是合法的IP
function is_ip($str) {
	$ip = explode(".", $str);
	if (count($ip) < 4 || count($ip) > 4) return 0;
	foreach($ip as $ip_addr) {
		if ( !is_numeric($ip_addr) ) return 0;
		if ( $ip_addr < 0 || $ip_addr > 255 ) return 0;
	}
    return (preg_match("/^([0-9]{1,3}\.){3}[0-9]{1,3}$/is", $str));
}
//修改function zen_update_whos_online()函数,把“$wo_ip_address = (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'Unknown');”使用如下代码替代:
//////////////////////////
$r = '';
$x = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
for ($i=0;$i<count($x);$i++) {
	$x[$i]=trim($x[$i]);
	if(is_ip($x[$i])) {
		$r=$x[$i];
		break;
	}
}
if($r != ''){
	$wo_ip_address = $r;
}else{
	$wo_ip_address = (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'Unknown');
}
//////////////////////////

这里只要修改其获取正确IP地址即可,接下来统计多少客户在线、重复IP和非重复客户是基于IP和会话ID的。至此,后台whos_online功能修改完毕。

原创文件,转载务必保留出处。
永久链接: http://blog.ifeeline.com/264.html