SSH 隧道技术保姆级讲解
不少人都有穿墙的需求, 穿墙对于一个普通的中国人来说, 有着不同寻常的意义. 概括简单来说有着启迪智慧, 培养思维之功效. 对于穿墙的方法, 有好多种吧. 今天想介绍一下利用 SSH 的隧道功能来进行穿墙的方法. 这个方法对于经常需要连接 ssh 服务器进行操作的朋友来说很方便, 对于其他的朋友来说, 就显得反而有些繁杂了. 下面介绍介绍这个方法的各个方面.
1 本文的受众
如果你遇到了以下问题,那么你应该阅读这篇文章
- 我听说过这种技术,我对它很感兴趣
- 我想在家里访问我在公司的机器(写程序,查数据,下电影)。
- 公司为了防止我们用 XX 软件封锁了它的端口或者服务器地址。
- 公司不让我们上 XX 网站,限制了网址甚至 IP。
- 公司不让我们看关于 XX 的信息,甚至花血本买了 XX 设备,能够对内容进行过滤。一看 XX 内容,链接就中断了。
- 我爸是搞电脑的,他在家里的路由器上动了手脚,我不能看 XXX 了。
带着这些问题,我们先从什么是 ssh 隧道开始。
2 什么是 SSH 隧道
首先看下面这张图,我们所面临的大部分情况都和它类似。我们的电脑在右上角,通过公司带有防火墙功能的路由器接入互联网(当然可能还有交换机什么的在中间连接着你和路由器,但是在我们的问题中交换机并不起到什么关键性的作用)。右下脚的部分是一个网站的服务器,它是我们公司防火墙策略的一部分,也就是说公司不希望我们访问这个服务器。在右上角还有一台机器,它也是属于我们的。但是这台机器并不在我们公司里面,换句话说他不受到公司防火墙的限制。最后也是最重要的一点是,我们能够在公司通过互联网直接访问这台机器。或者说这台位于公司防火墙外面的机器需要拥有一个独立的互联网 IP,同时公司的防火墙规则不会屏蔽这台机器,并且这台机器运行着一个 OpenSSH 服务器。
现在,我们清楚地知道了自己所处的网络环境。并且不难理解我们在公司无法访问那个服务器的原因是:线路 A-B-C 上 A- B 之间的防火墙屏蔽了对那个服务器的访问。与此同时,我们也很快注意到,线路 A-B-D 之间、D-C 之间是不受阻碍的。相信你已经想到了,在 A- B 之间的防火墙不会屏蔽对机器 d 的访问。因此我们可以通过机器 d 建立一个通道 A-B-D-C,从而访问到机器 c 上的数据。
这条通道可以用很多技术来建立,这里我们仅仅介绍如何使用 SSH 服务器来建立这样一个通道-他被称为 SSH 隧道。
3 如何建立本地 SSH 隧道
在我们计划建立一个本地 SSH 隧道之前,我们必须清楚下面这些数据:
- 中间服务器 d 的 IP 地址
- 要访问服务器 c 的 IP 地址
- 要访问服务器 c 的端口
现在,我们把上面这张图变得具体一些,给这些机器加上 IP 地址。并且根据下面这张图列出我们的计划:
- 需要访问
234.234.234.234
的 FTP 服务,也就是端口21
- 中间服务器是
123.123.123.123
现在我们使用下面这条命令来达成我们的目的 (转注: 此时是在公司内部机器 192.168.0.100
上操作)
ssh -N -f -L 2121:234.234.234.234:21 123.123.123.123
ftp localhost:2121 # 现在访问本地 2121 端口,就能连接 234.234.234.234 的 21 端口了
这里我们用到了 SSH 客户端的三个参数,下面我们一一做出解释:
- -N 告诉 SSH 客户端,这个连接不需要执行任何命令。仅仅做端口转发
- -f 告诉 SSH 客户端在后台运行
- -L 做本地映射端口,被冒号分割的三个部分含义分别是
- 需要使用的本地端口号
- 需要访问的目标机器 IP 地址(IP:
234.234.234.234
) - 需要访问的目标机器端口(端口:
21
) - 最后一个参数是我们用来建立隧道的中间机器的 IP 地址(IP:
123.123.123.123
)
我们再重复一下 -L
参数的行为。 -L X:Y:Z
的含义是,将 IP 为 Y 的机器的 Z 端口通过中间服务器映射到本地机器的 X 端口。在这条命令成功执行之后,我们已经具有绕过公司防火墙的能力,并且成功访问到了我们喜欢的一个 FTP 服务器了。
4 如何建立远程 SSH 隧道
通过建立本地 SSH 隧道,我们成功地绕过防火墙开始下载 FTP 上的资源了。那么当我们在家里的时候想要察看下载进度怎么办呢?大多数公司的网络是通过路由器接入互联网的,公司内部的机器不会直接与互联网连接,也就是不能通过互联网直接访问。通过线路
D-B-A
访问公司里的机器 a 便是不可能的。也许你已经注意到了,虽然 D-B-A
这个方向的连接不通,但是 A-B-D
这个方向的连接是没有问题的。那么,我们能否利用一条已经连接好的 A-B-D
方向的连接来完成 D-B-A
方向的访问呢?答案是肯定的,这就是远程 SSH 隧道的用途。
与本地 SSH 一样,我们在建立远程 SSH 隧道之前要清楚下面几个参数:
- 需要访问内部机器的远程机器的 IP 地址(这里是
123.123.123.123
) - 需要让远程机器能访问的内部机器的 IP 地址(这里因为是想把本机映射出去,因此 IP 是
127.0.0.1
) - 需要让远程机器能访问的内部机器的端口号(端口:
22
)
在清楚了上面的参数后,我们使用下面的命令来建立一个远程 SSH 隧道
ssh -N -f -R 2222:127.0.0.1:22 123.123.123.123
现在,在 IP 是 123.123.123.123
的机器上我们用下面的命令就可以登陆公司的 IP 是 192.168.0.100
的机器了。
ssh -p 2222 localhost
-N,-f
这两个参数我们已经在本地 SSH 隧道中介绍过了。我们现在重点说说参数 -R
。该参数的三个部分的含义分别是:
- 远程机器使用的端口(
2222
) - 需要映射的内部机器的 IP 地址(
127.0.0.1
) - 需要映射的内部机器的端口(
22
)
例如: -R X:Y:Z
就是把我们内部的 Y 机器的 Z 端口映射到远程机器的 X 端口上。
5 建立 SSH 隧道的几个技巧
5.1 自动重连
隧道可能因为某些原因断开,例如:机器重启,长时间没有数据通信而被路由器切断等等。因此我们可以用程序控制隧道的重新连接,例如一个简单的循环或者使用
djb’s daemontools.
不管用哪种方法,重连时都应避免因输入密码而卡死程序。关于如何安全的避免输入密码的方法.
。这里请注意,如果通过其他程序控制隧道连接,应当避免将 SSH 客户端放到后台执行,也就是去掉 -f
参数。
5.2 保持长时间连接
有些路由器会把长时间没有通信的连接断开。SSH 客户端的 TCPKeepAlive 选项可以避免这个问题的发生,默认情况下它是被开启的。如果它被关闭了,可以在 ssh
的命令上加上 -o TCPKeepAlive=yes
来开启。
另一种方法是,去掉 -N
参数,加入一个定期能产生输出的命令。例如: top 或者 vmstat。下面给出一个这种方法的例子:
ssh -R 2222:localhost:22 123.123.123.123 "vmstat 30"
5.3 检查隧道状态
有些时候隧道会因为一些原因通信不畅而卡死,例如:由于传输数据量太大,被路由器带入 stalled 状态。这种时候,往往 SSH 客户端并不退出,而是卡死在那里。一种应对方法是,使用 SSH 客户端的 ServerAliveInterval
和 ServerAliveCountMax
选项。=ServerAliveInterval= 会在隧道无通信后的一段设置好的时间后发送一个请求给服务器要求服务器响应。如果服务器在 ServerAliveCountMax
次请求后都没能响应,那么 SSH 客户端就自动断开连接并退出,将控制权交给你的监控程序。这两个选项的设置方法分别是在 ssh 时加入 -o ServerAliveInterval=n
和 -o ServerAliveCountMax=m
。其中 n
, m
可以自行定义。
5.4 如何将端口绑定到外部地址上
使用上面的方法,映射的端口只能绑定在 127.0.0.1
这个接口上。也就是说,只能被本机自己访问到。如何才能让其他机器访问这个端口呢?我们可以把这个映射的端口绑定在 0.0.0.0
的接口上,方法是加上参数 -b 0.0.0.0
。同时还需要打开 SSH 服务器端的一个选项- GatewayPorts
。默认情况下它应当是被打开的。如果被关闭的话,可以在 /etc/sshd_config
中修改 GatewayPorts no
为 GatewayPorts yes
来打开它。
5.5 如何寻找中间服务器
如果你家里使用 ADSL 上网,多半你会比较幸运。一般的 ADSL(例如 联通的 ADSL)都是有互联网地址的。你只需要在家里的路由器上一台装有 OpenSSH server 机器的 SSH 端口映射出去即可。同时一些提供 SSH 访问的虚拟主机也可以用于这一用途。例如: Hostmonser 或者 Dreamhost .
6 通过 SSH 隧道建立 SOCKS 服务器
如果我们需要借助一台中间服务器访问很多资源,一个个映射显然不是高明的办法(事实上,高明确实没有用这个方法)。幸好,SSH 客户端为我们提供了通过 SSH 隧道建立 SOCKS 服务器的功能。
通过下面的命令我们可以建立一个通过 123.123.123.123
的 SOCKS 服务器。
ssh -N -f -D 1080 123.123.123 # 将端口绑定在 127.0.0.1 上
ssh -N -f -D 0.0.0.0:1080 123.123.123.123 # 将端口绑定在 0.0.0.0 上
通过 SSH 建立的 SOCKS 服务器使用的是 SOCKS5 协议,在为应用程序设置 SOCKS 代理的时候要特别注意。
7 总结
至此,我们已经对如何利用 SSH 隧道有一个基本的认识了。现在,文章开始时的那些问题应该迎刃而解了吧。这里要特别说一下,由于 SSH 隧道也使用了 SSH 加密协议,因此是不会被防火墙上的内容过滤器监控到的。也就是说一切在隧道中传输的数据都是被加密的。当然,离开隧道后的数据还是会保持自己原有的样子,没有加密的数据还是会被后续的路由设备监控到。