Connecting to Intranet Servers by SSH Tunnel && frp

Introduction

学校内网有服务器,配置挺好的,传代下来给我了。

配置其实很垃圾:CPU(s) 24 x Intel(R) Xeon(R) CPU X5670 @ 2.93GHz (2 Sockets) + 32GiB RAM. 属于10年前用完的电子垃圾,某宝不到100块钱就能买到一个6核的X5670。。。

但是!!!外网连不进去学校内网,无法通过ssh连接到服务器。。

好在可以通过某个网页管理平台里的webshell连进去,但是webshell用的不是很爽,还是很想要通过ssh连进去。

Screen Shot 2020-08-05 at 1.21.37 AM

注意,学校内网的机器是可以访问到外面公网上的机器的。

另一方面,也是由于今天上班的时候,也遇到了类似的问题:内网的A机器可以ssh连到同样在内网的B机器,但是B机器就是ping不通A机器,估计是管理员把路由规则改了,可恶啊。。其中,A机器作为服务端,提供rpc服务,B机器需要去访问A机器的rpc接口才能被服务。

Screen Shot 2020-08-05 at 1.40.20 AM

此时,隔壁安研组的大哥exp说,能用ssh隧道(草,没听说过,好高大上啊)解决这个问题,随之,就在他的神船上操作了起来,不到几分钟就ok了,完美地解决了这个问题。我们在B机器上访问映射过来的某端口,就直接相当于访问A机器提供的rpc端口 。

所以,想学习一下这个ssh隧道到底是个多么高大上的东西,看看能不能通过同样的方式,也能让外网能够通过ssh连到学校内网的那台服务器。

Local Forward and Remote Forward via SSH Tunnel

学习资料:https://www.zsythink.net/archives/2450

学习资料里的mysql是5.x.x的,所以mysql流量是没有被加密的;但新版本的mysql的流量(8.x.x)是有TLS加密的

简单来说,就是通过ssh完成了一个转发(forward)的操作。

-L表示本地转发(local forward)。

ssh的文档中也能看出来这个-L参数的功能:

Screen Shot 2020-08-10 at 10.34.01 PM

画一张图表示一下我的理解:

Screen Shot 2020-08-10 at 11.28.45 PM

ssh -L 10fang wen dao000:B:9999 user@B就是在A机器的10000端口开启了一个监听,任何访问到该端口的流量,都会经过ssh提供的安全隧道,被转发至B机器的9999端口上。

因此,访问A的10000端口实际上就是访问B的9999端口。

这是建立在A能够ssh连接到B的前提下。(此时B可能无法ssh连接到A)


-R表示远程转发(remote forward),文档:

Screen Shot 2020-08-10 at 11.17.37 PM

再画一张图:

Screen Shot 2020-08-10 at 11.30.51 PM

可以发现其实改动很少,只不过就是ssh的发起者和接受者不同了而已。

还有一个不同点是,远程转发在A上监听的这个端口,只有A自己(127.0.0.1)能够连接,其他的ip是无法连接的;而本地转发在本地监听的那个端口,是可以设置为让任何ip连接的。

远程转发,就很贴近于我们的实际场景了。

ssh -R 20000:localhost:22 root@A

外网机器A无法访问到学校内网里的机器B,但是B可以访问A。B在通过ssh连接到A后,可以把B自己本地的一个端口(例如用于ssh服务的22端口)映射到A机器上的某个端口(例如20000);此时,A只需要访问自己本地的20000端口,就直接相当于在访问B的22端口。

可以看出来,问题的根源在于A和B之间的通信并不是双向的;但是B通过ssh连接到A后,建立起来的ssh隧道的通信却是双向的,所以,可以借助于ssh隧道来实现双方的相互通信。

Forwarding Port 22

学习资料:https://www.cnblogs.com/kwongtai/p/6903420.html

ok,我们现在要解决问题的是,如何让外网可以ssh连上内网的机器B。

按照上述方法,我们只需要通过B开启一个远程转发即可。但是问题是,我们每次ssh连接,都需要先上B机器去执行一下ssh -fCNR 20000:localhost:22 root@A???这样还不如直接用webshell。。。

此时,就需要一个==中转站C==,我们可以通过远程转发的方式,把B的22端口固定地(仅需一次即可)映射到C的20000端口上,然后我们再去访问C的20000端口,就能访问到B的22端口。

但问题又来了,远程转发到C的那个20000端口,又只能C自己去访问,不能让外网的其他的ip去访问。。

解决方案其实很简单:再在C上通过本地转发,将C本地的20000端口映射到另外一个30000端口上。C的30000端口外网ip可以任意访问,访问C的30000端口 == 访问C的20000端口 ==访问B的22端口。

因此,外网机器A,只需要ssh -p 30000 root@C即可ssh连接到学校内网中的B。

再画一个图:

Screen Shot 2020-08-10 at 11.57.24 PM

中转站C是需要公网ip的,否则A连不到C。这其实很好办,用一个学生机就可以了(反正学生机租着也没其他用处)。

其实从图中可以看到,实际上一通操作(一次远程转发+一次本地转发)的最终效果是:将B的22端口映射到了C的30000端口上,使得外网访问C的30000端口即可ssh连接到B。

FRP

ssh隧道转发未免看起来有些过于麻烦了。

事实上,有一些比较优秀的工具(软件)就是专门处理这种问题的(内网穿透)。

其中一个比较给力的就是基于golang的frp

按照官方文档里的配置操作一下,就OK了。

大致原理就是,需要一台类似于中转站C的frp server,用于转发frp client的各种流量。frp client只需要将自己的某个端口映射到frp server上,即可实现内网穿透、反向代理等等的功能。


贴一下我的配置。

学生机(frp server)frps.ini

1
2
3
4
5
6
7
[common]
bind_addr = 0.0.0.0
bind_port = 20000
authenticate_method = token
token = xxx # your password

vhost_http_port = 10005

nohup frps -c frps.ini &开启frp服务。

某个内网机器,将ssh的22端口映射出来,frpc.ini

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[common]
server_addr = xxx.xxx.xxx.xxx # frp server's ip
server_port = 20000
authentication_method = token
token = xxx # your password

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 20006

nohup frpc -c frpc.in &将端口映射到frp server上。

这样,我们只需要ssh -p 20006 root@xxx.xxx.xxx.xxx即可访问到内网的那台机器。

Summary

出于某些现实需求,去学习了一下内网穿透相关的东西,并成功利用所学的ssh隧道和frp,解决了外网无法ssh连接到学校内网机器的问题。

Load Comments?