平时我们一般用ssh连接服务器,但它还可以用来端口转发呀!
一、端口转发概述
我们知道,SSH 会自动加密和解密所有 SSH 客户端与服务端之间的网络数据。但是,SSH 还同时提供了一个非常有用的功能,这就是端口转发。它能够将其他 TCP 端口的网络数据通过 SSH 链接来转发,并且自动提供了相应的加密及解密服务。这一过程有时也被叫做“隧道”(tunneling),这是因为 SSH 为其他 TCP 链接提供了一个安全的通道来进行传输而得名。总的来说 SSH 端口转发能够提供两大功能:
- 加密 SSH Client 端至 SSH Server 端之间的通讯数据。
- 突破防火墙的限制完成一些之前无法建立的 TCP 连接。
二、本地端口转发和远程端口转发
我们来区分一下这两种端口转发的概念,我们知道,SSH端口转发自然需要SSH连接,但是SSH连接是有方向的,从SSH Client到SSH Server。而我们的应用也是有方向的,如果应用连接的方向和SSH的方向的一致的,那我们就说它是本地转发,如果两个方向不一致,我们就说它是远程转发。
本地转发实例
我们想象一下,假如我在实验室的服务器上开了一个本地web服务,比如本地测试一个网站或者本地开启jupyter notebook,但是我现在人在寝室啊,怎么能在自己的机器上访问到服务器上的这个web服务呢?为了能解决这个问题,我们可以通过搭建隧道进行解决,也就是接下来要用到的本地端口转发。
它的命令格式为:
1 | ssh -L <local port>:<remote host>:<remote port> <SSH hostname> |
那接下来我们就搭建一个隧道测试一下吧!
首先,我在服务器上开启jupyter notebook服务,
因为在这台服务器上我并没有配置jupyter,因此现在只运行在了本地的8888端口,我们下面搭建隧道在自己的机器上访问它吧!
打开terminal,输入:
1 | ssh -L 12345:localhost:8888 -p 22322 zhangfan@10.8.128.205 |
最后的是我的服务器的ip哦,加-p参数是因为实验室的一个内网ip的不同端口分配了不同的服务器,害,都内网ip了还这么稀缺,大哭…
-L参数代表本地转发,至于为什么<remote host>
字段用的是localhost,这里解释一下,remote的意思是远程的,也就是这个字段填写的是我们要连接的地址,那这个地址又是相对于谁来说的呢?当然是我们的服务器啦,因为jupyter服务是运行在我们服务器的本地的8888端口的啦,所以这里用的就是localhost了。
打开浏览器,输入localhost:12345我们可以看到打开了jupyter的页面。
远程转发实例
既然是远程转发,我们只要把上述本地转发的应用或者SSH连接的任一方向反向就变成了远程端口转发。
比如,本来是在本地的机器上建立向服务器的SSH连接,现在我们反过来:
1 | ssh -R <SSH host port>:<remote host>:<remote port> <SSH hostname> |
三、动态端口转发
无论我们刚才讨论的本地和远程转发,都是特定的一个端口到另一个端口的转发,那如果我们没有这个端口号怎么办?咦,什么样子的应用会没有这个端口号呢?比如说用浏览器进行web浏览,用QQ或者wechat聊天的时候呀!当我们在一个不安全的WiFi环境下上网的时候,用SSH动态转发来保护我们的网页浏览和社交软件的信息无疑是十分必要的。
动态转发命令格式:
1 | ssh -D <local port> <SSH Server> |
比如当我使用
1 | ssh -D 1234 -p 22322 zhangfan@10.8.128.205 |
就是我把本地的1234端口作为本地的端口号,所有流经1234端口的流量全部转发给了服务器。
嘿嘿嘿,是不是觉得和代理有点像,没错,就是很像!其实在这里SSH是创建了一个SOCKS代理服务。
那……这岂不是一个新的校园网免流的方法嘛?因为校园网局域网内的流量是不计费的,那我们通过实验室的服务器对外访问所消耗的流量就不是劳资的账号的流量了呀,哈哈哈哈哈!
前提是只能使用支持socks代理的应用,比如浏览器,QQ微信啥的…别忘了在应用设置一下开启socks代理哦。
不要告发我哦!我什么都不知道!我可不薅学校的羊毛!
四、实战——如何用SSH解决在家访问学校内网的服务器的jupyter服务
前提:现在有一台能正常使用的笔记本,一台有公网ip的阿里云服务器,一台学校实验室的内网服务器。
为了方便描述,分别简称为host1, host2, host3
首先我们捋一下思路:host2具备公网ip,因此host1和host3都可以连接,但反向不行。因此我们先对host3和host2之间建立SSH隧道,再对host1和host2建立对应端口的隧道,这样host1与host3之间就相当于打通了一条通路。
假设host3的jupyter运行在本地的8888端口上。因为只能是host3单向能连接到host2,因此我们只能在host3上对host2建立SSH隧道。那这样一来,host3就是SSH客户端,而host2就是SSH服务端,但host3本身是作为jupyter的服务提供者,因此应用和SSH连接的方向是相反的,所以我们要用的是远程端口转发命令。
首先连接到实验室的服务器:
1 | ssh host3 |
为了防止关掉terminal导致SSH链接断掉,因此使用screen命令:
1 | screen -S SSH_jupyter |
必须加上-g参数,以保证host1能使用host3建立的远程端口转发,加上-g参数就代表可以共享这个连接的意思。
按下ctrl+A+D可以从screen命令返回。
之后在自己的笔记本上,也就是host1上(可以用手机开热点模拟在家的情况)
1 | ssh -L 1234:localhost:12345 host2 |
好了最后笔记本上的terminal不要关掉哦,不然这个SSH隧道就断了哦。
现在我们在笔记本上的浏览器中输入:localhost:1234
是不是打开了在实验室的服务器上开启的jupyter服务呢。