起因

最近碰到一件事:B同学在他电脑的Ubuntu虚拟机中学习搭建服务器碰到了问题,要我帮他看下。我总不能一个QQ远程桌面连过去,那样操作会卡到崩溃。ssh过去是最好的方法,不过他的电脑跟我不在一个局域网,又是虚拟机,要怎么连过去呢?

怎么解决?

有两种方法:

  1. 通过一台公网服务器,通过ssh命令建立反向隧道
  2. 通过第三方的ngrok服务建立tcp反向隧道

它们的原理都是使用了反向隧道,原理见下图:

linux云服务器如何将自己的内网映射到自己的公网上_服务器

正向与反向的区别在于正向连接是使用者通过自己的客户端操作服务器资源;反向连接是使用者通过服务器操作客户端资源。结合上图理解:

  • 客户端1ssh连接公网服务器时,所有的操作都在服务器上,所以称为正向隧道;
  • 客户端2ssh连接公网服务器的2244端口时,公网服务器全部转发客户端1,使用者通过公网服务器操作客户端1,所以公网服务器到客户端1的连接称为反向隧道

第一种方法

客户端1执行一条命令即可建立反向隧道:

$ssh -N -f -R *:2244:localhost:22 106.10.10.xxx

其中-N表示不执行命令,只转发;-f表示后台运行;-R表示反向隧道;*:2244:localhost:22表示监听服务器的2244端口,所有包转发到本地的22端口;106.10.10.xxx为服务器IP

我只要在客户端2执行ssh -p 2244 xxx@106.10.10.xxx就能连接客户端1的电脑了。

实际使用中会发现,该通道会经常自动断开,这是正常现象。可通过autossh实现断开重连。使用autossh之前,必须确保该客户端与服务器连接使用了无密码的密钥对登陆

$autossh -M 5678 -N -f -R *:2244:localhost:22 106.10.10.xxx

其中-M 5678表示通过5678端口监听连接状态,有问题就重连。

第二种

使用ngrok(1.x版本)就简单多了,一条命令搞定,也不需要知道服务器的密码或传公钥,适合第三方服务器提供跳板服务。

$ngrok -proto=tcp 22
ngrok                                                                                                                                                                (Ctrl+C to quit)
                                                                                                                                                                                     
Tunnel Status                 online                                                                                                                                                 
Version                       1.7/1.7                                                                                                                                                
Forwarding                    tcp://106.10.10.xxx:2244 -> 127.0.0.1:22                                                                                                           
Web Interface                 127.0.0.1:4040                                                                                                                                         
# Conn                        0                                                                                                                                                      
Avg Conn Time                 0.00ms

根据上面的输出,我只要在客户端2执行ssh -p 2244 xxx@106.10.10.xxx就能连接客户端1的电脑了。

默认情况下,ngrok的转发端口是随机的,如果要固定,编辑~/.ngrok,按如下添加通道:

server_addr: 106.10.10.xxx:4443
trust_host_root_certs: false
tunnels:
  ssh:
    proto:
      tcp: "22"
    remote_port: 2244

然后通过以下命令启动:

ngrok start ssh