返回

解决Apache Mina SSHD连接重置问题:KEX失败排查

java

Apache Mina SSHD 连接重置问题排查

在构建基于 Apache Mina SSHD 的应用时,有时可能会遇到 "kex_exchange_identification: read: Connection reset by peer" 错误,这表明在 SSH 连接的密钥交换(KEX)阶段,连接被对端重置。这是一个常见的问题,以下我们将分析其可能的原因及解决方法。

问题分析

此错误通常发生在客户端与服务端建立连接后,尝试进行密钥交换的初始阶段。连接重置意味着在协商任何具体的加密或验证机制之前,连接就已经断开。发生这种情况可能有多种原因:

  • 协议不兼容: 客户端和服务端所使用的 SSH 协议版本或加密算法不一致。
  • 服务端配置错误: 服务端的 SSHD 配置不正确,例如缺少必要的密钥,配置了不被支持的算法等。
  • 客户端问题: 客户端配置不正确或使用了不兼容的选项。
  • 网络问题: 虽然可能性较低,网络基础设施中的某些问题也可能导致连接重置,比如中间网络设备的干扰。
  • 防火墙干扰: 防火墙可能会阻止或干扰 SSH 连接。
  • 资源问题: 服务器资源不足时,也有可能出现无法成功进行密钥交换。

解决办法

以下是一些解决 "kex_exchange_identification: read: Connection reset by peer" 错误的方法,根据上述原因进行针对性的处理:

1. 检查客户端和服务端协议及加密算法兼容性

首先需要确认客户端和服务端都支持相同的密钥交换算法、加密算法以及消息认证码。 可以通过配置服务器端允许支持多重兼容性。服务端配置方式示例如下,以下代码会显示默认的 SSH 协议支持。如果必要,您可以根据需求配置更多具体的协议。

import org.apache.sshd.server.SshServer;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.apache.sshd.common.kex.BuiltinDHFactories;
import org.apache.sshd.common.cipher.BuiltinCiphers;
import org.apache.sshd.common.mac.BuiltinMacs;

public class SshServerConfig {
   public static void config(SshServer sshd){
       sshd.setKeyExchangeFactories(BuiltinDHFactories.values());
       sshd.setCipherFactories(BuiltinCiphers.values());
       sshd.setMacFactories(BuiltinMacs.values());

       SecurityUtils.getRegisteredProviders().forEach(provider -> {
          System.out.println("Registered provider: "+provider.getName() );
       });
        sshd.getKexFactories().forEach(factory-> {
           System.out.println("Kex algorithm support:"+factory.getName());
        });
       sshd.getCipherFactories().forEach(factory-> {
           System.out.println("Cipher algorithm support:"+factory.getName());
       });
       sshd.getMacFactories().forEach(factory -> {
          System.out.println("Mac algorithm support: "+ factory.getName());
       });
   }
}
  • 操作步骤:

    1. 创建一个 SshServerConfig.java 文件,复制上面的代码。
    2. start()函数里, SshServerConfig.config(sshd);即可显示默认的支持并配置SSH 服务端支持的算法。
    3. 观察输出结果,确认客户端和服务端协议是否兼容。

2. 确保服务端密钥正确配置

服务端需要配置正确的 host key ,确保服务端能够验证自己。如果使用不匹配的 key 可能会导致 KEX 交换失败。 在问题中使用的代码如下已经做到了,确保服务端使用的 hostkey 是和服务器端文件系统里的 key 相匹配。

        // If the host key does not exist yet, it will be auto-created
        final File hostKey = new File(sshConfig.privateHostKey());
        SimpleGeneratorHostKeyProvider hostKeyProvider = new SimpleGeneratorHostKeyProvider(hostKey.toPath());
        hostKeyProvider.setAlgorithm(KeyUtils.RSA_ALGORITHM);
        if (!hostKey.exists()) {
            final List<KeyPair> hostKeys = hostKeyProvider.loadKeys(null);
            if (!hostKeys.isEmpty()) {
                Files.writeString(
                        new File(sshConfig.privateHostKey()).toPath(),
                        hostKeys.getFirst().getPrivate().toString()
                );
                Files.writeString(
                        new File(sshConfig.publicHostKey()).toPath(),
                        hostKeys.getFirst().getPublic().toString()
                );
            }
        }
        sshd.setKeyPairProvider(hostKeyProvider);
        sshd.setHostKeyCertificateProvider(new FileHostKeyCertificateProvider(hostKey.toPath()));

  • 操作步骤:
  1. 检查配置的私钥文件是否确实存在,路径是否正确。
  2. 检查 hostKey的公钥私钥是否匹配。
  3. 考虑重新生成新的密钥对,然后更新服务端配置。
 ssh-keygen -t rsa -b 4096 -f ~/.ssh/my_ssh_host_key
 ssh-keygen -y -f ~/.ssh/my_ssh_host_key > ~/.ssh/my_ssh_host_key.pub

