1. 这不是端口问题,是信任链崩塌的信号
“SSH登录异常频发”这八个字,我去年在三台生产服务器上连续看到过七次。每次告警邮件标题都一样,内容却千差万别:有的是凌晨3:17突然断连,session卡在 Authentication succeeded 之后0.8秒就reset;有的是同一IP在12秒内发起47次密钥协商失败;还有一台数据库前置机,明明 sshd_config 里禁了密码登录、只留ed25519密钥,却持续收到 Invalid user admin 的暴力扫描日志——而这个用户根本不存在于 /etc/passwd 。很多人第一反应就是改端口:把22改成2222,再加个fail2ban,仿佛换把锁就能关住所有风险。但真实情况是: 端口只是门牌号,SSH异常频发的本质,是服务端与客户端之间建立信任的整条链路中,至少一个环节正在持续失准 。它可能是密钥协商阶段的算法兼容性错位,可能是PAM模块对登录上下文的误判,也可能是TCP连接复用机制在高并发下的状态污染。这篇文章不讲“怎么改端口”,而是带你一层层剥开OpenSSH 8.9p1(当前主流LTS版本)的认证流水线,定位那些被 journalctl -u sshd 日志掩盖的真实病灶。适合运维工程师、SRE、云平台安全负责人,以及任何需要保障Linux服务器远程访问稳定性的技术决策者。你不需要精通密码学,但得愿意打开 /var/log/auth.log 逐行比对时间戳;你不必会写C,但得能看懂 sshd -T 输出的配置树结构。接下来的内容,全部来自我过去三年在金融、电商、IoT边缘集群的237次SSH故障复盘——其中162次最终证明,问题根因和端口号毫无关系。
2. 认证流水线解剖:从TCP握手到Shell启动的七道关卡
OpenSSH的登录过程远非“输入密码→显示#”这么简单。以一次典型的公钥登录为例,整个流程被严格划分为七个逻辑阶段,每个阶段都有独立的状态机、超时阈值和失败回调机制。理解这些关卡,是诊断异常频发的前提。
2.1 第一关:TCP连接建立与初始协商(0~3秒)
当客户端执行 ssh user@host 时,首先触发的是标准TCP三次握手。但OpenSSH在此基础上增加了 协议版本嗅探 :客户端在SYN-ACK后立即发送 SSH-2.0-OpenSSH_8.9 字符串,服务端必须在500ms内响应匹配的协议标识。这里埋着第一个隐患点:某些老旧防火墙或SD-WAN设备会截断超过128字节的初始包,导致服务端收不到客户端的协议声明,从而静默丢弃后续所有数据。我曾在一个跨国CDN节点遇到过类似问题——客户端日志显示 Connection timed out ,而服务端 tcpdump 抓包发现只有SYN包进入,没有SYN-ACK返回。解决方案不是改端口,而是强制客户端使用 -o ConnectTimeout=3 并添加 -o ServerAliveInterval=15 ,让连接层主动探测链路健康度。
2.2 第二关:密钥交换(KEX)阶段(3~8秒)
完成协议协商后,双方进入密钥交换。OpenSSH 8.9默认启用 curve25519-sha256,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256 三套算法。问题在于: 不同客户端实现对算法优先级的解析存在细微差异 。例如,某款国产终端软件硬编码了 diffie-hellman-group14-sha1 为首选,而服务端已通过 KexAlgorithms 禁用该算法(因其存在已知数学弱点),此时服务端会直接关闭连接,日志仅记录 kex_input_kexinit: unsupported kex alg 。这种失败不会触发fail2ban,因为尚未进入认证环节。实测发现,约12%的SSH异常频发案例源于此——它们在 /var/log/auth.log 中表现为大量 Disconnected from authenticating user ,但无具体错误码。验证方法很简单:在客户端加 -vvv 参数重放登录,观察 debug1: kex: algorithm: 行输出的算法列表是否与服务端 sshd -T | grep kexalgorithms 完全匹配。
2.3 第三关:用户身份解析(8~12秒)
通过KEX后,客户端发送用户名。服务端此时调用 getpwnam() 系统调用查询 /etc/passwd 。这里有个极易被忽略的陷阱: 当 nscd (Name Service Caching Daemon)缓存失效时,查询可能阻塞长达5秒 。尤其在LDAP或SSSD集成环境中,若网络抖动导致LDAP服务器响应超时, getpwnam() 会等待至 /etc/nsswitch.conf 中定义的 ldap [timeout=5] 阈值才返回 NULL ,进而触发 Invalid user xxx 日志。更隐蔽的是,某些容器化部署中 /etc/passwd 被挂载为只读,而应用又试图动态创建用户,导致 getpwnam() 始终失败。排查命令: time getent passwd testuser ,若耗时>1s,需检查nscd状态( systemctl status nscd )或直接临时停用 systemctl stop nscd 做对比测试。
2.4 第四关:认证方法选择与执行(12~25秒)
这是最复杂的阶段。服务端根据 AuthenticationMethods 和 PubkeyAuthentication 等配置,决定接受哪些认证方式。关键细节在于: OpenSSH支持多因素认证(MFA)的串行执行,但每个因素有独立超时 。例如配置 AuthenticationMethods publickey,keyboard-interactive:pam 时,公钥认证成功后,PAM模块必须在 LoginGraceTime 剩余时间内完成二次验证。若PAM中启用了 pam_faildelay.so 且延迟设为3秒,而 LoginGraceTime 仅剩2秒,整个登录就会因超时中断,日志显示 Failed keyboard-interactive for user 。我见过最离谱的案例:某银行系统将 LoginGraceTime 设为60秒,但PAM配置中 pam_tally2.so 的 deny=5 unlock_time=900 导致第5次失败后锁定900秒,而客户端重试间隔恰好是15秒——结果形成“失败5次→锁定15分钟→重试→再失败5次”的死循环,表面看就是“异常频发”。
2.5 第五关:会话初始化与环境加载(25~35秒)
认证通过后,sshd派生子进程执行 /bin/bash -l 。此时加载 .bashrc 、 .profile 等文件。问题在于: 某些脚本中包含阻塞式网络调用 。比如 .bas


1万+

被折叠的 条评论
为什么被折叠?



