搭建稳定的ssh隧道
背景
网上有现成的工具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
其中:
User
和Group
为执行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)