根本原因是TCP窗口缩放被中间设备错误丢弃及PMTUD失效导致SSH密钥交换卡住;可通过ssh -v日志停顿、tcpdump检查Window Scale选项、ping -M do测MTU等方式诊断,并临时关闭scaling或限制MTU验证。

为什么 SFTP 连接卡在 password prompt 或 key exchange 阶段
根本原因常是 TCP 窗口缩放(Window Scaling)被中间设备(如老旧防火墙、NAT 网关)错误丢弃或忽略,导致初始 SYN/ACK 协商失败或窗口通告异常;同时若路径 MTU 小于默认 1500 且 PMTUD(Path MTU Discovery)被禁用或 ICMP 被拦截,大包会被静默丢弃,SSH 的密钥交换阶段(尤其是 RSA 公钥传输)极易卡住。
如何确认是否为 TCP window scaling 导致的 SFTP 延迟
在客户端执行 ssh -v user@host,观察日志中是否出现长时间停顿在 debug1: SSH2_MSG_KEXINIT sent 后、迟迟不出现 SSH2_MSG_KEXINIT received;再用 tcpdump -i any port 22 -w ssh_handshake.pcap 抓包,过滤出三次握手和前几个 SSH 数据包,检查 SYN 和 SYN-ACK 中的 TCP Window Scale 选项是否一致、是否被篡改或消失。若服务端 SYN-ACK 没有回传 Window Scale 选项(或值为 0),而客户端已发送,则大概率是中间设备干扰。
- Linux 客户端可临时关闭 window scaling 测试:
echo 0 | sudo tee /proc/sys/net/ipv4/tcp_window_scaling - 该操作仅影响当前会话后的新建连接,无需重启网络服务
- 注意:关闭后长肥管道(high BDP)链路吞吐会明显下降,仅用于诊断
MTU 路径发现失败时的快速验证与绕过方法
若怀疑 PMTUD 失效,先用 ping -M do -s 1472 host(对应 IP 层 1500 字节)测试是否能通;逐步减小 -s 值(如 1400、1300),直到成功,反推路径 MTU。若 -M do(禁止分片)全部超时,但 -M want 可通,说明 ICMP “Fragmentation Needed” 被拦截,PMTUD 实际已断。
- 服务端侧临时缓解:在
/etc/ssh/sshd_config中添加ClientAliveInterval 30和TCPKeepAlive yes,避免连接因静默丢包被误判为僵死 - 客户端强制限制 MSS:
ssh -o "IPQoS throughput" -o "TCPKeepAlive=yes" -o "ServerAliveInterval=30" user@host,并配合系统级设置:sudo ip route change default via $GATEWAY dev $IFACE mtu 1400 - OpenSSH 8.9+ 支持
SetEnv SSH_CONNECTION_MTU=1400,但需服务端也支持该扩展(较新版本才启用)
生产环境长期修复建议
不要依赖临时内核参数或路由修改。真正稳定的方案是两端协同:确保防火墙/ISP 允许 ICMP Type 3 Code 4(Fragmentation Needed)通过;在 SSH 服务端启用 UsePrivilegeSeparation sandbox(现代 OpenSSH 默认开启),它对大包处理更健壮;若无法控制中间网络,可在客户端配置 ~/.ssh/config:
Host slow-sftp-host
HostName example.com
User myuser
ServerAliveInterval 15
TCPKeepAlive yes
IPQoS throughput
# 强制降低初始窗口,减少首包大小依赖
SetEnv SSH_CONNECTION_MTU=1400
Window scaling 和 PMTUD 是底层协同机制,单独调一端往往无效——比如只关客户端 scaling,但服务端仍发大窗口通告,中间设备照样丢包。最容易被忽略的是:某些云厂商安全组默认屏蔽所有 ICMP,连“Destination Unreachable”都不放行,此时 PMTUD 必然失效,必须显式放开 ICMP Type 3。










