SSH 实战与排错 — 抓包、密钥认证、端口转发与故障定位
Wireshark 抓 SSH、ssh/scp/sftp/ssh-keygen 全套命令、三种转发实操与经典报错排查 / 应用层 / TCP 22
Table of Contents
这篇你能学到
- SSH 抓包能看到什么、看不到什么——为什么排错不能指望抓包,而要靠
ssh -vvv和服务端日志。 - 从生成密钥、分发公钥、用 agent 免密,到 scp/sftp/rsync 传文件,再到
-L/-R/-D三种端口转发的完整命令手册。 - 七类高频报错(publickey 被拒、主机密钥变更、算法不匹配、连接慢、被踢下线、转发不生效等)的结论先行排查路径,以及 sshd 加固基线、协议对比与面试速查。
1. 抓包观察
1.1 Wireshark 显示过滤式
这组过滤式能干什么:把 SSH 会话按阶段拆开看。重点是 20(算法协商)和 21(切换密钥)这两个编号——20 号包里能看到双方支持的全部算法(排查"算法不匹配"的唯一依据),21 号包之后就只剩密文了。
| |
命令行:
这组命令能干什么:把 SSH 流量抓成文件,再用
tshark提取出每个包的版本串和消息编号,快速判断握手走到第几步就断了;最后一条专门把算法协商的细节打印出来做比对。
1.2 典型字段说明(能看到什么、看不到什么)
| 阶段 | 抓包可见内容 | 用途 |
|---|---|---|
| 版本交换 | 完全明文:SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.5 | 版本指纹、漏洞面判断、合规扫描 |
KEXINIT (20) | 明文:双方全部算法名单、16 字节 cookie | 排查"no matching key exchange method found"类算法不匹配 |
KEX_ECDH_REPLY (31) | 服务端主机公钥、临时公钥、签名 | 核对主机密钥类型与指纹 |
NEWKEYS (21) 之后 | 只剩 Encrypted packet (len=xx) | 用户名、口令、命令、输出全部不可见 |
| 包长模式 | 加密后仍能看到包大小与时序 | 侧信道:交互式击键会产生大量 sftp 传输为大包连发 |
| TCP 层 | 重传、零窗口、RTT | 定位"SSH 卡顿/断连"是网络问题还是服务端问题 |
重要结论:SSH 抓包看不到任何凭据。排错时不要指望从包里读命令,应转向服务端日志与
ssh -vvv。
2. 常用命令 / 配置
2.1 连接与调试
这组命令能干什么:覆盖日常连接的各种姿势——换端口、指定私钥、只跑一条命令、走跳板机。排错时第一个该敲的是
ssh -vvv,它会把握手和认证的每一步都打印出来;ssh -G则告诉你各层配置文件合并后到底生效了什么。
| |
2.2 密钥生成与分发
这组命令能干什么:造一把属于你的钥匙,再把"锁孔"(公钥)装到服务器上,从此免密登录。私钥永远留在本机,只有
.pub结尾的公钥才需要上传。最后两条用于登录前带外核对服务器指纹,是防中间人的关键步骤。
| |
2.3 ssh-agent 与 known_hosts
这组命令能干什么:前半段让你输一次私钥口令就管一整天(agent 在内存里替你保管解密后的私钥);后半段管理"已认识的服务器"名单——服务器重装后报主机密钥变更告警,就是用
ssh-keygen -R删掉旧记录。
2.4 文件传输
这组命令能干什么:三种传文件方式各有适用场景——
scp适合随手拷一两个文件(注意端口参数是大写-P,和 ssh 的小写-p相反);sftp适合交互式浏览远端目录;rsync适合大文件和增量同步,支持断点续传。
| |
2.5 端口转发三形态(面试必考)
这组命令能干什么:把 SSH 当隧道用。
-L是"拉进来"——内网数据库映射到你本机端口,本地工具直接连;-R是"推出去"——把你本机的服务暴露到远端服务器上,等于内网穿透;-D是"全都走"——本地起一个 SOCKS5 代理,浏览器整体从内网出口访问。-N表示"只打隧道不开 shell"。
| |
2.6 ~/.ssh/config 客户端配置(强烈推荐)
这段配置能干什么:把一长串连接参数固化成一个短名字,以后
ssh db01一条命令搞定跳板加认证。ControlMaster让后续连接复用已有 TCP,第二次连接几乎秒开;ServerAliveInterval则靠保活包防止 NAT 把空闲连接掐掉。
| |
之后直接 ssh db01 即可。ControlMaster 让第二次连接复用已有 TCP,秒开。
2.7 服务端(sshd)管理与加固
这组命令能干什么:在服务器端查看服务状态、验证配置、看认证日志。
sshd -t是改配置后的救命符——它做语法检查,能避免你重启服务后把自己锁在门外;sshd -T则打印所有实际生效的值(包括你没写在配置文件里的默认值)。
| |
/etc/ssh/sshd_config 加固基线:
这段配置能干什么:一份可以直接抄的加固清单。核心思路是只留公钥认证、不许 root 直登、限定允许的用户、只用现代加密算法,再关掉用不到的转发功能缩小攻击面。
UseDNS no是顺手解决"登录要等十几秒"的经典配置。
| |
改配置铁律:先
sshd -t校验,再保留一个已登录会话的前提下restart,用新窗口验证能登录后才关旧会话。
3. 常见故障与排错
3.1 Permission denied (publickey)
先记住结论:十有八九是权限问题,不是密钥问题——~/.ssh、authorized_keys、家目录只要对组或其他人可写,sshd 就会直接拒绝。先 chmod 一遍再查别的。
| 原因 | 排查与修复 |
|---|---|
| 权限过宽(最高频) | chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys; chmod 700 $HOME(家目录不能对组/其他人可写) |
| 公钥未正确写入 | 服务端 cat ~/.ssh/authorized_keys,确认是一行完整的 ssh-ed25519 AAAA... comment |
| 客户端选错私钥 | ssh -vvv 看 Offering public key: 行;用 -i + IdentitiesOnly yes 锁定 |
| SELinux 上下文错误 | restorecon -Rv ~/.ssh |
| 服务端禁用了公钥 | sshd -T | grep pubkeyauthentication |
| 用户不在白名单 | sshd -T | grep -E 'allowusers|allowgroups' |
| 密钥类型被禁 | 老 ssh-rsa(SHA-1) 在 OpenSSH 8.8+ 默认禁用 → 换 ed25519,或临时 -o PubkeyAcceptedAlgorithms=+ssh-rsa |
排查命令组合:
这两条能干什么:第一条从客户端视角看"我到底递交了哪把钥匙、服务端还允许哪些方法";第二条从服务端日志看"为什么拒绝"——服务端日志通常把真实原因写得更清楚,两边对照最快定位。
3.2 Host key verification failed / 主机密钥变更告警
先记住结论:这个告警本身是保护机制生效了,不是故障。先判断是"服务器重装/迁移"这类合法变更,还是真有中间人;确认无误再删旧记录,绝不要用 StrictHostKeyChecking=no 一关了之。
| |
- 先判断原因:服务器重装/迁移/负载均衡后端变化 → 正常;否则怀疑中间人。
- 带外核对指纹:在服务器本机执行
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub,与客户端提示的 SHA256 比对。 - 确认无误后:
ssh-keygen -R host(或ssh-keygen -R '[host]:2222')再重连。 - 不要图省事用
StrictHostKeyChecking=no,那等于放弃中间人防护。
3.3 no matching key exchange method found / no matching cipher found
先记住结论:这不是配置错了,是双方"没有共同语言"——新客户端把弱算法都删了,老设备又只会弱算法,交集为空。追加算法是应急,升级设备固件才是根治。
新旧版本算法集不相交(如新客户端连老设备,或加固过的服务端):
这组命令能干什么:第一条先看清对端到底支持哪些算法(从明文的 KEXINIT 包里读);第二条用
+号把缺失的老算法追加到本地列表末尾(注意是追加不是覆盖,否则会连带禁掉现代算法)。
这是降级兼容手段,仅用于老网络设备;根治办法是升级设备固件。
3.4 连接建立很慢(10~30 秒)
先记住结论:慢在哪一步决定了原因——认证之前就慢,通常是服务端反向 DNS 或 GSSAPI 探测在超时;认证成功之后才慢,那是登录脚本的问题,跟 SSH 协议无关。
| 原因 | 验证 | 解决 |
|---|---|---|
| 服务端反向 DNS 超时 | ssh -vvv 停在 debug1: SSH2_MSG_SERVICE_ACCEPT 前后 | sshd_config: UseDNS no |
| GSSAPI/Kerberos 探测 | 日志中大量 gssapi 尝试 | 客户端 GSSAPIAuthentication no |
| 登录脚本慢 | 认证成功后才慢 | 检查 ~/.bashrc、/etc/profile.d/、pam_motd、landscape-sysinfo |
| MTU / 分片 | 大包卡住,小交互正常 | ping -M do -s 1400 host 试探;调 MTU |
| IPv6 优先但不通 | -4 后立即变快 | ssh -4 或 AddressFamily inet |
3.5 会话空闲后被断开(Connection closed by remote host / broken pipe)
先记住结论:多半不是 SSH 断了,是中间的 NAT/防火墙把空闲连接的会话表项老化删掉了——加保活包让连接"看起来一直有流量"即可;跑长任务则应该直接上 tmux。
这两段配置能干什么:客户端每 30 秒发一次保活探测、连续 6 次无响应才断开;服务端反向每 60 秒探一次、3 次无响应断开。两边都配上,空闲连接就不会被中间设备悄悄掐断。
NAT/防火墙会话老化是最常见根因(保活包让 NAT 表项不过期)。长任务建议直接用 tmux / screen,断线也不丢进程。
3.6 端口转发不生效
先记住结论:先分清是"隧道没建起来"还是"隧道通了但目标不通"——本地端口连不上属于前者(多半忘了 -N 或端口被占),能连但被拒属于后者(服务端到目标不通,或被 AllowTcpForwarding 策略挡住)。
| 现象 | 原因 |
|---|---|
-L 本地端口连不上 | -N 未加导致进程退出;或本地端口被占用(ss -tlnp | grep 13306) |
-L 通但目标拒绝 | 服务端到目标不通:登上 gw 执行 nc -zv db 3306 验证 |
-R 只能本机访问 | 服务端需 GatewayPorts yes(默认 no 只绑 127.0.0.1) |
| 全部转发失败 | 服务端 AllowTcpForwarding no 或 PermitOpen 限制;sshd -T | grep -i forwarding |
| SOCKS 代理 DNS 泄漏 | 用 socks5h://(h = 由代理端解析域名)而非 socks5:// |
3.7 其他高频错误速查
先记住结论:refused 是服务没监听、timed out 是包被防火墙丢了——这两个报错一眼就能区分"服务问题"和"网络问题",其余多为客户端本地配置或密钥状态问题。
| 报错 | 含义与处理 |
|---|---|
Connection refused | 端口没监听/服务未启:ss -tlnp | grep 22 |
Connection timed out | 防火墙 DROP 或路由不通:nc -zv host 22、traceroute |
Too many authentication failures | agent 里密钥太多被逐个尝试超限:-o IdentitiesOnly=yes -i <key> |
Bad owner or permissions on ~/.ssh/config | chmod 600 ~/.ssh/config |
sign_and_send_pubkey: signing failed | agent 中私钥失效/硬件密钥未插:ssh-add -D 后重加载 |
kex_exchange_identification: read: Connection reset | 被 fail2ban/防火墙拦截,或对端不是 SSH 服务 |
Unable to negotiate ... no matching host key type | 见 3.3,追加 HostKeyAlgorithms=+ssh-rsa |
REMOTE HOST IDENTIFICATION HAS CHANGED | 见 3.2 |
4. 与其他协议对比
| 维度 | SSH | Telnet | TLS/HTTPS | IPSec VPN | RDP |
|---|---|---|---|---|---|
| 端口 | TCP 22 | TCP 23 | TCP 443 | UDP 500/4500, ESP | TCP/UDP 3389 |
| 规范 | RFC 4251~4254 | RFC 854/855 | RFC 8446 | RFC 4301 等 | MS-RDPBCGR |
| 加密 | 内建(自有密码套件) | 无(全明文) | 内建(X.509 PKI) | 网络层加密 | TLS + CredSSP |
| 服务端身份 | 主机密钥 TOFU / SSH CA | 无 | CA 签发证书链 | IKE 证书或 PSK | 多为自签名证书 |
| 客户端认证 | 公钥/口令/2FA/证书/Kerberos | 明文口令 | 可选客户端证书 | 证书/PSK/EAP | 域账号 + NLA |
| 传输内容 | 字符流 + 任意 TCP 隧道 | 字符流 | HTTP 等应用数据 | 任意 IP 报文 | 图形与设备重定向 |
| 多路复用 | 通道(channel) | 无 | HTTP/2 stream | 无(IP 层) | MCS 虚拟通道 |
| 文件传输 | scp / sftp / rsync | 无 | HTTP 上传下载 | 依赖上层 | 驱动器重定向 |
| 典型场景 | Linux 运维、Git、自动化 | 端口探测、老设备应急 | Web | 站点互联 | Windows 图形运维 |
关系记忆:Telnet + 加密 + 公钥认证 + 隧道 = SSH;SSH 之于命令行 ≈ HTTPS 之于 Web。
5. 速查表 / 常见面试题
5.1 命令速查
| 目标 | 命令 |
|---|---|
| 生成密钥 | ssh-keygen -t ed25519 -C "备注" |
| 分发公钥 | ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host |
| 查看指纹 | ssh-keygen -lf ~/.ssh/id_ed25519.pub |
| 删除旧主机记录 | ssh-keygen -R host |
| 调试连接 | ssh -vvv user@host |
| 打印生效配置 | 客户端 ssh -G host / 服务端 sshd -T |
| 配置语法检查 | sshd -t |
| 本地转发 | ssh -N -L 本地端口:目标host:目标端口 网关 |
| 远程转发 | ssh -N -R 远端端口:localhost:本地端口 网关 |
| SOCKS 代理 | ssh -N -D 1080 网关 |
| 跳板 | ssh -J bastion user@target |
| 只执行命令 | ssh user@host 'command' |
| 保持会话 | tmux / autossh -M 0 |
5.2 关键文件与默认值
| 项目 | 路径 / 值 |
|---|---|
| 客户端配置 | ~/.ssh/config(600)、/etc/ssh/ssh_config |
| 服务端配置 | /etc/ssh/sshd_config、/etc/ssh/sshd_config.d/*.conf |
| 用户私钥/公钥 | ~/.ssh/id_ed25519(600)/ .pub(644) |
| 授权公钥 | ~/.ssh/authorized_keys(600),~/.ssh 目录 700 |
| 已知主机 | ~/.ssh/known_hosts、/etc/ssh/ssh_known_hosts |
| 主机密钥 | /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key |
| 日志 | /var/log/auth.log(Debian)、/var/log/secure(RHEL)、journalctl -u sshd |
| 默认 rekey 阈值 | 1 GB 或 1 小时 |
| 默认通道窗口 | 2 MB,最大包 32 KB |
5.3 高频面试题
Q1:SSH 的三层协议分别做什么? A:**传输层协议(RFC 4253)**负责算法协商、密钥交换、服务端主机认证、加密与完整性;**认证协议(RFC 4252)**在加密通道内认证客户端用户;**连接协议(RFC 4254)**把一条加密连接复用成多个通道(shell、sftp、端口转发、X11)。
Q2:SSH 和 Telnet 的核心区别? A:Telnet 全程明文、无服务端身份验证、只能口令认证、无隧道能力;SSH 先建加密通道再认证,支持主机密钥校验、公钥/2FA 认证、通道复用与端口转发。抓包时 Telnet 能逐字看到口令,SSH 只能看到版本串和算法名单。
Q3:公钥认证的完整过程?私钥会不会传到服务器?
A:不会。客户端先用公钥"探测"服务端是否接受(服务端在 authorized_keys 中查找并回 PK_OK),然后客户端用私钥对包含 session_id 的数据签名发给服务端,服务端用 authorized_keys 中的公钥验签。私钥始终不出本机,且签名绑定本次会话无法重放。
Q4:known_hosts 是干什么的?主机密钥变更告警一定是攻击吗?
A:记录服务端主机公钥,实现 TOFU 信任,防止中间人。变更告警最常见的合法原因是服务器重装、迁移、更换负载均衡后端、容器重建。处理方式是带外核对指纹后 ssh-keygen -R host,而不是盲目 StrictHostKeyChecking=no。
Q5:-L、-R、-D 的区别?举个场景。
A:-L 本地转发——把远端/内网服务映射到本地端口(本地连数据库 ssh -L 13306:db:3306 gw);-R 远程转发——把本机服务暴露到远端(内网穿透,让公网机器访问家里的 Web ssh -R 8080:localhost:80 vps);-D 动态转发——本地起 SOCKS5 代理,目标地址由应用运行时决定(浏览器走内网)。
Q6:为什么建议用 ed25519 而不是 RSA?
A:Ed25519 密钥仅 256 位却提供约 128 位安全强度,签名/验签快、密钥短、实现天然抗时序侧信道,且不依赖高质量随机数(确定性签名)。RSA 需要 3072/4096 位才等强,且 ssh-rsa(SHA-1 签名)已在 OpenSSH 8.8+ 默认禁用(应使用 rsa-sha2-256/512)。
Q7:SSH 为什么还需要通道级流控?TCP 不是有吗?
A:因为一条 TCP 上复用多个通道。若只靠 TCP 流控,某个读取缓慢的通道会阻塞整条连接上的所有通道。SSH 给每通道独立窗口(CHANNEL_WINDOW_ADJUST 补充),实现通道间隔离。
Q8:什么是 rekey?为什么需要? A:会话中期重新执行密钥交换生成新密钥。OpenSSH 默认 1 GB 数据或 1 小时触发。目的是限制单个密钥保护的数据量(降低密码分析与 IV 重用风险),并增强前向保密。
Q9:ProxyJump 和 Agent Forwarding 有什么区别?该用哪个?
A:ForwardAgent 把本地 agent 的签名能力暴露给跳板机,跳板机被攻陷时攻击者可用你的身份连其他机器;ProxyJump (-J) 只是把跳板机当 TCP 隧道,认证仍在本地完成,私钥能力不外泄。优先用 -J。
Q10:如何加固一台对公网开放的 SSH 服务器?
A:① 仅公钥认证(PasswordAuthentication no);② PermitRootLogin no;③ AllowUsers/AllowGroups 白名单;④ 算法白名单禁用弱套件;⑤ MaxAuthTries 3 + fail2ban;⑥ 改非标端口降噪;⑦ 关闭不需要的 X11/TCP 转发;⑧ 保持 OpenSSH 版本最新(如修复 Terrapin CVE-2023-48795、regreSSHion CVE-2024-6387);⑨ 有条件上 SSH 证书 CA 与 MFA。
Q11:SFTP 和 FTPS 是一回事吗? A:完全不同。SFTP 是 SSH 的一个 subsystem,跑在 TCP 22 的 SSH 加密通道内,单端口、防火墙友好;FTPS 是 FTP + TLS,仍是 FTP 的双通道(控制 21 + 数据端口)模型,NAT/防火墙配置麻烦。
Q12:抓包能看到 SSH 里传了什么命令吗?
A:SSH_MSG_NEWKEYS 之后全部加密,看不到内容。但仍可从包长与时序做侧信道推断(交互式击键产生规律小包,可辅助判断输入长度),以及看到明文的版本串与算法协商清单。