端口转发系列文章:
1)Windows端口转发(Port Forwarding in Windows)
2) Linux端口转发(iptables)
文章转载自IBM的实战SSH端口转发一文,加上个人的理解,花费10分钟,你将有以下收获哦:1) ssh端口转发功能是什么? 2) 通过ssh本地端口转发和远程端口转发实例明白二者的异同点。3) 进阶版的端口动态转发和X协议转发的案例分析。4) 个人总结并设计一款端口转发开源工具。。
众所周知,SSH 会自动加密和解密所有SSH 客户端与服务端之间的网络数据。同时SSH还提供了一个端口转发的功能:将其他TCP 端口的网络数据通过SSH连接来转发,并且自动提供了相应的加密及解密服务。这一过程也被叫做“隧道”(tunneling),这是因为 SSH为其他TCP链接提供了一个安全的通道来进行传输而得名。例如,Telnet,SMTP,LDAP 这些 TCP 应用均能够从中得益,避免了用户名,密码以及隐私信息的明文传输。与此同时,如果您工作环境中的防火墙限制了一些网络端口的使用,但是允许SSH的连接,也能够使用SSH隧道将TCP端口转发。总的来说SSH端口转发能够提供两大功能:
1) 加密SSH Client端至SSH Server端之间的通讯数据。
2) 突破防火墙的限制完成一些之前无法建立的TCP连接。
本地转发案例分析
实验室有台LDAP服务器LdapServerHost,但是限制了只有本机上部署的应用才能直接连接此LDAP服务器。由于调试或测试的需要想从远程机器(LdapClientHost)临时连接到这个LDAP服务器 , 有什么方法能实现呢?
答案无疑是本地端口转发了,命令格式如:ssh -L <local port>:<remote host>:<remote port> <SSH hostname>
在LdapClientHost上执行如下命令即可建立一个SSH的本地端口转发:$ ssh -L 7001:localhost:389 LdapServerHost
本地端口转发本例中选择7001端口作为本地的监听端口,在选择端口号时要注意非管理员帐号是无权绑定1-1023端口的,所以一般是选用一个1024-65535之间的并且尚未使用的端口号。
然后将远程机器(LdapClientHost)上的应用直接配置到本机的7001端口上(而不是LDAP服务器的389端口上)。之后的数据流将会是下面这个样子:
1) 在 LdapClientHost 上的应用将数据发送到本机的7001端口上,
2) 本机的SSH Client会将7001端口收到的数据加密并转发到LdapServertHost的SSH Server上。
3) SSH Server会解密收到的数据并将之转发到监听的LDAP 389端口上,
4) 最后再将从 LDAP 返回的数据原路返回以完成整个流程。
这整个流程应用并没有直接连接 LDAP 服务器,而是连接到了本地的一个监听端口,但是SSH端口转发完成了剩下的所有事情,加密,转发,解密,通讯。
几个注意点:
1) 只能在建立 SSH 连接的同时创建端口转发,而不能给一个已经存在的 SSH 连接增加端口转发。
2) 你可能会疑惑命令中 <remote host> 为什么用 localhost,它指向的是哪台机器呢?在本例中,它指向 LdapServertHost 。为什么用 localhost 而不是 IP 地址或者主机名呢?其实这个取决于之前是如何限制 LDAP 只有本机才能访问。如果只允许 lookback 接口访问的话,那么自然就只有 localhost 或者 IP 为 127.0.0.1 才能访问了,而不能用真实 IP 或者主机名。
3) 在LdapClientHost已经建立了端口转发,那么这个端口转发可以被其他机器使用么?比如能否新增加一台LdapClientHost2来直接连接LdapClientHost的7001端口?答案是不行的,在主流 SSH 实现中,本地端口转发绑定的是lookback接口,这意味着只有localhost或者127.0.0.1才能使用本机的端口转发 , 其他机器发起的连接只会得到 “connection refused. ”。好在SSH同时提供了GatewayPorts关键字,通过指定它与其他机器共享这个本地端口转发:ssh -g -L <local port>:<remote host>:<remote port> <SSH hostname>
远程转发实例分析
由于网络或防火墙的原因我不能用SSH直接从LdapClientHost连接到LDAP服务器(LdapServertHost),但是反向连接是允许的。那此时我们的选择自然就是远程端口转发了。命令格式:ssh -R <local port>:<remote host>:<remote port> <SSH hostname>
在 LDAP 服务器(LdapServertHost)端执行如下命令:$ ssh -R 7001:localhost:389 LdapClientHost
远程端口转发和本地端口转发相比,SSH Server和SSH Client的位置对调了一下,但数据流依然是一样的。在LdapClientHost上的应用将数据发送到本机的7001端口上,而本机的SSH Server会将7001端口收到的数据加密并转发到LdapServertHost的SSH Client上。SSH Client会解密收到的数据并将之转发到监听的LDAP 389端口上,最后再将从LDAP返回的数据原路返回以完成整个流程。
看到这里,你是不是会有点糊涂了么?为什么叫本地转发,而有时又叫远程转发?这两者有什么区别?
本地转发与远程转发的对比与分析
首先,SSH端口转发自然需要SSH连接,而SSH连接是有方向的,从SSH Client到SSH Server。而应用也是有方向的,比如需要连接LDAP Server时,LDAP Server自然就是Server端,应用连接的方向也是从应用的Client端连接到应用的Server端。如果这两个连接的方向一致,那我们就说它是本地转发。而如果两个方向不一致,我们就说它是远程转发。
本地转发时:LdapClientHost同时是应用的客户端,也是SSH Client,这两个连接都从它指向LdapServertHost(既是LDAP服务端,也是SSH Server)。
远程转发时:LdapClientHost是应用的客户端,但却是SSH Server;而LdapServertHost是LDAP的服务端,但却是SSH Client 。这样两个连接的方向刚好相反。
另一个方便记忆的方法是,Server端的端口都是预定义的固定端口(SSH Server的端口 22,LDAP的端口389),而Client端的端口都是动态可供选择的端口(如例子中的7001 端口)。如果Server端的两个端口都在同一台机器,Client 端的两个端口都在另一台机器上,那么这就是本地连接;如果这四个端口交叉分布在两个机器上,每台机器各有一个Server端端口,一个Client端端口,那就是远程连接。
弄清楚了两者的区别之后,再来看看两者的相同之处。如果环境既允许LdapClientHost发起SSH连接到LdapServerHost,也允许LdapServerHost发起SSH连接到LdapClientHost。那么选择本地转发或远程转发都是可以的,能完成一样的功能。
接着来看个进阶版的端口转发。之前涉及到的各种连接/转发都只涉及到了两台机器,还记得在本地转发中提到的一个问题么?本地转发命令中的 <remote host> 和 <SSH hostname> 可以是不同的机器么?
ssh -L <local port>:<remote host>:<remote port> <SSH hostname>
答案是可以的!让我们来看一个涉及到四台机器 (A,B,C,D) 的例子。
在 SSH Client(C) 执行下列命令来建立 SSH 连接以及端口转发:$ ssh -g -L 7001:<B>:389 <D>
然后在应用客户端(A)上配置连接机器(C)的 7001端口即可。注意我们在命令中指定了“ -g ”参数以保证机器(A)能够使用机器(C)建立的本地端口转发。而另一个值得注意的地方是,在上述连接中,(A)<-> (C))以及 (B)<->(D)之间的连接并不是安全连接,它们之间没有经过 SSH 的加密及解密。如果它们之间的网络并不是值得信赖的网络连接,我们就需要谨慎使用这种连接方式了。
动态转发实例分析
前面讨论过的本地转发和远程转发都有个前提要求有一个固定的应用服务端的端口号(389端口)。如果没有这个端口号怎么办?什么样的应用会没有这个端口号呢?比如说用浏览器进行Web浏览,比如说MSN等等。
当在一个不安全的WiFi环境下上网,用SSH 动态转发来保护网页浏览及MSN信息无疑是十分必要的。先来看一下动态转发的命令格式:$ ssh -D <local port> <SSH Server>
举例 $ ssh -D 7001 <SSH Server>
动态端口转发在这里SSH创建了一个 SOCKS 代理服务。来看看帮助文档中对 -D 参数的描述:
-D port :This works by allocating a socket to listen to port on the local side, and whenever a connection is made to this port, the connection is forwarded over the secure channel, and the application protocol is then used to determine where to connect to from the remote machine. Currently the SOCKS4 and SOCKS5 protocols are supported, and ssh will act as a SOCKS server. Only root can forward privileged ports. Dynamic port forwardings can also be specified in the configuration file.
之后可以直接使用 localhost:7001 来作为正常的 SOCKS 代理来使用,直接在浏览器或 MSN 上设置即可。在SSH Client端无法访问的网站现在也都可以正常浏览。而这里需要值得注意的是,此时 SSH 所包护的范围只包括从浏览器端(SSH Client 端)到SSH Server 端的连接,并不包含从SSH Server端到目标网站的连接。如果后半截连接的安全不能得到充分的保证的话,这种方式仍不是合适的解决方案。
X 协议转发实例分析
日常工作当中,可能会经常会远程登录到 Linux/Unix/Solaris/HP 等机器上去做一些开发或者维护,也经常需要以GUI方式运行一些程序,比如要求图形化界面来安装DB2/WebSphere等等。这时候通常有两种选择来实现:VNC或者X窗口,让我们来看看后者。
使用X窗口通常需要分别安装:X Client和X Server。在本例中X Client是所访问的远程Linux/Unix/Solaris/HP,而我们的X Server则是发起访问的本地机器(例如正在使用的笔记本或台式机)。把X Client端的X窗口显示在X Server端需要先行在X Client端指定X Server的位置,命令格式如下:
export DISPLAY=<X Server IP>:<display #>.<virtual #>
例如:export DISPLAY=myDesktop:1.0
然后直接运行X应用即可,X窗口就会自动在我们的本地端打开。
一切运行正常,但是,这时候 IT 部门突然在远程 Linux/Unix/Solaris/HP 前面加了一道防火墙。非常不幸的是,X 协议并不在允许通过的列表之内。怎么办?只能使用 VNC 了么?不,其实只要使用了SSH端口转发即可通过,同时也对 X 通讯数据做了加密,真是一举两得。(当然,使用此方法前最好先咨询相关 IT 部门是否符合相应的安全条例,以免造成违规操作。)
建立命令也很简单,直接从本地机器(X Server 端)发起一个如下的SSH连接即可:$ ssh -X <SSH Server>
建立连接之后就可以直接运行远程的 X 应用。注意建立 X 转发之后会自动设置 DISPLAY 环境变量,通常会被设置成localhost:10.0,我们无需也不应该在连接之后再进行修改此环境变量。
一个比较常见的场景是,我们的本地机器是Windows操作系统,这时可以选择开源的XMing来作为我们的XServer,而SSH Client则可以任意选择了,例如PuTTY,Cygwin均可以配置访问SSH的同时建立X转发。
总结
至此,我们已经完成了本地端口转发,远程端口转发,动态端口转发以及 X 转发的介绍。回顾起来,总的思路是通过将 TCP 连接转发到 SSH 通道上以解决数据加密以及突破防火墙的种种限制。对一些已知端口号的应用,例如 Telnet/LDAP/SMTP,我们可以使用本地端口转发或者远程端口转发来达到目的。动态端口转发则可以实现 SOCKS 代理从而加密以及突破防火墙对 Web 浏览的限制。对于 X 应用,无疑是 X 转发最为适用了。虽然每一部分我们都只是简单的介绍了一下,但如果能灵活应用这些技巧,相信对我们的日常生活 / 工作也是会有所帮助的。
个人理解:架构抽象
ssh的端口转发功能,便于现有非加密架构(如使用Telnet/LDAP/SMTP协议的架构产品)的改造或者基于防火墙限制只能通过某个端口转发的改造或者应用服务端的应用端口不固定。请求端在基本上不用动原有实现和服务端只要可以ssh登录的基础上,增加一个是否开启ssh的端口转发的开关(原有实现要读这个开关)和一个local port参数,前者开启后自动配置端口本地转发或端口远程转发,后者调用则会创建ssh隧道进行端口转发。
继续向上抽象:是不是可以有ssh的端口转发,可以做一个端口转发的开源工具,通过配置文件告知以什么方式端口转发,本地和远程方式需要配置local port、remote host ip、remote port和SSH hostname,动态方式需要提供local port和SSH Server。
ssh -L <local port>:<remote host>:<remote port> <SSH hostname>
ssh -R <local port>:<remote host>:<remote port> <SSH hostname>
ssh -D <local port> <SSH Server>
如果是要建立多个ssh隧道进行端口转发,除了local port可以固定外,要上层应用传递动态的参数remote host ip、remote port和SSH hostname以便于建立隧道。
网友评论