ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SSH连接失败排查:从算法协商原理到ssh -Q命令实战

SSH连接失败排查:从算法协商原理到ssh -Q命令实战 从一次真实连接失败说起。前一阵有同事找我说从Windows笔记本上用SSH连一台Ubuntu服务器输入密码后马上被断开报错提示no matching key exchange method found。我让他跑了一条ssh -Q kex发现客户端这边的密钥交换算法列表里根本没有服务器要求的那些双方在握手阶段就谈崩了。这类问题在启用新系统、升级OpenSSH、或者用老版本客户端去连新服务器的时候特别常见。搞清楚SSH客户端到底支持哪些加密算法不仅是为了满足好奇心更是排查连接故障、做安全加固、兼容老设备时的基本功。这篇内容我会从协商原理、查询命令、算法命名规则、真实调试案例、服务端配置几个角度完整过一遍适合运维、网工以及所有被SSH连接问题折磨过的同学。1. SSH握手与算法协商为什么说客户端支持什么是命门1.1 一次SSH连接要过四道算法关很多人对SSH加密的理解停留在连上之后数据是加密的这个层面但实际上一开始双方就要在加密细节上进行一轮密集的谈判。SSH连接建立时会依次确定四类算法密钥交换算法KEX决定双方如何在不安全的网络上安全地协商出会话密钥比如curve25519-sha256、diffie-hellman-group14-sha256。主机密钥算法决定服务器用什么类型的密钥证明自己的身份比如ssh-ed25519、rsa-sha2-512、ecdsa-sha2-nistp256。对称加密算法Cipher决定实际传输数据的加密方式比如aes128-gcmopenssh.com、chacha20-poly1305openssh.com。MAC算法决定消息完整性校验的方式防止数据在传输过程中被篡改比如hmac-sha2-256。客户机和服务器各自持有一份按优先级排列的算法清单。握手时双方把清单发给对方然后按照客户端优先、服务器支持的原则选出第一个双方都有的算法。如果四类算法中有任何一类找不到交集连接就直接失败报错通常会告诉你具体是哪一类没匹配上。1.2 为什么客户端支持的算法常常是排查突破口从实际运维经验来看大部分算法不兼容问题都出在客户端这边太老或者服务器配置被刻意收紧过。服务器端OpenSSH为了安全新版默认会禁用一些旧算法比如基于SHA-1的ssh-rsa签名、diffie-hellman-group1-sha1密钥交换而几年没更新的客户端、嵌入式设备的SSH实现、某些光猫/路由器自带的SSH可能只支持这些老算法。两边一见面服务器说我只认新算法客户端说我只会老算法连接立刻失败。反过来如果服务器配置了很严格的算法白名单哪怕客户端很新也可能被拒之门外。这时候你第一件要做的事就是把客户端支持的算法完整列出来再和服务端的sshd_config配置逐项对比。所以说学会查询客户端算法列表等于掌握了一把排查SSH连接问题的钥匙。2. 四类命令查询OpenSSH客户端算法清单2.1ssh -Q一行命令导出完整算法清单OpenSSH从7.4版本开始提供了-Q选项专门用来查询当前客户端支持的各类算法。它不需要连接任何服务器直接在本地执行即可非常适合快速摸清本机能力。# 查看支持的所有对称加密算法 ssh -Q cipher # 查看支持的所有密钥交换算法 ssh -Q kex # 查看支持的所有MAC完整性校验算法 ssh -Q mac # 查看支持的所有主机密钥类型 ssh -Q key实际执行结果会按字典序输出。比如ssh -Q cipher在较新的OpenSSH版本上会显示3des-cbc aes128-cbc aes128-ctr aes128-gcmopenssh.com aes192-cbc aes192-ctr aes256-cbc aes256-ctr aes256-gcmopenssh.com chacha20-poly1305openssh.com这里能看到很多以openssh.com结尾的名字这表示OpenSSH自己扩展的算法实现。ssh -Q还能查询其他类型的算法集合比如ssh -Q pubkey可以查公钥算法ssh -Q cipher-auth可以查带认证加密的密码算法。想知道-Q支持哪些参数直接执行ssh -Q help即可。2.2ssh -vvv查看一次真实连接中实际协商出的算法-Q查的是理论清单而实际连接时到底用哪一套需要看调试输出。加一个-v就能看到基本协商过程-vvv则输出更详细的调试信息。ssh -vvv user192.168.1.100在输出的早期阶段你会看到类似这样的片段debug1: kex: algorithm: curve25519-sha256 debug1: kex: host key algorithm: ssh-ed25519 debug1: kex: server-client cipher: chacha20-poly1305openssh.com MAC: implicit compression: none debug1: kex: client-server cipher: chacha20-poly1305openssh.com MAC: implicit compression: none这四行就是本次连接最终采用的算法组合。注意MAC显示implicit说明当使用chacha20-poly1305这类AEAD认证加密算法时加密和完整性校验是一体的不再需要单独的MAC算法。用-vvv的好处是能看到双方算法列表的完整交换过程包括客户端哪些算法被服务器拒绝了。比如debug1: kex: client-server cipher: aes128-ctr MAC: hmac-sha2-256之后如果跟着一堆no matching cipher found就能直接定位到是哪一类算法不匹配。2.3 非OpenSSH客户端的查看方式不是所有环境都用OpenSSH客户端。Windows下很多人用PuTTY或者公司内部部署了Bitvise SSH Client它们的查看方式略有不同PuTTY连接设置里的Cipher、Key exchange、Host key type等下拉框就是算法列表但默认没有一键导出文本清单的功能。最直接的办法是在Session配置界面点Logging开启SSH packet data日志连接后日志里会如实记录双方协商出的算法。稍微麻烦一点但能解决问题。Bitvise SSH Client连接时会弹出图形界面在Security选项卡里能看到协商后的加密算法和MAC。Bitvise还允许你手动取消勾选某些算法方便模拟旧客户端环境。其他商业SSH客户端、网络设备自带的SSH实现很多没有提供命令入口建议直接查厂商文档或者用nc加Wireshark抓包看ClientHello中的SSH_MSG_KEXINIT消息这是最底层、最准确的办法。实际排查时我倾向于先用ssh -vvv验证OpenSSH的行为再针对特定客户端单独处理。2.4 服务端算法清单的查询方法客户端查完之后通常还要查服务端。服务端支持哪些算法主要由OpenSSH编译参数和sshd_config决定。在服务器上执行# 查看sshd默认支持的算法不含配置文件中的覆盖 sshd -T | grep -E kexalgorithms|ciphers|macs|hostkeyalgorithms # 查看ssh客户端默认支持的算法不含配置文件中的覆盖 ssh -G . | grep -E ^kexalgorithms|^ciphers|^macs|^hostkeyalgorithmsssh -T这个命令在OpenSSH里可以打印出实际生效的配置比直接看sshd_config更可靠因为它会把默认值和配置文件合并后的最终结果一起输出。如果服务器已经限制了算法但你想临时换个方式测也可以用ssh -o KexAlgorithms...在客户端覆盖算法列表这属于下一阶段再展开的技巧。3. 从算法名称到背后逻辑弄懂aes128-ctr、chacha20-poly1305这些字符串3.1 对称加密算法命名拆解ssh -Q cipher输出的字符串看着眼花其实有规律可循。以aes128-gcmopenssh.com为例aes是算法家族即高级加密标准AES。128是密钥长度单位是比特。还有aes192、aes256。gcm是工作模式即伽罗瓦计数器模式一种带认证功能的加密模式在SSH里使用时会自动提供完整性校验不需要额外的MAC算法。openssh.com表示这个算法标识由OpenSSH项目定义。严格说aes*-gcmopenssh.com最初是OpenSSH对RFC 5647的先行实现后来成为事实标准。cbc和ctr对应另外两种常见工作模式。ctr计数器模式比cbc密码块链接模式更适合并行计算安全性上通常也优于cbc。另一个高频出现的名字是chacha20-poly1305openssh.com。chacha20是一种流加密算法poly1305是消息认证码算法两者组合在一起实现认证加密。它在没有AES硬件加速的设备上速度优势明显所以树莓派、路由器之类的小设备反而更适合它。在OpenSSH 6.5加入后使用率一直很高目前依然是客户端和服务器协商时的热门首选。3.2 密钥交换算法与主机密钥算法密钥交换算法里curve25519-sha256代表基于Curve25519椭圆曲线的ECDH密钥交换名字里的sha256是完成密钥派生时用的哈希函数。diffie-hellman-group14-sha256则是经典DH算法搭配2048位MODP群其中group14对应RFC 3526中的2048位群group16对应4096位群group1则是已经被淘汰的768位群。主机密钥算法方面ssh-ed25519基于Ed25519签名算法是目前OpenSSH默认会优先使用的主机密钥类型rsa-sha2-512和rsa-sha2-256是传统的RSA签名加上SHA-2哈希而ssh-rsa因为只使用了SHA-1在新版OpenSSH和客户端里默认不会主动选择。不少老设备还停在ssh-rsa这正好解释了为什么老设备连新服务器时经常报host key algorithm不匹配。3.3 这份算法清单应如何影响你的日常选择看完清单之后重点不是背下每个算法而是建立起一个判断标准优先选择支持AEAD的算法、优先选择椭圆曲线类密钥交换、优先选择SHA-2及以上哈希的MAC。满足这几个条件的组合通常兼顾安全和性能。反过来在清单里看到3des-cbc、aes*-cbc、hmac-sha1、diffie-hellman-group1-sha1这类名字基本可以归类为为了兼容古董设备才保留的算法日常新增配置时不应该再主动选它们。4. 调试记录一台Ubuntu服务器如何拒绝了旧版客户端的算法4.1 完整复现一个算法协商失败现场有一回我用内部一个旧工具去连Ubuntu 24.04服务器工具封装了自己的SSH实现报错信息含糊得很只说了connection closed。我改用系统自带的OpenSSH客户端加-vvv去连输出里很快出现了关键日志debug1: kex: algorithm: curve25519-sha256 debug1: kex: host key algorithm: ssh-ed25519 debug1: kex: client-server cipher: aes128-ctr MAC: hmac-sha2-256 compression: none debug1: kex: server-client cipher: aes128-ctr MAC: hmac-sha2-256 compression: none有趣的是用新版OpenSSH客户端连的时候一切正常说明服务器没问题。问题只出在旧工具支持的算法列表太窄它可能只支持diffie-hellman-group1-sha1、ssh-rsa和aes128-cbc这类老算法。而Ubuntu 24.04的OpenSSH默认配置为了安全把这些算法全关了。于是双发握手时在密钥交换阶段就找不到交集连接自然失败。4.2 用-o参数手动限制算法来验证猜测为了确认这个猜测我在新版OpenSSH客户端上主动模拟旧客户端的行为把算法列表限制成和旧工具一样ssh -o KexAlgorithmsdiffie-hellman-group1-sha1 \ -o HostKeyAlgorithmsssh-rsa \ -o Ciphersaes128-cbc \ user192.168.1.100执行后立刻复现了旧工具的报错报错里明确指出no matching key exchange method found。这种用新客户端模拟老参数的方法在排查SSH兼容性问题时非常有效。它把问题从黑盒报错变成了可控变量每次只改一类算法就能精确锁定是KEX、主机密钥、Cipher还是MAC出了问题。4.3 常见的几类报错和对应的排查方向根据我的实操经验SSH算法不匹配的报错一般长这样含义也相对明确报错片段含义排查方向no matching key exchange method found密钥交换算法无交集查看-Q kex输出对比服务端KexAlgorithmsno matching host key type found主机密钥算法无交集查看-Q key输出重点检查服务器端启用的HostKey类型no matching cipher found对称加密算法无交集查看-Q cipher输出排查双方Ciphers配置no matching MAC foundMAC算法无交集查看-Q mac输出检查服务端MACs字段unable to negotiate a key exchange method协商失败通常伴随版本过老优先升级客户端SSH库不建议直接放宽服务端算法出现这些错误时千万不要第一时间就想着把服务器算法放宽。先确认客户端版本、再确认服务端配置、最后确认中间有没有防火墙或代理改动了SSH协议内容这是我认为更稳妥的排查顺序。放宽服务器算法意味着引入安全风险只能作为临时手段不该成为常态。5. 服务端配置实战让sshd只放行我想要的算法5.1sshd_config里和算法相关的四个核心指令如果你管理服务器通常会在/etc/ssh/sshd_config或/etc/ssh/sshd_config.d/下的单独文件里通过这几个指令来控制算法# 密钥交换算法 KexAlgorithms curve25519-sha256,curve25519-sha256libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group14-sha256 # 对称加密算法 Ciphers chacha20-poly1305openssh.com,aes256-gcmopenssh.com,aes128-gcmopenssh.com,aes256-ctr,aes192-ctr,aes128-ctr # MAC完整性校验算法 MACs hmac-sha2-512-etmopenssh.com,hmac-sha2-256-etmopenssh.com,hmac-sha2-512,hmac-sha2-256 # 主机密钥算法 HostKeyAlgorithms ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-512,rsa-sha2-256需要注意OpenSSH对不同指令的大小写和拼写极度敏感比如HostKeyAlgorithms和PubkeyAcceptedAlgorithms不要弄混。前者限制服务器返回哪些主机密钥后者限制哪些公钥登录方式被接受。OpenSSH 8.5以后还引入了PubkeyAcceptedAlgorithms代替PubkeyAcceptedKeyTypes老写法虽然还能识别但会在日志里提示改名了。5.2 配置后的验证流程改完配置不能直接重启服务完事我建议按下面三步走先用sshd -t检查配置文件语法。如果有拼写错误或非法算法名这一步就会报错不会影响线上连接。执行ssh -G . | grep -E kexalgorithms|ciphers|macs|hostkeyalgorithms查看客户端视角下这些指令最终值。确认无误后systemctl restart sshd。重启SSH服务不会踢掉已有的连接但建议仍然在晚间操作以防万一。重启后再起一个ssh -vvv连接看协商出的算法是否落在预期范围内。5.3 兼容旧客户端时的中间路线最小化放行如果真的需要兼容老设备比如旧版路由器、光猫、嵌入式系统不要直接把弱算法全放开而是按需放行最小集。一个相对可控的做法是单独为老设备指定一个备用端口在备用端口上放开部分旧算法正常端口保持高强度配置。# /etc/ssh/sshd_config 片段 Match Address 192.168.1.0/24 KexAlgorithms diffie-hellman-group14-sha256,diffie-hellman-group1-sha1 Ciphers aes128-cbc,3des-cbc MACs hmac-sha1注意号表示在默认值基础上追加而不是完全覆盖这个用法在需要默认安全、局部兼容时特别实用。不过我要提醒一句像diffie-hellman-group1-sha1和3des-cbc这种级别的算法能不放就不放放行就意味着把服务器的安全性拉低到十几年前的水平只能是临时过渡方案。5.4 为什么说只信协商结果最强算法是收益最高的加固动作之前遇到过一台被扫描工具反复试探的服务器日志里全是大量无效认证尝试。后来把Ciphers收紧到只保留chacha20-poly1305openssh.com和aes*-gcmopenssh.com把KexAlgorithms收紧到只保留curve25519-sha256和diffie-hellman-group16-sha512不仅扫描工具报错次数明显下降正常使用也没有任何感觉。真正影响连接体验的往往不是算法本身而是公钥认证、网络延迟和并发连接数。所以只要客户端跟得上把算法白名单收得越紧攻击面越小收益越大。6. 踩坑总结与几条保命的排查命令6.1 我见过最常见的三个误操作第一个误操作是在没有查客户端列表时就把服务器配置全改成最新算法。结果客户端不支持连接直接挂掉。正确的顺序永远是先在客户端执行ssh -Q确认客户端支持什么再决定服务端怎么配。第二个误操作是改配置后不执行sshd -t直接重启一旦手滑打错算法名可能导致sshd压根起不来。第三个误操作是盲目把PasswordAuthentication yes打开来绕过算法问题这完全不是同一件事。算法问题是握手层的问题密码认证是身份验证层的问题改密码配置对算法协商没有任何帮助反而会引入暴力破解风险。6.2 几条我常年驻留在笔记里的命令组合每次排查SSH算法问题我会按固定顺序跑下面这几组命令# 1. 看客户端支持什么本机执行 ssh -Q kex ssh -Q cipher ssh -Q mac ssh -Q key # 2. 看服务端最终生效的算法配置服务器执行 sshd -T | grep -E kexalgorithms|ciphers|macs|hostkeyalgorithms # 3. 实际连接并输出协商细节 ssh -vvv userserver # 4. 手动指定算法验证问题是否出在某一类算法上 ssh -o KexAlgorithmscurve25519-sha256 -o Cipherschacha20-poly1305openssh.com userserver # 5. 如果怀疑是防火墙或中间设备影响看TCP层是否正常 nc -vz userserver 22这套组合最大的优势是递进关系清楚先查理论清单再查实际生效清单然后看真实协商结果接着用指定算法做对照实验最后排除网络层问题。大多数SSH算法不匹配的问题走完这套流程基本都能定位。6.3 关于升级客户端的SSH库这一个建议遇到算法不兼容时我的第一反应永远是升级客户端而不是放宽服务端。很多看起来玄学的连接问题其实是客户端OpenSSH版本太老比如7.x以前的版本或者Windows自带的OpenSSH组件长期没更新。Windows用户可以定期检查系统更新里的OpenSSH客户端组件Linux用户如果发行版仓库里的OpenSSH版本过旧可以启用backports源或考虑编译安装。升级一次客户端往往比在服务端维护一堆旧算法白名单要省心得多。我这两年在真实环境里最大的体会是SSH算法清单不是一份需要背下来的静态表而是一套动态的协商机制。你掌握了查询方法、理解了命名规则、知道了如何定向验证以后再碰到任何无法连接算法不匹配连接被重置类的问题心里都不会慌。无论是日常维护的公网服务器还是家里路由器、光猫上那个不起眼的SSH开关方法论都是同一套。
返回列表