在之前的文章 SSH隧道:访问翻墙服务器的临时性手段 中我提出了一种使用 SSH 隧道来作为穿越 GFW 的流量伪装方法。这种方法可以将 Shadowsocks 流量转化成 SSH 特征的流量,从而避免被 GFW 给 BAN 掉。在过去一年使用起来看,SSH 隧道相当稳定,性能较好,而且从来没有被 BAN 过。因此我在部署中将 SSH 作为一个永久性措施保留下来:即我在跳板服务器和海外服务器之间建立一个 SSH 隧道,然后我的客户端通过跳板服务器来连接海外服务器从而实现科学上网。

但是 SSH 隧道本身的稳定性可能有问题,一旦隧道挂掉需要手动连接,这个未免太麻烦。之前我的建议是使用 autossh 来监管 ssh 隧道进程。但是这个方法有两个问题:

  1. autossh 监管的稳定性不是特别好,还是会经常挂掉。
  2. autossh 管理不方便,关闭,重启隧道等操作都比较麻烦。

因此这里我们给出用 Supervisor 来监管 ssh 进程的方案。以下以 Ubuntu 系统为例。首先安装 Supervisor

1
2
$ sudo apt update
$ sudo apt install -y supervisor

我们要准备一个启动 SSH 隧道的脚本 start_tun.sh 以便让 supervisor 来管理

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

TUNNEL_GATEWAY="$1"
PORT="$2"
TARGET_PORT="$3"

su -c \
"ssh -N -C \
-L 0.0.0.0:$PORT:$TARGET_PORT remote_user@$TUNNEL_GATEWAY \
-o \"ServerAliveInterval 30\" -o \"TCPKeepAlive yes\"" \
local_user

其中

  • TUNNEL_GATEWAY: 是海外服务器的地址。这里我们做了参数化处理,用传递给脚本的第一个参数作为这个地址。
  • PORT: 是跳板服务器上的端口;
  • TARGET_PORT: 是海外服务器上的端口。设置好之后访问 localhost:PORT 等同于访问远程服务器上的 localhost:TARGET_PORT。
  • remote_user: 是海外服务器上的用户。你需要提前在这个账户下设置好你的跳板服务器的公钥。要确认这一点你可以在跳板服务器上以 local_user 身份运行 ssh-copy-id remote_user@TUNNEL_GATEWAY
  • local_user: 是跳板服务器上运行 SSH 隧道的本地用户。

然后我们在 /etc/supervisor/conf.d 下新建一个 tunnel.conf 文件,并填入下面的内容:

1
2
3
4
5
6
7
8
9
[program:tunnel]
command=/path/to/start_tun.sh server_host PORT TARGET
user=root
autostart=true
autorestart=true
redirect_stderr=true
stopasgroup=true
stopsignal=QUIT
stdout_logfile=/path/to/logfile.log

其实这里可以直接指定用 local_user 来作为执行用户,这样 start_tun.sh 脚本里面也不需要用 su 命令来转成 local_user 用户。不过我这里需要在脚本里面额外做别的设置,所以做了这个处理。

然后执行 sudo supervisorctl reread 读取新增的设置,然后运行 sudo supervisorctl update 即可应用新设置,运行 start_tun.sh脚本。

  • 由于 autostart=true 的作用,跳板服务器开机时会自动开启 start_tun.sh 脚本;
  • 由于 autorestart=true 的作用,ssh 隧道意外关闭时会自动重启;
  • 使用 sudo supervisorctl stop tunnel 可以停止 ssh 隧道;
  • 使用 sudo supervisorctl restart tunnel 可以重启 ssh 隧道。