☰
SSH连接被重置?深入排查ssh_exchange_identification报错
2026/10/13 23:19:44 网站建设 项目流程

1. 问题现象与排查思路拆解

1.1 这个报错到底在说什么

ssh_exchange_identification: read: Connection reset by peer这个报错,但凡做过几年运维或者后端开发的人,大概率都遇到过。它的字面意思是:SSH 客户端在尝试和远程主机交换身份标识(identification)的阶段,读取数据时连接被对端重置了。注意关键词——交换身份标识阶段。这个阶段发生在 TCP 三次握手完成之后、密钥交换和用户认证之前。换句话说,TCP 层是通的,但 SSH 协议层的握手还没走完就被掐断了。

很多人第一次看到这个报错会懵,因为它的表现很“分裂”:有时候能连上,有时候连不上;有时候重试几次就好了,有时候怎么试都不行。这种间歇性特征恰恰说明问题不在 SSH 配置本身,而更可能出在连接建立的上游环节——可能是服务端资源限制、可能是网络中间设备干扰、也可能是服务端守护进程本身出了问题。

我先把结论性的排查方向摆出来,后面再逐层展开:

  • 服务端sshd进程是否正常监听、是否达到最大连接数
  • 服务端 TCP Wrapper(/etc/hosts.deny、/etc/hosts.allow)是否拦截了客户端
  • 服务端MaxStartups、MaxSessions等并发参数是否触顶
  • 客户端 IP 是否被fail2ban或类似防护工具封禁
  • 网络路径中是否存在会话数限制或连接重置行为
  • 服务端系统资源(文件描述符、内存、PID)是否耗尽

1.2 为什么这个报错容易误判

这个报错最坑的地方在于,它把“连接被重置”这个结果抛给了客户端,但真正的原因往往藏在服务端。客户端的 SSH 只是如实报告了“我读到一半,对面把连接断了”。所以如果你只在客户端折腾ssh_config、换密钥、加-v参数,大概率是白费功夫。

我见过有同学遇到这个问题后,第一反应是删掉known_hosts重新生成密钥,结果当然没用。还有人怀疑是本地网络问题,换了热点、换了网线,依然无效。原因很简单:问题不在客户端,而在服务端或中间链路。

正确的排查姿势应该是:先确认服务端sshd的状态,再看服务端的连接限制和防护策略,最后才考虑网络中间设备。这个顺序不能反,否则你会浪费大量时间在无关环节上。

1.3 排查前的信息收集清单

在动手之前,先收集这几项信息,能帮你快速缩小范围:

收集项获取方式用途
客户端完整报错ssh -vvv user@host定位卡在哪个阶段
服务端 sshd 状态systemctl status sshd确认守护进程是否正常
服务端监听端口ss -tlnp | grep :22确认端口是否被监听
服务端当前连接数ss -tn state established '( dport = :22 )' | wc -l判断是否触顶
服务端系统日志journalctl -u sshd -n 100查看拒绝原因
客户端 IP 是否被封检查防护工具日志排除封禁可能

提示:ssh -vvv的输出非常关键,它会告诉你连接卡在Connecting to host之后、SSH2_MSG_KEXINIT之前还是之后。如果卡在SSH2_MSG_KEXINIT之前就断了,基本可以确认是服务端在协议握手前就拒绝了连接。

2. 服务端 sshd 配置与资源限制深挖

2.1 MaxStartups 参数:最容易被忽视的元凶

MaxStartups是sshd_config里一个非常关键的参数,它控制的是同时处于未认证状态的 SSH 连接数。默认值通常是10:30:100,意思是:当未认证连接数达到 10 个时开始随机拒绝,达到 30 个时拒绝 30% 的新连接,达到 100 个时全部拒绝。

这个参数的设计初衷是防止暴力破解和资源耗尽,但在实际场景中,它经常成为“背锅侠”。比如你的服务端前面有一个负载均衡或者跳板机,大量连接在短时间内涌入,未认证连接数瞬间超过阈值,后面的连接就会被直接重置,客户端看到的就是Connection reset by peer。

查看当前配置:

grep -i maxstartups /etc/ssh/sshd_config

如果输出是注释状态或者默认值,而你的业务场景确实存在并发连接较多的情况,可以适当调大:

# 编辑 sshd_config MaxStartups 100:30:200

这行的含义是:未认证连接数达到 100 时开始随机拒绝,达到 200 时全部拒绝。调大之后记得重载配置:

systemctl reload sshd

注意:调大MaxStartups只是缓解症状,不是根治。如果你的服务端频繁触顶,说明要么有异常连接在冲击,要么服务端处理能力不足,需要从根源上排查。

2.2 MaxSessions 与 MaxConnections 的区别

