如何可靠地保持SSH隧道打开?
我在工作中使用SSH隧道来绕过各种idotic防火墙(我的老板也没意见:)。问题是,过了一段时间,ssh连接通常会挂掉,隧道也会被破坏。
如果我至少能自动监控隧道,我可以在它挂掉的时候重新启动隧道,但我甚至还没有想出办法。
能告诉我如何防止我的ssh连接挂起的人,当然要加分!!。
我在工作中使用SSH隧道来绕过各种idotic防火墙(我的老板也没意见:)。问题是,过了一段时间,ssh连接通常会挂掉,隧道也会被破坏。
如果我至少能自动监控隧道,我可以在它挂掉的时候重新启动隧道,但我甚至还没有想出办法。
能告诉我如何防止我的ssh连接挂起的人,当然要加分!!。
所有有状态的防火墙在一段时间内没有看到一个连接的数据包后,都会忘记这个连接(以防止状态表中充满了两端都没有关闭连接就死亡的连接)。大多数TCP实现会在长时间没有收到对方的消息后发送keepalive数据包(2小时是一个常见的值)。但是,如果有一个有状态的防火墙,在发送keepalive数据包之前就忘记了这个连接,那么一个长期存在但闲置的连接就会死亡。
如果是这种情况,解决方案是防止连接变成空闲。OpenSSH有一个叫做 ServerAliveInterval 的选项,可以用来防止连接空闲太久(作为奖励,即使连接空闲,它也会检测到对等体什么时候死的更快)。
在你自己的mac或linux机器上配置你的ssh,让服务器ssh每3分钟活一次。打开一个终端,进入你家里的隐形.ssh。
cd ~/.ssh/
然后创建一个1行的配置文件:
echo "ServerAliveInterval 180" >> config
你还应该添加:
ServerAliveCountMax xxxx (high number)
默认值是3,所以ServerAliveInterval 180会在9分钟后停止发送(ServerAliveInterval指定的3分钟间隔的3)。
Systemd是理想的选择。
创建一个服务文件/etc/systemd/system/sshtunnel.service
,包含:
[Unit]
Description=SSH Tunnel
After=network.target
[Service]
Restart=always
RestartSec=20
User=sshtunnel
ExecStart=/bin/ssh -NT -o ServerAliveInterval=60 -L 5900:localhost:5900 user@otherserver
[Install]
WantedBy=multi-user.target
(修改ssh命令以适应)
sshtunnel
的身份运行,所以要确保用户首先存在 systemctl enable sshtunnel
将其设置为在启动时启动 systemctl start sshtunnel
立即启动 2018年1月更新 : 一些发行版(如. 例如Fedora 27)可能会使用SELinux策略来阻止systemd init使用SSH,在这种情况下,将需要创建一个自定义策略来提供必要的豁免。
在我看来,你们肯定都误解了ServerAliveCountMax。根据我对文档的理解,它是指在不终止连接的情况下可以不被回复的服务器活着的消息的数量。因此,在我们讨论的这种情况下,将它设置为一个高值只会确保挂起的连接不会被检测到并被终止。
简单的设置ServerAliveInterval应该足以解决防火墙忘记连接的问题,而将ServerAliveCountMax设置为低值,则可以让发起端注意到失败,如果连接还是失败,则终止。
你想要的是,1)在正常情况下连接永久保持打开状态,2)连接失败被检测到,发起端在失败时退出,3)每次退出时重新发出ssh命令(如何做到这一点非常依赖于平台,Jawa建议的 “while true "脚本是一种方法,在OS X上我其实设置了一个launchd项)。
始终使用ServerAliveInterval
SSH选项,以防隧道问题是由过期的NAT会话产生的。
在连接完全中断的情况下,始终使用重生方法,你在这里至少有三个选项。
while true do ssh ...; sleep 5; done
) 不要去掉睡眠命令,ssh
可能会很快失败,你会重生太多的进程 /etc/inittab
,要想访问一个在另一个国家运来并安装在NAT后面的盒子,不需要端口转发到盒子上,你可以配置它创建一个ssh隧道回到你身边。-Ubuntu上的upstart脚本,/etc/inittab
不可用。
或者一直使用这两种方法。
我用这个方法解决了这个问题。
编辑
~/.ssh/config
添加
ServerAliveInterval 15
ServerAliveCountMax 4
根据man page for ssh_config:
ServerAliveCountMax
Sets the number of server alive messages (see below) which may be
sent without ssh(1) receiving any messages back from the server.
If this threshold is reached while server alive messages are
being sent, ssh will disconnect from the server, terminating the
session. It is important to note that the use of server alive
messages is very different from TCPKeepAlive (below). The server
alive messages are sent through the encrypted channel and there‐
fore will not be spoofable. The TCP keepalive option enabled by
TCPKeepAlive is spoofable. The server alive mechanism is valu‐
able when the client or server depend on knowing when a connec‐
tion has become inactive.
The default value is 3. If, for example, ServerAliveInterval
(see below) is set to 15 and ServerAliveCountMax is left at the
default, if the server becomes unresponsive, ssh will disconnect
after approximately 45 seconds. This option applies to protocol
version 2 only.
ServerAliveInterval
Sets a timeout interval in seconds after which if no data has
been received from the server, ssh(1) will send a message through
the encrypted channel to request a response from the server. The
default is 0, indicating that these messages will not be sent to
the server. This option applies to protocol version 2 only.
ExitOnForwardFailure yes
是其他建议的一个很好的辅助手段。如果它连接了,但不能建立端口转发,对你来说就像它根本没有连接一样没有用。
最近我自己也遇到了这个问题,因为这些解决方案要求你每次都要重新输入密码,如果你使用密码登录的话,我就用sshpass在循环中加上一个文本提示,以避免在批处理文件中出现密码。
我想我应该把我的解决方案分享给大家,以防有人有同样的问题:
#!/bin/bash
read -s -p "Password: " pass
while true
do
sshpass -p "$pass" ssh user@address -p port
sleep 1
done
``` 。
由于autossh
不能满足我们的需求(如果在第一次尝试时不能连接到服务器,它就存在错误),我们写了一个纯bash程序。 https://github.com/aktos-io/link-with-server
它默认为服务器上NODE的sshd端口(22)创建一个反向隧道。如果你需要执行任何其他操作(比如转发额外的端口,在连接上发送邮件等等……),你可以将你的脚本on-connect
和on-disconnect
文件夹。