从未收到SSH服务器标识 - 握手死锁[SSHJ]

ara*_*ran 12 java sockets ssh sftp deadlock

我们在尝试为我们的应用程序实现SftpConnections池时遇到了一些麻烦.

我们目前正在使用SSHJ(Schmizz)作为传输库,面临一个我们在开发环境中无法模拟的问题(但错误在生产中随机显示,有时在三天之后,有时仅在10分钟之后).

问题是,当尝试通过SFTP发送文件时,线程被锁定在initschmizz' TransportImpl类的方法中:

   @Override
    public void init(String remoteHost, int remotePort, InputStream in, OutputStream out)
            throws TransportException {
        connInfo = new ConnInfo(remoteHost, remotePort, in, out);

    try {

        if (config.isWaitForServerIdentBeforeSendingClientIdent()) {
            receiveServerIdent();
            sendClientIdent();
        } else {
            sendClientIdent();
            receiveServerIdent();
        }


        log.info("Server identity string: {}", serverID);

    } catch (IOException e) {
        throw new TransportException(e);
    }

    reader.start();
}
Run Code Online (Sandbox Code Playgroud)

isWaitForServerIdentBeforeSendingClientIdent对我们来说是假的,所以首先客户端(我们)发送我们的标识,如日志中所示:

" 客户身份字符串:blabla "

然后转向receiveServerIdent:

    private void receiveServerIdent() throws IOException 
{
        final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
        while ((serverID = readIdentification(buf)).isEmpty()) {
            int b = connInfo.in.read();
            if (b == -1)
                throw new TransportException("Server closed connection during identification exchange");
            buf.putByte((byte) b);
        }
    }
Run Code Online (Sandbox Code Playgroud)

线程永远不会得到控制权,因为服务器永远不会回复其身份.好像代码卡在这个While循环中.没有超时或SSH异常被抛出,我的客户端只是一直等待,线程陷入僵局.

这是readIdentification方法的impl:

private String readIdentification(Buffer.PlainBuffer buffer)
        throws IOException {
    String ident = new IdentificationStringParser(buffer, loggerFactory).parseIdentificationString();
    if (ident.isEmpty()) {
        return ident;
    }

    if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
        throw new TransportException(DisconnectReason.PROTOCOL_VERSION_NOT_SUPPORTED,
                                     "Server does not support SSHv2, identified as: " + ident);

    return ident;
}
Run Code Online (Sandbox Code Playgroud)

看起来像ConnectionInfo的输入流永远不会获取要读取的数据,就像服务器关闭了连接一样(即使如前所述,也没有抛出任何异常).

我试图通过使协商饱和来模拟这个错误,在连接时关闭套接字,使用conntrack在进行握手时杀死已建立的连接,但是根本没有运气,所以任何帮助都会非常感激.

:)

小智 1

遇到类似的问题,我们会看到:

信息 [net.schmizz.sshj.transport.TransportImpl : pool-6-thread-2] - 客户端身份字符串:blablabla

信息 [net.schmizz.sshj.transport.TransportImpl : pool-6-thread-2] - 服务器标识字符串:blablabla

但在某些情况下,服务器没有响应。我们的服务通常会唤醒并同时传输多个文件,每个连接/线程一个文件。

问题出在 sshd 服务器配置中,我们将 maxStartups 从默认值 10 增加(我们注意到批量大小增加到 10 以上后不久就开始出现问题)

/etc/ssh/sshd_config 中的默认值:

最大初创公司 10:30:100

变成:

最大创业公司 30:30:100

最大创业公司

指定 SSH 守护程序的并发未经身份验证连接的最大数量。在身份验证成功或连接的 LoginGraceTime 过期之前,其他连接将被删除。默认值为 10:30:100。或者,可以通过指定三个冒号分隔的值 start:rate:full(例如“10:30:60”)来启用随机提前丢弃。如果当前有启动 (10) 个未经身份验证的连接,则 sshd 将以速率/100 (30%) 的概率拒绝连接尝试。如果未经身份验证的连接数量达到满 (60),概率会线性增加,并且所有连接尝试都会被拒绝。

如果您无法控制服务器,您可能必须找到一种方法来限制客户端代码中的并发连接尝试。