SSH 登录慢排查实战:UseDNS、GSSAPI 与 shell 启动脚本,从 8 秒到 0.3 秒的定位方法

你有没有遇到过这种情况:SSH 连服务器,输入密码或点回车之后,光标卡在原地好几秒,甚至十几秒才出现命令行?用 ssh -v 能看到卡在某个阶段,但不知道卡在等什么。更诡异的是,有时候换一台客户端、换一个网络就快了。这种"SSH 登录慢"是运维里最经典、也最容易被忽略的体验问题之一。本文把常见的几类原因和排查方法讲清楚。

先定位:慢在哪一步

排查任何问题,第一步都是找到现象发生的准确位置。SSH 登录分几个阶段,用 -v(或 -vvv 看更详细)能看到每一步的时间:

ssh -vvv user@server.example.com

观察输出,卡顿通常出现在以下几个位置之一:

  • 卡在 Connecting to ...:TCP 层或网络层面慢,可能是路由、防火墙、或目标端口响应慢。
  • 卡在 SSH2_MSG_KEX_... 之后、认证之前:密钥交换或认证协商慢,常见于反向 DNS 解析、GSSAPI 协商。
  • 卡在输入密码后:认证环节慢,比如 pam 相关模块在做超时操作。
  • 登录成功后、提示符出现前卡顿:登录 shell 的启动脚本(.bash_profile、.bashrc)里有网络操作或耗时命令。

明确卡在哪一步,才能对症下药。

头号元凶:服务端反向 DNS 解析(UseDNS)

最常见的"登录等好几秒"就是它。SSH 服务端在客户端连接时会尝试对来源 IP 做反向 DNS 查询(PTR),拿到主机名后再做一次正向查询验证(FCrDNS)。如果这台服务器的 /etc/resolv.conf 指向的 DNS 服务器响应慢、或者根本不可达,每次连接都要等 DNS 超时,于是登录就慢。

先验证:在服务端看 /etc/resolv.conf,然后手动测反向解析是否慢:

time getent hosts <你的客户端IP>
# 或
time dig -x <你的客户端IP>

如果这条命令要好几秒,基本确定是它。解决办法是在 /etc/ssh/sshd_config 里关闭反向解析:

# /etc/ssh/sshd_config
UseDNS no

然后重载:sudo systemctl reload sshd,再登录,通常瞬间就快了。注意:UseDNS 默认值在不同发行版和 OpenSSH 版本里不一样,有的默认 yes,有的在编译时决定。显式写 UseDNS no 最保险。关掉它不影响安全性——它主要用于 hosts.allow/deny 里基于主机名的访问控制,而依赖 IP 的规则不受影响。

第二号元凶:GSSAPI 认证协商(GSSAPIAuthentication)

如果卡在认证协商阶段,而且客户端和服务端都没有用 Kerberos,那很可能是 GSSAPIAuthentication 在作怪。服务端会尝试 GSSAPI/Kerberos 认证,客户端也会去联系本地的 GSSAPI 机制,每次都超时才回退到普通认证,白白浪费几秒。

服务端处理:

# /etc/ssh/sshd_config
GSSAPIAuthentication no

客户端如果也慢,还可以在客户端的 ~/.ssh/config 里加:

Host *
    GSSAPIAuthentication no
    GSSAPIDelegateCredentials no

重载 sshd 和重启客户端后,认证阶段通常会明显加快。判断依据是:ssh -vvv 输出里如果反复出现 gssapi-with-mic 或类似行再超时,那就是它。

客户端侧:正向/反向 DNS 与 IPv6 解析

有时候慢不在服务端,而在客户端。客户端也可能对目标主机名做反向解析,或者先尝试 IPv6 连接、等超时才回退到 IPv4。典型表现是:用 IP 连很快,用域名连很慢;或者在某些网络下慢,换个网络就正常。

排查方法:

# 看域名解析耗时
time dig server.example.com
# 强制用 IPv4 连,看是否变快
ssh -4 user@server.example.com
# 强制用 IPv6 连
ssh -6 user@server.example.com

如果 -4 明显快,说明是 IPv6 路径有问题(比如服务器没有可用的 IPv6,但 DNS 里既有 AAAA 又有 A 记录,客户端先试 IPv6 卡住)。可以在客户端 ~/.ssh/config 里针对该主机强制 IPv4:

Host server.example.com
    AddressFamily inet

登录后才卡:shell 启动脚本里的坑

如果你已经过了认证、但提示符出现前还要等好几秒,那问题在登录 shell 的初始化脚本里。常见嫌疑:

  • /etc/profile 或 ~/.bashrc 里执行了网络请求,比如 curl 检查更新、hostname 反查、whois、任何带超时的命令。
  • 启动了交互式检查工具,比如某些发行版会跑 motd 的更新脚本(/etc/update-motd.d/),而其中某个脚本在做网络操作。
  • conda、nvm、sdkman 等工具在 shell 启动时做初始化,或去联网检查版本。

排查方法:让 SSH 登录时打印每个脚本的耗时。在服务端临时加:

# 在 /etc/bash.bashrc 顶部加
set -x

或者更精确地,用 bash -x -l -c exit 模拟登录并看它卡在哪。定位到具体脚本后,把网络操作改为异步、加超时、或者用 if [ -n "$PS1" ] 之类的判断确保只在交互式登录时才执行非必要逻辑。

其他可能:MTU、防火墙、负载

