接触linux的同学不可避免地要常常接触ssh,一般地,我们都可以在本地主机(下称host1)上通过ssh命令直接连接另一台远程主机(下称host2)来执行想要的命令。

但在某些情况下,因为存在防火墙,或者两台主机不在同一个网络,再者其他别的什么原因,可能我们无法直接从host1登录host2。

要解决这个问题,办法也不少,比方说在防火墙上打个洞,至少把host2的主机的ssh端口暴露出来,当然,这一般不是我们所期望的解决方案;更为常见的办法,是再找一台既能够被host1访问,又能够被host2访问的主机(下称host3)用作跳板,从而间接实现host1登录host2。<!–,示意图如下

–>

要实现这个效果,办法又有不止一种,之前也介绍过用iptables实现端口映射,本文则主要讲讲如何用ssh的“端口转发”(Port Forwarding)功能实现类似的效果。

ssh的端口转发分为3种,本地转发、远程转发和动态转发。

本地转发

用法

1
ssh -L [<local host>:]<local port>:<remote host>:<remote port> <ssh host>

例如,在host1上执行

1
ssh -L 7001:<host2>:8001 <host3>

即可建立host1到host3的ssh连接,并在host1上监听7001端口,将到达此端口的数据,通过host3,转发到host2的8001端口上。

注意一点,host1默认只在本地回环的7001端口上监听,所以从host1以外的主机是无法使用这个本地转发的,如果需要别的机器使用,有两种办法:一是带上常被忽略的[:]参数,并且写成0.0.0.0来监听host1所有网卡;二是带上-g参数,顺带着补充几个ssh常见的参数:-f后台运行,-C压缩数据,-N不执行命令。

远程转发

用法

1
ssh -R [<local host>:]<local port>:<remote host>:<remote port> <ssh host>

首先要想想为什么会有远程转发的存在。很多时候,我们的host3面临这样一种境地,它可以访问外网,但外网不能访问它,最常见的就是host3位于nat路由之后的情形。这种情况下本地转发就失效了,此时需要host3反过来主动建立到host1的连接,应当在host3上执行

1
ssh -R 7002:<host2>:8002 <host1>

类比本地转发,这次建立的是host3到host1的ssh连接,并在host3上监听7002端口,将到达此端口的数据,转发到host2的8002端口上。

有点晕?其实本地转发和远程转发两者最显著的差异在于一开始host1和host3建立ssh连接的方向,谁是ssh client,谁是ssh server,其他数据流的方向是一样的。如果这个ssh连接的方向,和端口转发的方向一致,那么就是“本地转发”,否则是“远程转发”。假如host1和host3之间本身就可以互相访问,那这两种转发用哪一种都可以。

动态转发

用法

1
ssh -D [<local host>:]<local port> <ssh host>

最后这个动态转发和前面两者不太一样,无论本地转发还是远程转发,都要指定怎么转发,但有时候这个不是我们一开始就能确定的。其实,在动态转发中,ssh扮演了SOCKS(5)代理的角色,具体实现是SOCKS协议的事了,超出了本文的范畴。如果你在host1上执行了

1
ssh -D 9001 <host3>

那么host1上的其他应用就可以设置127.0.0.1:9001作为自己的SOCKS(5)代理服务器了。

实现连接维持

不知道各位看官发没发现一个问题,上面的所有操作,都是基于事先建立好的一个ssh连接之上的,但连接毕竟是连接啊,谁知道啥时候就断了呢?万一连接断了,岂不是又要我们人工登录上去重新配一次端口转发,这多麻烦!这时就可以请出autossh这货了。它是干啥滴?

autossh is a program to start a copy of ssh and monitor it, restarting it as necessary should it die or stop passing traffic.

man一下,说的很清楚,它用来启动一个ssh连接并监视它,一旦它挂了就重启它。具体的原理呢,就是在建立ssh连接的2台主机之间再建立1个用于监视的连接,而这个连接上定期有测试数据传送。

啊,别问我如果这个监视的连接也挂了该怎么办。。。而且理论上,如果建立ssh连接的时候附带着执行一条会定期产生数据传送的命令,应该也可以实现类似的效果吧?

至于使用,autossh主要就多了一个-M参数,指定2个端口号,分别用于监视数据的发送和接收,如果只指定了其一,另一个自动取给定的这个端口号+1;至于-f参数,我又在这里踩坑了,也是看了帮助文档以后才知道,虽然和ssh里的-f一样都是使命令在后台运行,但给autossh加上-f可能会导致ssh无法输入密码什么的,所以这个参数放在ssh里头好了。

举个栗子,autossh的用法

1
autossh -M 4444 -gNfL 2222:192.168.1.102:3333 user@192.168.1.103

就可以实现“在后台建立到192.168.1.103的ssh连接,不执行命令但用于将到达本机2222端口的数据转发到192.168.1.102的3333端口,而且它是可被共享的;另外通过4444和4445端口监视这个ssh连接的存活状况,如果发生异常会自动重连”。对了,最好将autossh添加到开机启动里头哦,否则机器一重启,我们的配置又没有了。

参考资料

实战 SSH 端口转发
SSH的三种端口转发(Port forwarding)
SSH反向连接及Autossh