将生成的 my_ssh_host_key 以及 my_ssh_host_key.pub 部署到应用。

3. 检查客户端配置

检查客户端配置确保它使用了正确的密钥进行连接。客户端可以使用 -v 参数查看具体协商信息, 也可以指定特定的加密算法进行连接测试。

   ssh -vvv -i path_to_key -p 2222  <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="553431383c3b156467627b657b657b64">[email protected]</a> -oKexAlgorithms=diffie-hellman-group14-sha256

尝试更改不同的 -oKexAlgorithms=diffie-hellman-group14-sha256, 可以尝试 diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512 这样的选项,进一步定位问题。如果连接成功,表明默认密钥交换算法服务端并没有正确支持,考虑第一点建议,更改服务端默认支持的算法。

  • 操作步骤:
    1. 使用客户端命令,带-v 参数查看调试输出信息
    2. 使用客户端-oKexAlgorithms 选项指定特定算法。
    3. 如果必要,请检查 ~/.ssh/config 文件。确保配置不会干扰到 SSH 连接。

4. 检查网络问题及防火墙配置

防火墙配置问题较为隐蔽。可以临时关闭防火墙, 看看问题是否还存在。 或者在防火墙配置里添加对于端口 2222 (默认值)允许通信的规则。网络配置不正确, 也可能会导致 connection reset, 需要仔细排查,比如网卡是否工作,网络协议配置,是否正确等。可以使用 ping 测试或者 telnet 测试服务端端口,查看网络是否可以访问,例如:

ping 127.0.0.1 # 如果本机IP不是 127.0.0.1 可以尝试ping 服务端IP地址
telnet 127.0.0.1 2222 # 如果本机IP不是 127.0.0.1 可以尝试telnet 服务端IP地址 以及SSH 监听的端口
  • 操作步骤:
    1. 关闭防火墙进行测试,或者为SSH 服务打开指定端口,端口默认为2222。
    2. 使用 ping 或 telnet 测试服务端连接性。

5. 服务器资源排查

服务端应用需要充足的资源才能维持连接,如果服务器的内存,CPU 过低,可能会无法进行连接,导致错误,需要对服务器的资源进行排查,通过top命令,监控内存以及cpu等,必要时候需要升级资源,提高服务的稳定性和性能。

top
  • 操作步骤:
    1. 执行top命令,查看服务资源使用情况,如果发现CPU和内存接近100% ,可能需要进行扩容。

附加的安全建议

  • 限制访问: 确保只允许来自特定 IP 地址或 IP 地址范围的 SSH 连接。
  • 使用密钥验证: 禁用密码认证,使用 SSH 密钥进行身份验证,增强安全性。
  • 保持更新: 及时更新 Apache Mina SSHD 和其他相关组件,修复潜在的安全漏洞。
  • 日志记录: 开启详细日志,以便更好地诊断和排查问题,尤其针对认证登录尝试。
  • 使用监控: 可以使用监控系统来监视系统以及SSH服务运行状况。

总结

当面对 Apache Mina SSHD 中的 “kex_exchange_identification: read: Connection reset by peer” 错误时,需要仔细检查协议兼容性、服务端密钥配置、客户端配置以及网络连接。 通过逐一排查,您应该能够有效地定位问题并解决它。在问题解决后,加强安全配置也非常必要,保证服务的安全稳定运行。