如何可靠地保持 SSH 隧道打开?

Pel*_*ier 290 networking linux security ssh shell

我在工作中使用 SSH 隧道绕过各种 idotic 防火墙(我的老板没问题:))。问题是,一段时间后 ssh 连接通常会挂起,并且隧道被破坏。

如果我至少可以自动监控隧道,我可以在它挂起时重新启动隧道,但我什至没有想到这样做的方法。

可以告诉我如何防止我的 ssh 连接挂起的人当然可以加分!

Kei*_*thB 347

听起来你需要autossh。这将监视 ssh 隧道并根据需要重新启动它。我们已经使用它几年了,它似乎运行良好。

autossh -M 20000 -f -N your_public_server -R 1234:localhost:22 -C
Run Code Online (Sandbox Code Playgroud)

关于 -M 参数的更多细节在这里

  • `autossh -f -nNT -i ~/keypair.pem -R 2000:localhost:22 username@myoutsidebox.com` 您可能会注意到我使用 -nNT 进行了设置,它不会创建远程终端,因此我可以将autossh 进入后台,并使用 -i 选项让 SSH 使用 .pem 文件。如果您要始终保持连接打开,我绝对建议您进行额外的设置。 (9认同)
  • 就其价值而言,通常最好省略`-M` 参数:https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=351162 (3认同)
  • 我这样做是为了让它在网络更改时重试,它对我来说效果很好: autossh -M 0 -o "ServerAliveInterval 10" -o "ServerAliveCountMax 2" -L 9999:localhost:19999 server@example.com (3认同)
  • 对于那些只是复制然后想知道为什么他们的连接可能会很慢的人:`-C` 强制压缩连接,它在调制解调器线路和慢速互联网连接上很有用,但如果快速连接到位,它实际上会减慢速度。如今,它很可能不会被使用,除非您的连接速度非常慢。 (3认同)
  • +1 对于 autossh,它按照它在罐头上所说的去做。我相信它的部分功能也是发送保持活动风格的数据包以防止任何类型的超时。 (2认同)

Ces*_*arB 44

所有有状态防火墙在一段时间内没有看到该连接的数据包后都会忘记该连接(以防止状态表变得充满连接,而两端都已死亡而没有关闭连接)。大多数 TCP 实现会在很长时间后发送一个 keepalive 数据包,而不会收到另一方的消息(2 小时是一个常见值)。但是,如果有状态防火墙在发送保活数据包之前忘记了连接,则长期存在但空闲的连接将死亡。

如果是这种情况,解决方案是防止连接变为空闲。OpenSSH 有一个名为ServerAliveInterval的选项,可用于防止连接空闲时间过长(作为奖励,即使连接空闲,它也会检测到对等方何时死亡)。

  • @metamatt,您引用的排名较低的答案排名较低是有充分理由的:这是错误的。 (5认同)

Jaw*_*awa 31

我已经使用以下 Bash 脚本在前一个终止时继续生成新的 ssh 隧道。当您不想或无法安装其他软件包或使用编译器时,使用脚本会很方便。

while true
do
  ssh <ssh_options> [user@]hostname
  sleep 15
done
Run Code Online (Sandbox Code Playgroud)

请注意,这需要一个密钥文件来自动建立连接,但 autossh 也是如此。

  • 如果您无法在服务器中安装东西,它会有所帮助。autossh 没有预装,而且官僚主义有时非常迟钝。 (5认同)
  • 如果 ssh 本身冻结,这将无济于事,是吗? (4认同)
  • 是的,最好不要安装东西。我已经这样做了一年,这是我保持远程机器可访问的唯一方法(甚至设置 crontab 在重新启动时运行它)。它从不失败,更重要的是,我知道为什么它永远不会失败。 (4认同)
  • 您应该添加在 autossh 上使用此脚本的任何原因,还是只是这样更容易? (2认同)

Ian*_*anB 29

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
Run Code Online (Sandbox Code Playgroud)

(修改 ssh 命令以适应)

  • 这将以用户身份运行,sshtunnel因此请确保用户首先存在
  • systemctl enable sshtunnel将其设置为在启动时启动的问题
  • 问题systemctl start sshtunnel立即开始

2018 年 1 月更新:某些发行版(例如 Fedora 27)可能会使用 SELinux 策略来阻止 systemd init 使用 SSH,在这种情况下,需要创建自定义策略以提供必要的豁免。

  • 这看起来与我的要点非常相似:https://gist.github.com/guettli/31242c61f00e365bbf5ed08d09cdc006#file-ssh-tunnel-service 欢迎反馈! (3认同)
  • 非常适合“systemd”系统。如果使用“Restart=on-failure”,那么手动终止 SSH 客户端将不会导致 systemd 重新启动 SSH 客户端并成功退出。 (2认同)

