可靠的ssh反向端口转发方法

基本问题:如何远程管理在NAT后面的Linux计算机?ssh反向端口转发比较不错。

digraph rportforward {
"远程主机" -> "中转服务器" [ label="ssh反向端口转发" ]
"客户端"  -> "中转服务器" [ label="ssh" ]
"中转服务器" -> "远程主机" [ label="ssh" ]
}

反向端口转发示意图

其中,中转服务器位于互联网,有独立的IP地址。简单的端口转发命令是

ssh -R <rPort>:127.0.0.1:22 <mServerHost>

但是简单使用这个命令是不可靠的,涉及到如下问题:

  • 需要远程主机的交互终端一直开着
  • 断线检测不可靠,会出现假连接
  • 无法自动重连

针对第一个问题,解决方案是采用 screen 程序,让screen 接管交互终端,这样比较不容易被误杀。

针对第三个问题,解决方案是采用 autossh程序,这是个能够自动重联ssh的程序,最简单的使用方法是直接替换 ssh

autossh -R <rPort>:127.0.0.1:22 <mServerHost>

但是,仅仅这样还是不够的,因为autossh自己并没有很好的解决断线检测的问题,但是提供一种机制:

  • monitor端口

简单的说,就是通过ssh做两个隧道,把数据发到远端再转发回来,如果没收到,就表明链路断了。这种方法使用 -M 参数:

autossh -M <mport> -R <rPort>:127.0.0.1:22 <mServerHost>

实验发现,这种方法也不是很靠谱。不用monitor端口,可以利用 SSHv2协议的KeepAlive机制,需要客户端和服务端配合,我发现这是最可靠的:

客户端,需要增加两个选项 ServerAliveInterval=15, ExitOnForwardFailure=yes, 服务端,需要增加两个选项: ClientAliveInterval=15 和ClientAliveCountMax=3 。

这样客户端和服务器端都会采用心跳来检查连接的健康状态,没有心跳就会断开连接。而客户端的 ExitOnForwardFailure=yes则确保新的连接必须成功,不成功则退出,然后 autossh接管异常退出,重连,客户端的命令行参数如下

autossh -R <rPort>:127.0.0.1:22 -o ServerAliveInterval=15 -o ExitOnForwardFailure=yes <mServerHost>

[2016.10.28更新] 在Centos 7系统上 autossh 的 -M 选项是必须的,因此改为

autossh -M 0 -R <rPort>:127.0.0.1:22 -o ServerAliveInterval=15 -o ExitOnForwardFailure=yes <mServerHost>

服务器端需要修改 /etc/ssh/sshd_config 文件,然后reload sshd服务。

[2017.05.22更新]

更多参数:

  • -f 表示autossh自己进入后台执行,
  • -qTN表示不分配终端,仅用于转发。因此改为
autossh -M 0  -f -qTN -R <rPort>:127.0.0.1:22-o ServerAliveInterval=15 -o ExitOnForwardFailure=yes <mServerHost>

另外,此方法对正向端口转发同样有效:

autossh -M 0  -f -qTN -L <localPort>:<remoteHost>:<remotePort>-o ServerAliveInterval=15 -o ExitOnForwardFailure=yes <mServerHost>