从零开始搭建简易远程服务器(三)-- SSH端口转发
SSH 端口转发(port forwarding)是一种将其他 TCP 端口的网络数据通过 SSH 链接来转发的技术, 也被称作“隧道”(tunneling)。SSH 端口转发能够提供两大有用的功能:
- 加密 SSH 客户端至服务端之间的通信连接(因为SSH连接自动提供了相应的加密和解密服务)
- 绕过防火墙的限制完成一些之前无法建立的 TCP 连接
之后将会看到,我们这个例子属于其中的第二项功能,因为我们希望与远程机器建立一个 TCP 链接无法实现的连接。有关 SSH 端口转发的介绍,下文是一个非常好的材料(并且是中文的):
英文材料可以看:
本文从实际例子出发介绍端口转发的应用。其中很多引用到实战SSH端口转发的内容,非常推荐大家去读读这篇文章。
为什么需要端口转发?
让我们先来回顾一下我们已经实现了什么:我们利用 python flask 搭建起了一个简单的服务器,启动之后能够监听本地主机(localhost)的某个端口然后提供服务,显示我们准备好的网页文件。
然而我们注意到,一直以来我们只是在本地机器上对某个端口进行访问。那么,这个端口是否能够被远程机器所访问呢?换句话说,一个远程的客户端是否能够通过连接这个TCP端口实现对服务器的访问呢?通常在以下两种情况下, 答案是否定的:
- 服务器上的配置限制了只有通过本地主机才能连接此服务器。
- 由于防火墙的限制,无法用SSH直接从外部连接到此服务器(比如说服务器处于内网之中)。
这时候我们就需要用到端口转发技术来实现对服务器的远程访问了。
实例环境
假设我们是在一个“公家”机器上搭建的服务器(比如说你工作所在单位配备的机器),该机器能够被外部网络连接(SSH)。但是公司一般会对该机器的外部访问权限进行限制,即只允许外部网络通过 SSH 端口连接。比如说我们可以尝试在远程客户端的浏览器窗口输入服务器的 IP 地址和相应的监听端口,会发现服务器并没有反应,说明我们并没有办法从外部网络通过 TCP 端口来连接。
本地端口转发
虽然“公家”机器对外部连接进行了限制,我们可以使用本地端口转发(local port forwarding)实现从远程机器连接到服务器。
假设服务器监听的端口是5000,IP地址是 ServerHost。那么我们可以从远程机器(客户端)输入以下命令实现本地端口转发:
ssh -L 7001:localhost:5000 ServerHost
建立起该 SSH 连接之后,我们只要在远程机器(客户端)上访问本机的7001端口(localhost:7001),便实现连接到服务器5000端口的效果。 That’s all!(很神奇有木有?)
用法解析
首先,该命令建立了一个从远程机器到 ServerHost 的SSH连接,而-L 选项指定了我们需要建立一个本地端口转发。
其中,7001是我们希望在远程机器上配置的端口(一般选用一个1024-65535之间并尚未被使用的端口即可)。5000是服务器在服务端监听的端口。localhost 指的就是 ServerHost 的本地主机,因为我们限制了只允许(服务端)本机上部署的应用才能连接到此服务器。因此,本地端口转发的完整命令是这样的:
ssh -L <local port>:<remote host>:<remote port> <SSH hostname>
在我们的例子中,local 指的就是远程的客户端机器,remote指的是服务端的服务器机器,填入对应的内容就可以得到我们前面输入的命令了。
我们来捋一下建立起本地端口转发之后,数据是怎么通过 SSH 端口流动的:
- 首先,我们在远程机器(客户端)上访问本机的7001端口
- 此时本机会将这个从7001端口接收到的访问数据加密并通过建立起的 SSH 通道转发到服务端机器 ServerHost上
- 由于数据是通过 SSH 通道发送的,所以数据实际发送和接受的端口是25端口(一般SSH都用这个)
- 服务端机器接收到该数据之后,再将数据转发到服务器监听的5000端口上
- 服务器对数据请求进行回应,然后按照原路返回要回应的数据以完成整个流程
简而言之,本地端口转发的作用是:将远程服务器监听的端口映射到本机的某个端口上,以致访问本机的该端口就可以实现访问远程服务器端口的效果(这里远程是对于发起请求的客户端机器而言)。而整个数据连接过程实际上是通过 SSH 通道及其端口实现的(是不是有种暗度陈仓的感觉)。
进阶版
利用本地端口转发,我们实现了从远程的客户端机器访问服务端的服务器。可是这个这个端口转发只能够被该客户端机器这一台机器使用。这是因为在主流 SSH 实现中,本地端口转发绑定的是 lookback 接口,这意味着只有 localhost 或者 127.0.0.1 才能使用本机的端口转发 , 其他机器发起的连接只会得到“connection refused.”。
那我们能不能实现让其他远程机器能够共享该客户端机器的本地端口转发,从而都能访问到这个服务器呢?答案是肯定的,SSH 提供了 GatewayPorts 的关键字来实现这个“共享”的功能,只需要加上 -g 选项就行了:
ssh -g -L 7001:localhost:5000 ServerHost
假设客户端机器的IP地址为 ClientHost,这样其他机器就可以通过访问 ClientHost:7001 来访问服务器了。
后记
利用 SSH 端口转发,我们可以方便地实现对远程服务器进行 TCP 连接的效果。这里我们还要注意一个问题:SSH 端口转发是通过 SSH 连接建立起来的,我们必须保持这个 SSH 连接以使端口转发保持生效。一旦关闭了此连接,相应的端口转发也会随之关闭。
另外,本文只介绍了端口转发当中的一种 – 本地端口转发。其实还有远程端口转发和动态端口转发等有趣的技术。由于没有在这个项目中实践过,就不提及了。感兴趣的朋友可以去看这篇介绍:实战SSH端口转发
最后,感谢白俄罗斯友人 Vasili 为我指出了这个使用端口转发技术实现远程访问服务器 TCP 连接的思路!