小智 24

在您自己的 mac 或 linux 机器上配置您的 ssh,每 3 分钟保持服务器 ssh 处于活动状态。打开终端并在您的家中使用您的隐形 .ssh:

cd ~/.ssh/ 
Run Code Online (Sandbox Code Playgroud)

然后创建一个 1 行配置文件:

echo "ServerAliveInterval 180" >> config
Run Code Online (Sandbox Code Playgroud)

你还应该添加:

ServerAliveCountMax xxxx (high number)
Run Code Online (Sandbox Code Playgroud)

默认值为 3,因此 ServerAliveInterval 180 将在 9 分钟(ServerAliveInterval 指定的 3 分钟间隔中的 3 分钟)后停止发送。

  • 投反对票,因为将 ServerAliveCountMax 设置为“高数”是没有意义的。ServerAliveCountMax 指定在放弃之前尝试发送“keepalive”消息的次数。默认值为 3,因此使用 ServerAliveInterval 180,仅当服务器在 9 分钟后未响应时才会停止发送,在这种情况下,您的连接可能已经完全中断。 (27认同)
  • 我对这个答案投了赞成票,因为感谢您提到 ServerAliveCountMax,如果您指定 ServerAliveInterval 而没有 ServerAliveCountMax 会发生什么。但是就像前面的评论一样,我注意到“将在之后停止发送”的计算是错误的,我认为这个答案如果只提供有关这些选项的信息,而不是告诉我们如何使用 cd 和 echo 命令应用它们会更好. (4认同)
  • 请注意,如果您已有配置文件,则不建议使用您的命令。使用 &gt;&gt; 进行重定向会好很多! (3认同)
  • **不要**将 `ServerAliveCountMax` 增加到一个巨大的数字 - 你这样做实际上是在禁用 keepalive。`ServerAliveCountMax` 限制 **missed** 回复的数量,而不是成功的数量,如果对方没有响应,则终止连接。对它限制发送**的“keepalive”数据包总量的理解是基于对手册的误读**。谷歌搜索 30 秒 [将使这一点非常清楚](https://unix.stackexchange.com/questions/475075/serveraliveinterval-and-serveralivecountmax)。 (2认同)

Mat*_*way 16

对于那些不想(或)不能使用 AutoSSH 的人...

我有一个 NAS,我想从互联网访问,我无法使用端口转发,因为我的 ISP 使用 CGNAT(我的公共 IP 并不是真正的公共 IP,我位于另一个路由器后面,我无法控制) )。因此,为了访问我的 NAS,我有一个 VPS(我从 OVH 租用它,每月费用非常低),并且它有一个固定的公共 IP 地址。因此,要从互联网访问我的 NAS,我只需在 NAS 和 VPS 之间创建一个 SSH 隧道,该隧道始终可靠地保持开放状态(用于全天候访问)。然而,由于不活动(尽管 ssh 进程保持运行),我的 SSH 隧道被“关闭”。通过让客户端(在我的例子中是 VPS)使用保持活动选项“ping”服务器(在我的例子中是 NAS),可以轻松克服这个问题。

要创建 SSH 隧道,我发出以下命令(从 NAS):

ssh -NT -o ServerAliveInterval=60 -o ServerAliveCountMax=10 -o ExitOnForwardFailure=yes -i /var/services/homes/foouser/.ssh/id_rsa -R 8080:localhost:80 -R 4443:localhost:443 foouser@<VPS>
Run Code Online (Sandbox Code Playgroud)

解释一下这个命令:

  • -N- 不执行远程命令;这对于转发端口很有用。
  • -T- 禁用伪 tty 分配。
  • -R 8080:localhost:80- 指定远程(服务器)主机上的给定端口将转发到本地端的给定主机和端口。在这种情况下,意味着将远程服务器的80端口转发到客户端的8080端口。
  • -i /path/to/key- 指定用于建立 ssh 会话的 ssh 密钥的路径,否则您将必须输入用户名(如果未提供)和密码来建立 ssh 会话。
  • ServerAliveInterval- 客户端在向服务器发送“服务器活动”消息以保持连接活动之前等待的秒数。
  • ServerAliveCountMax- 可能在没有服务器回复的情况下发送的“服务器活动”消息的数量。如果达到此阈值,ssh 将与服务器断开连接,从而终止会话。
  • ExitOnForwardFailure- 如果设置为“yes”,如果 ssh 无法设置所有请求的动态、隧道、本地和远程端口转发(例如,如果任一端无法绑定和侦听指定端口),则应终止连接。
  • foouser@<VPS>foouser- 指定用于与服务器建立远程端口转发 ssh 会话的用户帐户<VPS>

