【摘要】 背景网上有现成的工具autossh专门用来建立稳定的ssh连接,不过经过测试效果不好,故障率较高(可能是没有正确配置导致)。另一方面,在windows上安装autossh比较麻烦,需要自己编译。在Linux使用下面的方案可以不用额外安装其它软件。端口转发例子以内网穿透为例,假设内网中有一台树莓派(以下简称客户端),想借助一台有外网IP的服务器(以下简称服务器)创建端口转发,以...

背景

网上有现成的工具autossh专门用来建立稳定的ssh连接,不过经过测试效果不好,故障率较高(可能是没有正确配置导致)。另一方面,在windows上安装autossh比较麻烦,需要自己编译。在Linux使用下面的方案可以不用额外安装其它软件。

端口转发例子

先修改配置文件 /etc/ssh/sshd_config  将 GatewayPorts yes 。

以内网穿透为例,假设内网中有一台树莓派(以下简称客户端),想借助一台有外网IP的服务器(以下简称服务器)创建端口转发,以实现在外网访问到内网树莓派的ssh服务。
在树莓派上执行:

ssh -NT -R 1122:127.0.0.1:22 用户名@服务器IP

执行之后即可以在服务器上登录到树莓派的ssh:

ssh pi@127.0.0.1 -p 1122

由于TCP连接是不稳定的,所以基于TCP的ssh端口转发连接也是不稳定的。如果网络断开,可能提示:

Connection to xxx.xxx.xxx.xxx closed by remote host.

断线重连

断线重连很简单,ssh命令退出后,重新执行命令建立连接即可。伪代码如下:

while(true){
 `ssh -NT -R 1122:127.0.0.1:22 用户名@服务器IP`}

不过执行ssh命令需要输入密码,首先要配置ssh免密登录,在客户端执行:

$ ssh-keygen
$ ssh-copy-id 用户名@服务器IP

输入密码之后即完成免密登录配置,之后再执行ssh命令连接到服务器就不用再输入密码了。
下面使用systemctl来实现断线重连和开机自动运行。在客户端创建一个systemctl服务配置文件:

sudo vi /usr/lib/systemd/system/ssh-link.service

写入以下内容:

[Unit]
Description=ssh port forwarding service.
[Service]
Type=simple
ExecStart= /bin/sh -c 'ssh -NT -R 1122:127.0.0.1:22 用户名@服务器IP'
Restart=always
RestartSec=10
User=pi
Group=pi
[Install]
WantedBy=multi-user.target

其中:

  • UserGroup为执行ssh-keygen命令的用户和用户组。
  • Restart=always表示ssh命令退出后,等待RestartSec=10秒,然后重新执行。

保存后运行一下:

sudo systemctl start ssh-link

查看运行状态,正常情况如下:

$ sudo systemctl status ssh-link
● ssh-link.service
 Loaded: loaded (/usr/lib/systemd/system/ssh-link.service; bad; vendor preset: enabled) Active: active (running) since Sun 2020-11-08 23:00:33 CST; 3s ago ......

开机自启

配置开机启动:

$ sudo systemctl enable ssh-link Created symlink /etc/systemd/system/multi-user.target.wants/ssh-link.service → /usr/lib/systemd/system/ssh-link.service.

此时可以重启客户端,正常情况下,重启之后会自动建立ssh端口转发连接。

心跳检测

前面说到,ssh命令退出后,systemctl会重新执行ssh命令以建立连接。但有些特殊情况下,连接实际上断开了,但ssh命令没有结束。
例如服务器突然断电/网线被拔掉,服务器没有发送TCP reset包,所以客户端不知道连接断开,也就不会退出ssh命令。
同理,客户端突然断电,服务器也不知道客户端“挂了”。如果客户端随后重新联网并创建ssh端口转发,可能会提示服务器端口已被占用(因为服务器上之前的ssh会话还保持着)。
实际上,TCP连接是有心跳检测机制的,即TCP KeepAlive,不过它默认2小时发送一次心跳包,这实在是太长了。

服务器ssh配置 在服务器上编辑sshd配置文件/etc/ssh/sshd_config, 配置以下参数:

ClientAliveInterval 10
ClientAliveCountMax 3

其中:
ClientAliveInterval:参数表示如果服务器连续N秒没有收到来自客户端的数据包,则服务器会向客户端发送一条消息。
ClientAliveCountMax:表示如果服务器发送了N次数据到客户端都没有收到回应时,就会认为连接已经断开,服务器会结束会话、关闭监听的端口。
上述配置表示,如果服务器连续10秒没有收到客户端的数据,就会主动发送数据给客户端。连续发送了3次数据到客户端,都没有收到回复就断开连接。这意味着,网络断开后的最长30秒内,服务器就会关闭ssh会话
保存之后需要重新sshd服务:

sudo systemctl restart sshd

客户端配置

通过上述配置,服务器就可以检测客户端是否存活。同理,也需要修改客户端的配置,让客户端可以检测服务端是否存活。
在客户端编辑配置文件/etc/ssd/ssh_config,配置以下参数:

ServerAliveInterval 10
ServerAliveCountMax 3

保存之后在客户端重启ssh:

sudo systemctl restart ssh

经过上述配置后,一个稳定的ssh端口转发连接就建立起来了(已经经过数月的实际测试,断线后会自动重连)。

文章来源: segmentfault.com,作者:毛英东,版权归原作者所有,如需转载,请联系作者。

原文链接:segmentfault.com/a/1190000038153088

ssf 隧道
SSF - 安全套接字漏斗 - 网络工具 - TCP 和 UDP 端口转发、SOCKS 代理、远程外壳、本机中继协议、独立 (securesocketfunneling.github.io)

发表评论

邮箱地址不会被公开。 必填项已用*标注