很多人会把MaxSessions和MaxConnections搞混。简单说:

  • MaxConnections:控制单个网络连接上可以复用多少个 SSH 会话(多路复用场景)
  • MaxSessions:控制单个已认证连接上允许的最大会话数

这两个参数通常不会导致Connection reset by peer,因为它们在认证之后才生效。但如果你用的是 SSH 多路复用(ControlMaster),并且配置不当,可能会间接引发连接异常。检查方式:

grep -iE "maxsessions|maxconnections" /etc/ssh/sshd_config

一般情况下保持默认即可,除非你有明确的调优需求。

2.3 文件描述符与进程数限制

sshd作为系统服务,受 systemd 的资源限制约束。如果文件描述符(nofd)或进程数(nproc)达到上限,新的连接就无法建立,表现同样是连接被重置。

查看 sshd 服务的资源限制:

systemctl show sshd | grep -iE "limitnofile|limitnproc"

如果LimitNOFILE偏低(比如 1024),在高并发场景下很容易触顶。可以通过 systemd 的 override 机制调整:

mkdir -p /etc/systemd/system/sshd.service.d cat > /etc/systemd/system/sshd.service.d/override.conf <<EOF [Service] LimitNOFILE=65535 LimitNPROC=65535 EOF systemctl daemon-reload systemctl restart sshd

实操心得:调整LimitNOFILE之后,一定要用systemctl show sshd | grep LimitNOFILE确认生效,而不是只看配置文件。systemd 的 override 有时候会被其他配置覆盖,确认生效才算数。

2.4 TCP Wrapper 的拦截逻辑

虽然现在很多新系统已经默认不安装 TCP Wrapper,但一些老系统或者特定发行版仍然在用/etc/hosts.deny和/etc/hosts.allow来控制访问。如果hosts.deny里有sshd: ALL这样的规则,而hosts.allow里没有放行你的客户端 IP,连接就会被拒绝。

检查方式:

cat /etc/hosts.deny cat /etc/hosts.allow

如果发现hosts.deny里有针对sshd的拒绝规则,而hosts.allow里没有对应的放行规则,就需要添加:

# 在 hosts.allow 中添加 sshd: 你的客户端IP

注意:TCP Wrapper 的规则是“先 allow 后 deny”,如果hosts.allow里放行了,hosts.deny里的规则就不会生效。但反过来,如果hosts.allow里没有,hosts.deny里又有拒绝规则,就会被拦截。

3. 防护工具与网络中间环节排查

3.1 fail2ban 封禁的识别与解封

fail2ban是服务端最常见的防护工具之一,它会监控 SSH 登录失败日志,达到阈值后自动封禁来源 IP。如果你的客户端 IP 被封了,表现就是连接被重置。

查看 fail2ban 状态:

fail2ban-client status sshd

输出会显示当前被封禁的 IP 列表。如果你的 IP 在列表里,说明被 ban 了。解封方式:

fail2ban-client set sshd unbanip 你的客户端IP

但解封只是临时措施,更重要的是搞清楚为什么会被封。常见原因包括:密码错误次数过多、使用了错误的密钥、自动化脚本频繁重试等。如果是自己的脚本导致的,需要调整脚本的重试策略;如果是密码问题,建议改用密钥认证。

实操心得:我遇到过一种情况,某台跳板机上的定时任务用错误的凭据反复尝试连接,导致跳板机 IP 被目标服务器封禁,进而影响了所有经过跳板机的连接。排查时只看客户端日志是看不出来的,必须去服务端看 fail2ban 日志才能定位。

3.2 网络中间设备的会话限制

有些网络环境(比如某些云平台的安全组、企业防火墙、NAT 网关)会对单个 IP 的并发连接数或新建连接速率做限制。当连接数超过阈值时,中间设备会直接重置连接,客户端看到的就是Connection reset by peer。

这类问题的特征是:间歇性出现,且与连接频率正相关。连接越频繁,越容易触发;隔一段时间再连,可能就正常了。

排查方法:

  • 在服务端抓包,看是否收到了客户端的 SYN 包
  • 如果服务端收到了 SYN 但没收到后续的 ACK 或 SSH 协议数据,说明中间设备可能拦截了
  • 如果服务端根本没收到 SYN,说明问题在更上游的网络路径

抓包命令示例:

tcpdump -i any -nn port 22 -w ssh_debug.pcap

抓包后分析,重点看 TCP 三次握手是否完成、SSH 协议版本交换是否发生。

3.3 DNS 反向解析超时

sshd默认会尝试对客户端 IP 做反向 DNS 解析,用于日志记录。如果 DNS 服务器响应慢或者不可达,sshd会在这个环节卡住,超过LoginGraceTime后断开连接。客户端看到的就是连接被重置。

解决方式有两种:

  • 在sshd_config中关闭反向解析:UseDNS no
  • 确保服务端的 DNS 配置正常,能快速解析客户端 IP