还值得向服务器(在我的例子中,在我的 VPS 上)添加一些 ssh 配置选项;通过添加以下文件(如果尚不存在):

[foouser@vps ~]$ cat /home/foouser/.ssh/config
Host *
    TCPKeepAlive yes
    ClientAliveInterval 30
    ClientAliveCountMax 9999
Run Code Online (Sandbox Code Playgroud)

注意:您可以将*(这意味着将此配置应用于“所有主机”)替换为特定主机 - 在我的情况下,我的 NAS(即连接到我的 VPS 的主机)位于我的路由器后面;我的路由器的公共 IP 地址经常更改,因为它是 DHCP 分配的(来自我的 ISP),所以我坚持使用“所有主机”。

SystemD 进程 (Synology NAS)

我也有这个命令(将 SSH 隧道作为 systemd 进程启动的命令,如果有人感兴趣,这里是脚本:

foouser@nas:~$ cat /etc/systemd/system/sshtunnel-web.service 
[Unit]
Description=SSH Tunnel for WebStation
After=network.target

[Service]
Restart=always
RestartSec=1
User=foouser
ExecStart=/bin/ssh \
    -NT \
    -o ServerAliveInterval=60 \
    -o ServerAliveCountMax=10 \
    -o ExitOnForwardFailure=yes \
    -i /var/services/homes/foouser/.ssh/id_rsa \
    -R 8080:localhost:80 \
    -R 4443:localhost:443 \
    foouser@<VPS>

[Install]
WantedBy=multi-user.target
Run Code Online (Sandbox Code Playgroud)

要启动并启用 SSH 隧道服务:

foouser@nas:~$ sudo systemctl daemon-reload
foouser@nas:~$ sudo systemctl start sshtunnel-web.service
foouser@nas:~$ sudo systemctl enable sshtunnel-web.service
Run Code Online (Sandbox Code Playgroud)

这对我来说已经可靠地工作了几个月。这包括在我的家庭路由器、VPS 服务器和 NAS 多次重新启动后保持可靠。


小智 13

在我看来,你们肯定都误解了 ServerAliveCountMax。据我了解文档,它是服务器活动消息的数量,这些消息可以在不终止连接的情况下得到答复。因此,在我们在这里讨论的情况下,将其设置为高值只会确保不会检测到和终止挂起的连接!

简单地设置 ServerAliveInterval 应该足以解决防火墙忘记连接的问题,并且将 ServerAliveCountMax 设置为低值将使始发端注意到失败并在连接失败时终止。

您想要的是,1) 连接在正常情况下永久保持打开状态,2) 检测到连接失败并在失败时退出,以及 3) 每次重新发出 ssh 命令退出(你如何做到这一点非常依赖于平台,Jawa 建议的“while true”脚本是一种方式,在 OS XI 上实际上设置了一个启动项)。


小智 12

ServerAliveInterval如果隧道问题是由过期的 NAT 会话生成,请始终使用SSH 选项。

如果连接完全中断,请始终使用重生方法,这里至少有三个选项:

  • 自动ssh程序
  • bash 脚本 ( while true do ssh ...; sleep 5; done) 不删除 sleep 命令,ssh可能会很快失败并且您将重新生成太多进程
  • /etc/inittab, 要访问在其他国家/地区运送和安装的盒子,在 NAT 之后,无需端口转发到盒子,您可以将其配置为创建返回给您的 ssh 隧道:

    tun1:2345:respawn:/usr/bin/ssh -i /path/to/rsaKey -f -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip 'sleep 365d'
    
    Run Code Online (Sandbox Code Playgroud)
  • Ubuntu 上的 upstart 脚本,其中/etc/inittab不可用:

    start on net-device-up IFACE=eth0
    stop on runlevel [01S6]
    respawn
    respawn limit 180 900
    exec ssh -i /path/to/rsaKey -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip
    post-stop script
        sleep 5
    end script
    
    Run Code Online (Sandbox Code Playgroud)

或始终使用这两种方法。


小智 8

我用这个解决了这个问题:

编辑

~/.ssh/config
Run Code Online (Sandbox Code Playgroud)

并添加

ServerAliveInterval 15
ServerAliveCountMax 4
Run Code Online (Sandbox Code Playgroud)

根据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.
Run Code Online (Sandbox Code Playgroud)


jco*_*ctx 5

ExitOnForwardFailure yes是其他建议的一个很好的补充。如果它连接但无法建立端口转发,那么它对您来说就像根本没有连接一样无用。