SSH 在登录提示之前挂起

SSH 在登录提示之前挂起

我已经从远程树莓派设置了多个反向 ssh 隧道,运行 debian jessie。 RPI 使用 3G 加密狗来获得内网连接,这就是为什么我使用反向 ssh 远程登录的原因。每个 RPI 都会设置一个到云服务器的反向 ssh 隧道,我用它来登录每个系统。

ssh 隧道设置如下:

ssh -N -o ExitOnForwardFailure=yes -R 23xx:localhost:22 [email protected]

其中 23xx 是用于转发来自端口 22 的连接的端口,178.xxxxx 是服务器的 IP 地址。

我的问题是,有时当我尝试 ssh 进入系统时,它会永远挂起,没有错误,如下所示:

ssh pi_username@localhost -p 23xx

此后终端不会输出任何内容并永远挂起。当我尝试使用 -vvv 进行调试时,我得到的是:

ssh pi_username@localhost -p 23xx -vvv

OpenSSH_6.7p1 Debian-5+deb8u4, OpenSSL 1.0.1t  3 May 2016
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: Applying options for *
debug2: ssh_connect: needpriv 0
debug1: Connecting to localhost [::1] port 23xx.
debug1: Connection established.
debug1: key_load_public: No such file or directory
debug1: identity file /home/master/.ssh/id_rsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/master/.ssh/id_rsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/master/.ssh/id_dsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/master/.ssh/id_dsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/master/.ssh/id_ecdsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/master/.ssh/id_ecdsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/master/.ssh/id_ed25519 type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/master/.ssh/id_ed25519-cert type -1
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_6.7p1 Debian-5+deb8u4

看起来连接已建立,但没有提示登录。有任何想法吗?关于如何进一步调试有什么建议吗?

答案1

这只是一个理论,但我怀疑正在发生的情况是 ssh 会话的 TCP 连接正在消失,但“云服务器”没有检测到它。因此,当您去连接 时localhost -p 23xx,ssh 进程仍然存在并正在侦听,但是当它尝试将数据发送回 Pi 时,它会挂起,直到遇到 TCP 重传的最大次数,最后决定连接已死亡并退出(你说它永远挂起,但我敢打赌,如果你等待足够长的时间,你就会得到连接重置)。
现在假设您已将 Pi 配置为在 ssh 隧道中断时重新连接,您可能认为这应该可以解决该问题。这个想法有几个潜在的问题。首先,Pi 可能也没有检测到死连接。因此,在它尝试发送数据并达到 TCP 重新传输限制之前,它不会看到已失效的连接并进行重新连接。第二个潜在问题是,即使它确实检测到失效连接并尝试重新连接,它也无法在云服务器上建立侦听器,因为之前的 ssh 仍然存在并占用该端口。

这里的解决方案是配置 ssh,以便它可以检测死连接。有几种方法可以解决这个问题:TCP KeepAlive 和 SSH KeepAlive。 (参考:https://unix.stackexchange.com/a/34201/4358

TCP KeepAlive(TCPKeepAlive在 ssh 配置中设置)使用 TCP 的本机保持活动功能。基本上,内核每隔 X 秒发送一个空的 TCP ACK,当它没有从另一端收到 ACK 时(或者它得到重置),它会关闭通知应用程序 (SSH) 的连接。

SSH KeepAlive(ServerAlive*&ClientAlive*设置)类似,但在更高层运行。这里,SSH 进程通过连接发送实际数据并寻找响应。这应该可以像正常的 TCP KeepAlive 一样检测死连接,但是更有可能保持连接处于活动状态,因为中间的跃点可以识别 TCP KeepAlive 数据包并忽略它们,并使空闲连接超时。但是 SSH KeepAlive 无法被识别,因为它看起来像是到达中间任何一跳的真实流量。

长话短说:

在树莓派上,将以下设置添加到您的 ssh 客户端配置(~/.ssh/config/etc/ssh/ssh_config):

ServerAliveInterval 15
ServerAliveCountMax 1

文档

在服务器上,将以下设置添加到 ssh 守护程序配置 ( /etc/ssh/sshd_config):

ClientAliveInterval 20
ClientAliveCountMax 1

文档

请注意,我将间隔值设置得稍高一些。这样做的原因只是为了避免双方在完全相同的时间发送 KeepAlive 消息并在线路上相互交叉。这并没有真正的危害,只是效率低下一点点。哪一边更高并不重要,只要它们不同即可。

答案2

我遇到了同样的问题。

$ ifconfig wlan0
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.9  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::d949:d257:c622:3388  prefixlen 64  scopeid 0x20<link>
        ether 18:1d:ea:00:4e:cf  txqueuelen 1000  (Ethernet)
        RX packets 333970  bytes 471184965 (449.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 76462  bytes 9787485 (9.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

将 mtu 值更改为 1200。(最大传输单元

$ ifconfig wlan0
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1200
        inet 192.168.1.9  netmask 255.255.255.0  broadcast 192.168.1.255
        ether 18:1d:ea:00:4e:cf  txqueuelen 1000  (Ethernet)
        RX packets 334117  bytes 471271158 (449.4 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 76604  bytes 9808842 (9.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

此后 SSH 按预期连接。希望这个答案能缩短您寻找解决方法的时间。 :)

相关内容