推荐第一种,直接关闭:

echo "UseDNS no" >> /etc/ssh/sshd_config systemctl reload sshd

注意:关闭UseDNS后,SSH 日志里记录的就是 IP 而不是主机名。如果你依赖主机名做审计,需要权衡一下。不过大多数场景下,IP 已经足够定位问题了。

3.4 GSSAPI 认证导致的握手延迟

GSSAPI认证在某些环境下会导致 SSH 握手阶段出现明显延迟,严重时可能触发超时断开。如果你不需要 GSSAPI 认证,可以在客户端和服务端都关闭它。

客户端配置:

# ~/.ssh/config Host * GSSAPIAuthentication no

服务端配置:

# /etc/ssh/sshd_config GSSAPIAuthentication no

关闭后重载配置,再测试连接速度。

4. 常见问题速查与实战排查记录

4.1 问题速查表

现象可能原因排查命令解决方式
间歇性连接重置MaxStartups 触顶grep MaxStartups /etc/ssh/sshd_config调大 MaxStartups
特定 IP 无法连接fail2ban 封禁fail2ban-client status sshd解封并排查原因
连接卡住后重置DNS 反向解析超时grep UseDNS /etc/ssh/sshd_config设置 UseDNS no
高并发时大量重置文件描述符耗尽systemctl show sshd | grep LimitNOFILE调大 LimitNOFILE
所有连接都失败sshd 未监听或崩溃systemctl status sshd重启 sshd 并查日志
特定网段无法连接TCP Wrapper 拦截cat /etc/hosts.deny在 hosts.allow 放行
握手阶段延迟高GSSAPI 认证ssh -vvv观察卡点关闭 GSSAPIAuthentication

4.2 一次完整的排查记录

某次线上环境出现批量连接重置,现象是:大约 30% 的 SSH 连接在握手阶段被重置,重试后有时能成功。按照下面的顺序排查:

第一步,客户端ssh -vvv确认卡在SSH2_MSG_KEXINIT之前,说明服务端在协议握手前就断了。

第二步,服务端journalctl -u sshd -n 200查看日志,发现大量error: maximum authentication attempts exceeded和Connection closed by authenticating user。这说明有大量未认证连接在冲击服务端。

第三步,ss -tn state established '( dport = :22 )' | wc -l查看当前连接数,发现稳定在 80 左右,而MaxStartups是默认的10:30:100。未认证连接数长期在 30 以上,触发了随机拒绝。

第四步,进一步排查发现,某台应用服务器上的定时任务配置错误,每秒尝试建立 5 个 SSH 连接,且全部使用错误的凭据。这些连接在认证阶段失败,但会占用未认证连接配额。

第五步,修复定时任务配置,同时将MaxStartups调整为100:30:200,问题消失。

这个案例的教训是:不要只盯着报错本身,要顺着连接链路往上找。Connection reset by peer只是结果,真正的原因可能在应用层的错误配置。

4.3 避坑技巧汇总

  • 不要急着重启 sshd:重启会断开所有现有连接,如果是生产环境,影响面很大。先排查原因,确认需要重启再操作。
  • 善用ss而不是netstat:ss更快更详细,netstat在很多新系统上已经默认不安装了。
  • 日志级别调高:临时将sshd_config中的LogLevel调为VERBOSE,可以获取更详细的握手信息,排查完记得调回来。
  • 客户端也用-vvv:不要只看服务端日志,客户端的详细输出能告诉你连接卡在哪个阶段。
  • 注意时间同步:如果服务端和客户端时间偏差过大,某些认证方式可能会失败,虽然不直接导致这个报错,但会增加排查复杂度。

提示:如果你在云平台上遇到这个问题,优先检查云平台的安全组和网络 ACL 规则。有些云平台的安全组会对 SSH 连接做速率限制,超过阈值就会重置连接。这种情况在服务端日志里往往看不到任何记录,因为连接根本没到达服务端。

4.4 长期预防建议

与其每次出问题再排查,不如提前做好预防。几个实用的建议:

  • 定期检查sshd的并发连接数,设置监控告警
  • 合理配置MaxStartups,根据实际业务并发量调整
  • 关闭不必要的认证方式和 DNS 解析,减少握手环节
  • 对应用层的 SSH 连接做连接池管理,避免频繁新建连接
  • 保留足够的日志轮转周期,方便事后追溯

我个人在实际操作中的体会是,这类问题的排查效率取决于你对 SSH 握手流程的理解程度。如果你清楚TCP 握手 -> 协议版本交换 -> 密钥交换 -> 认证这个顺序,就能通过ssh -vvv的输出快速定位卡点,而不是盲目尝试各种配置。另外,服务端的日志永远比客户端的报错更有价值,养成先看服务端日志的习惯,能帮你省下大量时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询