如果以上都不是,还有几种情况:

  • MTU 不匹配:如果网络链路上 MTU 有问题,SSH 握手时的大包会被丢弃,表现为连接建立慢或卡顿。用 ping -M do -s 1472 测试路径 MTU,或在客户端连不上时试 ssh -o IPQoS=throughput。根治要在网络层修 MTU/MSS。
  • 防火墙做了连接跟踪或限速:某些防火墙对新建连接做速率限制或需要等待状态表,导致 SSH 首包慢。检查 iptables -L -n -v、nft list ruleset。
  • 服务器负载过高:CPU 满载或内存吃紧时,sshd 派生进程也可能慢。登录后 uptime、top 一看便知。这种情况根因不在此,要去解决负载问题。
  • PAM 模块超时:少数环境里 PAM 的某些模块(如依赖 LDAP、或 pam_systemd 在容器里)会超时。用 /etc/pam.d/sshd 逐项排查,临时注释非必要模块验证。

用时间戳精确量化每一阶段

ssh -vvv 能告诉你卡在哪一步,但它不带时间。想看"到底每步花了几秒",可以用 ts(moreutils 提供的给每行加时间戳的工具)来给 ssh 的调试输出打上时间:

sudo apt install moreutils -y
ssh -vvv user@server 2>&1 | ts '%H:%M:%.S'

这样输出里每一行前面都有精确到毫秒的时间,哪一步停顿了多少一目了然。比如你会看到 debug1: SSH2_MSG_SERVICE_ACCEPT received 和下一行之间隔了 5 秒,那就锁定在这段。这个技巧比单纯 -vvv 高明得多,因为"卡顿"和"几行日志之间"是两回事,有了时间戳才能把二者对应起来。

另一个更"土"但有效的办法:用 date +%s.%N 在客户端脚本里记录开始和结束时间,做多次连接的统计对比:

for i in 1 2 3 4 5; do
  s=$(date +%s.%N)
  ssh -o BatchMode=yes user@server true
  e=$(date +%s.%N)
  echo "第 $i 次: $(echo "$e - $s" | bc) 秒"
done

多次测量能排除偶发波动,确认问题是"每次必慢"还是"时快时慢",这对判断根因很重要。

一个真实案例:从 8 秒到 0.3 秒

说一个很典型的场景。有台服务器,用 IP 连是快的,用域名连就要等七八秒。用 ssh -vvv IP 没用,因为快;用 ssh -vvv 域名 才慢。加了时间戳后发现,卡在 debug1: SSH2_MSG_SERVICE_REQUEST sent 之后、SSH2_MSG_SERVICE_ACCEPT received 之前。

这个位置正是认证服务协商(GSSAPI/Kerberos)的阶段。服务端 GSSAPIAuthentication 开着,但没有配置 Kerberos,于是每次都要等 GSSAPI 超时才回退。ssh -vvv 里还能看到 debug1: Authentications that can continue: gssapi-with-mic,password,publickey——gssapi-with-mic 排在最前,就是它在拖时间。

两处修改:服务端 /etc/ssh/sshd_config 加 GSSAPIAuthentication no,客户端 ~/.ssh/config 加 GSSAPIAuthentication no,重载 sshd。再测,登录从 8 秒降到 0.3 秒。问题根源不是网络、不是服务器性能,而是一个默认开启、却在当前环境里纯浪费时间的认证选项。

预防:把好的配置固化下来

排查完、优化完,要做的最后一件事是固化配置,避免换台机器又踩。服务端 sshd_config 里明确写上这几行:

UseDNS no
GSSAPIAuthentication no
# 明确认证方式,减少协商往复
PubkeyAuthentication yes
PasswordAuthentication no
PermitRootLogin no

客户端 ~/.ssh/config 里也加上针对性的设置,尤其如果你经常连一批机器:

Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    GSSAPIAuthentication no
    Compression yes

Host slow-server
    AddressFamily inet

ServerAliveInterval 还能顺手解决另一个常见困扰:网络空闲时 SSH 连接被防火墙悄悄掐断。它定期发送心跳包保活,配合 ServerAliveCountMax 判断真正断线。

和其他"慢"问题的区别

最后提醒一点,别把"SSH 登录慢"和"命令执行慢"混为一谈。前者是连接建立、认证阶段的问题;后者是连上之后每条命令都要等——那通常是 shell 启动脚本、命令补全、或 PROMPT_COMMAND 里的网络操作。还有一种是"首次连接快、隔一会儿再连又慢",那要重点查 DNS 缓存和防火墙连接跟踪表的老化时间。

学会区分现象、用 -vvv 加时间戳定位、再对症修改配置,SSH 登录慢几乎总是可以在十分钟内解决的。这既是具体的排障技能,也是一种通用的运维思维:先测量,再优化,别凭感觉改配置。

一份可复制的排查清单

  1. ssh -vvv 定位卡在哪一阶段。
  2. 卡在连接建立 → 测 ping/tcptraceroute,查路由与防火墙。
  3. 卡在认证协商 → 服务端关 UseDNS no + GSSAPIAuthentication no,重载 sshd。
  4. 客户端慢 → 测 dig 耗时、试 ssh -4、检查 ~/.ssh/config。
  5. 登录后才卡 → 用 bash -x -l 或 set -x 找 shell 启动脚本里的网络操作。
  6. 都不是 → 查 MTU、防火墙、系统负载、PAM。

绝大多数情况下,UseDNS no 和 GSSAPIAuthentication no 这两条就能解决 90% 的"SSH 登录慢"。这也提醒我们:很多性能问题不是资源不够,而是某个默认开启的、会做网络查询的选项在拖后腿。养成用 -vvv 定位、用 time 量化的习惯,排查会非常高效。

Last modification:October 8th, 2026 at 12:26 pm

Leave a Comment