RSTunnel

GitHub: JayGoldberg/RSTunnel 是一个可靠的SSH Tunnel的持续维护,但是它不需要 autossh 就可以工作。这个 RSTunnel (可靠 SSH Tunnel)是一组 纯shell脚本 (兼容 /bin/sh )用于维护从客户端到服务器的安全隧道。

autossh 的麻烦之处是它是 C 程序,需要为不同的架构平台编译,而获取交叉编译工具链并非易事,所以最好依赖大多数操作系统内置的二进制文件。 RSTunnel 目标是只使用 Shell ,而且兼容最简单的 ash ,以及能够兼容像dropbear SSH这种不常用的客户端。

  • 简单查看一下 rstunnel 脚本,就可以看到关键的一句检查SSH Tunnel是否正常工作的语句:

通过 nc 工具检查SSH Tunnel是否工作
...
  echo "test data" | timeout $NET_CONNECT_TIMEOUT nc localhost $CHECKPORT 2>&1 >/dev/null

  if [ "$?" -ne 0 ]; then
    ErrMsg ">> [ F A I L E D ]"
    ErrMsg ">> Do you have a check port defined in your SSH config?"
    InfoMsg "===> Attempting to restart tunnel"

    TunKill; ErrorCheck "killing tunnel"

    TunRun; ErrorCheck "starting tunnel"

    MailUser
  else
    InfoMsg "===> Tunnel is up and running ()"
  fi
...

脚本写得很精简,阅读可以理解原理以及辅助逻辑

CTunnel

备注

我比较习惯使用SSH config来控制SSH,所以我fork了RSTunnel改写了一个 CTunnel(持久化Tunnel) (还比较粗糙,待改进)

  • 随脚本提供了一个 install 工具,用于通过交互方式完成安装,实际上就是生成一个 rstunnel.conf

  • 运行依赖 nc 命令,是通过 netcat 工具包提供,所以需要确保安装:

安装 netcat 来获得 nc
apt-get install netcat

nc 返回124状态码

我在主机上配置了每分钟执行一次 ctunnel ,发现没有正常检测出SSH tunnel的异常: 从外部访问NGINX显示 502 Bad Gateway ,说明SSH tunnel异常了。

登陆服务器检查,发现ssh进程存在(但应该是不工作了):

显示ssh进程依然存在
admin     616610  0.0  0.0  14968  5428 ?        Ss   11:56   0:01 ssh: /home/admin/.ssh/47.116.4.160-22-admin [mux]

而是,确实是可以正常 ssh 进入服务器(速度很快),这说明SSH连接是正常的。

但是,根据我的配置,SSH端口转发却无法工作了:

  • 从远端NGINX服务器上反向访问代理端口 127.0.0.1:24180 端口,这里使用 telnet 模拟访问,显示立即被断开:

telnet测试反向代理端口
$ telnet 127.0.0.1 24180
Trying 127.0.0.1...
telnet: connect to address 127.0.0.1: Connection refused
telnet: Unable to connect to remote host

手工执行了一次 timeout 3 nc localhost 3128 ,然后检查 echo $? 发现返回码是 124

我搞错了,原来通过 timeout 3 来执行,按照 man timeout 解释,如果命令超时,并且 --preserve-status 没有设置,就会返回退出码 124 。由于 nc localhost 3128 实际上成功以后是不结束的,通过 timeout 3 来结束,那么正常情况下拿到的就是 124

实际上脚本是通过如下检测:

nc检测命令
NET_CONNECT_TIMEOUT=3
CHECKPORT=3128
echo "test data" | timeout $NET_CONNECT_TIMEOUT nc -v localhost $CHECKPORT 2>&1 >/dev/null

我检查发现这种只检查正向端口转发并不能反映反向端口转发的状态。例如,这里检查 ssh 登陆服务器是正常的,正向端口转发 3128 也是正常的,但是反向端口转发就是失败的。

所以脚本要修订为先远程登陆到服务器执行反向端口转发检查:

ssh到远程服务器上反向nc检测命令
TUNNEL=aliyun
NET_CONNECT_TIMEOUT=3
CHECKPORT=24180

ssh $TUNNEL "echo 'test data' | timeout $NET_CONNECT_TIMEOUT nc -v localhost $CHECKPORT 2>&1 >/dev/null"

此时,如果远程服务器反向端口转发异常,会返回:

ssh到远程服务器上反向nc检测命令输出
nc: connect to localhost port 24180 (tcp) failed: Connection refused
nc: connect to localhost port 24180 (tcp) failed: Connection refused

$ echo $?
1

修订了 ctunnel ,在上述失败情况下杀掉ssh进程重连。

不过,我发现一个问题,ssh登陆到远程服务器上检查,如果网络联通,会有两个返回值,一个失败一个成功:

ssh到远程服务器上反向nc检测命令输出有两个返回,什么意思?
nc: connect to localhost port 24180 (tcp) failed: Connection refused
Connection to localhost 24180 port [tcp/*] succeeded

为什么ssh反向 RemoteForward 端口明明是打开的,为何会有两条记录,一个失败一个成功?

我验证发现,似乎这种SSH端口转发情况下,都会出现一条失败( (tcp) failed: Connection refused ),然后接下来就是成功记录( port [tcp/*] succeeded! ),不过不影响最后的检查结果( $?=0 )

参考